This is an automated email from the ASF dual-hosted git repository.

shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new bf91696cc fix(xrpc): bound redis command-line preallocation size 
(#13483)
bf91696cc is described below

commit bf91696cc1489670cd6e6ee4312c0550dff30945
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Wed Jun 10 16:06:13 2026 +0800

    fix(xrpc): bound redis command-line preallocation size (#13483)
---
 apisix/stream/xrpc/protocols/redis/init.lua |  28 ++++-
 t/xrpc/redis.t                              | 152 ++++++++++++++++++++++++++++
 2 files changed, 179 insertions(+), 1 deletion(-)

diff --git a/apisix/stream/xrpc/protocols/redis/init.lua 
b/apisix/stream/xrpc/protocols/redis/init.lua
index 9aff6d0d1..73b09701a 100644
--- a/apisix/stream/xrpc/protocols/redis/init.lua
+++ b/apisix/stream/xrpc/protocols/redis/init.lua
@@ -21,12 +21,14 @@ local xrpc_socket = 
require("resty.apisix.stream.xrpc.socket")
 local ffi = require("ffi")
 local ffi_str = ffi.string
 local math_random = math.random
+local math_floor = math.floor
 local OK = ngx.OK
 local DECLINED = ngx.DECLINED
 local DONE = ngx.DONE
 local sleep = ngx.sleep
 local str_byte = string.byte
 local str_fmt = string.format
+local str_match = string.match
 local ipairs = ipairs
 local tonumber = tonumber
 
@@ -43,6 +45,7 @@ local protocol_name = "redis"
 local _M = {}
 local MAX_LINE_LEN = 128
 local MAX_VALUE_LEN = 128
+local MAX_CMD_LINE_PREALLOC = 64
 local PREFIX_ARR = str_byte("*")
 local PREFIX_STR = str_byte("$")
 local PREFIX_STA = str_byte("+")
@@ -134,6 +137,13 @@ local function read_len(sk)
     end
 
     local s = ffi_str(p + 1, len - 1)
+    -- RESP lengths are plain decimal integers; reject any other numeric
+    -- syntax (e.g. scientific notation like "1e9") so it cannot slip
+    -- through tonumber and be treated as a valid length.
+    if not str_match(s, "^%-?%d+$") then
+        return nil, str_fmt("invalid len string: \"%s\"", s)
+    end
+
     local n = tonumber(s)
     if not n then
         return nil, str_fmt("invalid len string: \"%s\"", s)
@@ -148,13 +158,25 @@ local function read_req(session, sk)
         return nil, err
     end
 
-    local cmd_line = core.tablepool.fetch("xrpc_redis_cmd_line", narg, 0)
+    if narg < 1 or narg ~= math_floor(narg) then
+        return nil, str_fmt("invalid argument number: %s", narg)
+    end
+
+    -- narg comes from the client; only use it as a bounded preallocation hint
+    -- so an oversized declared length cannot force a large upfront allocation.
+    -- the table still grows as the actual arguments are read.
+    local prealloc = narg < MAX_CMD_LINE_PREALLOC and narg or 
MAX_CMD_LINE_PREALLOC
+    local cmd_line = core.tablepool.fetch("xrpc_redis_cmd_line", prealloc, 0)
 
     local n, err = read_len(sk)
     if not n then
         return nil, err
     end
 
+    if n < 0 then
+        return nil, str_fmt("invalid bulk length: %s", n)
+    end
+
     local p, err = sk:read(n + 2)
     if not p then
         return nil, err
@@ -188,6 +210,10 @@ local function read_req(session, sk)
             return nil, err
         end
 
+        if n < 0 then
+            return nil, str_fmt("invalid bulk length: %s", n)
+        end
+
         local s
         if not is_key and n > MAX_VALUE_LEN then
             -- avoid recording big value
diff --git a/t/xrpc/redis.t b/t/xrpc/redis.t
index cb438c1cc..f9cff50a7 100644
--- a/t/xrpc/redis.t
+++ b/t/xrpc/redis.t
@@ -781,3 +781,155 @@ log redis request ["psubscribe","dog*","cat*"]
 log redis request ["publish","dog1","Hello"]
 log redis request ["punsubscribe","cat*","dog*"]
 log redis request ["set","dog","1"]
+
+
+
+=== TEST 14: invalid declared argument number is rejected
+--- config
+    location /t {
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            -- declare a negative argument count in the RESP array header
+            local bytes, err = sock:send("*-1\r\n")
+            if not bytes then
+                ngx.say("failed to send: ", err)
+                return
+            end
+
+            -- the connection should be closed by the gateway without a reply
+            local line = sock:receive("*a")
+            ngx.say("received: [", line or "", "]")
+            sock:close()
+        }
+    }
+--- response_body
+received: []
+--- stream_conf_enable
+--- error_log
+invalid argument number: -1
+--- no_error_log eval
+[
+    qr/\[error\](?!.*(?:invalid argument number|invalid len string|invalid 
bulk length))/,
+    qr/\[crit\]/,
+    "stack traceback",
+]
+
+
+
+=== TEST 15: non-decimal declared length (scientific notation) is rejected
+--- config
+    location /t {
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            -- scientific notation parses with tonumber but is not valid RESP
+            local bytes, err = sock:send("*1e9\r\n")
+            if not bytes then
+                ngx.say("failed to send: ", err)
+                return
+            end
+
+            -- the connection should be closed by the gateway without a reply
+            local line = sock:receive("*a")
+            ngx.say("received: [", line or "", "]")
+            sock:close()
+        }
+    }
+--- response_body
+received: []
+--- stream_conf_enable
+--- error_log
+invalid len string: "1e9"
+--- no_error_log eval
+[
+    qr/\[error\](?!.*(?:invalid argument number|invalid len string|invalid 
bulk length))/,
+    qr/\[crit\]/,
+    "stack traceback",
+]
+
+
+
+=== TEST 16: negative command bulk length is rejected
+--- config
+    location /t {
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            -- a valid array count but a negative bulk length for the command
+            local bytes, err = sock:send("*1\r\n$-1\r\n")
+            if not bytes then
+                ngx.say("failed to send: ", err)
+                return
+            end
+
+            -- the connection should be closed by the gateway without a reply
+            local line = sock:receive("*a")
+            ngx.say("received: [", line or "", "]")
+            sock:close()
+        }
+    }
+--- response_body
+received: []
+--- stream_conf_enable
+--- error_log
+invalid bulk length: -1
+--- no_error_log eval
+[
+    qr/\[error\](?!.*(?:invalid argument number|invalid len string|invalid 
bulk length))/,
+    qr/\[crit\]/,
+    "stack traceback",
+]
+
+
+
+=== TEST 17: negative argument bulk length is rejected
+--- config
+    location /t {
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            -- valid command, then a negative bulk length for the argument
+            local bytes, err = sock:send("*2\r\n$3\r\nGET\r\n$-1\r\n")
+            if not bytes then
+                ngx.say("failed to send: ", err)
+                return
+            end
+
+            -- the connection should be closed by the gateway without a reply
+            local line = sock:receive("*a")
+            ngx.say("received: [", line or "", "]")
+            sock:close()
+        }
+    }
+--- response_body
+received: []
+--- stream_conf_enable
+--- error_log
+invalid bulk length: -1
+--- no_error_log eval
+[
+    qr/\[error\](?!.*(?:invalid argument number|invalid len string|invalid 
bulk length))/,
+    qr/\[crit\]/,
+    "stack traceback",
+]

Reply via email to