From dee0d11a74dbb8efcec8148bd2ff43d203b48931 Mon Sep 17 00:00:00 2001 From: Oran Agra Date: Sun, 7 Sep 2025 16:56:38 +0300 Subject: [PATCH] Avoid OOM error on very big args due to allocation of very big query buffer (#14327) We have code to assume that if we're facing a big argument, then the next argument is likely to be very big too, so we allocate another huge query buffer. This will backfire and return OOM error on any command with an argument that's larger than half the memory limit (even if the db completely empty). To mitigate that, we reserve query buffer for another big argument, only if that big argument is less than 1/30 of the memory limit. --- src/networking.c | 10 +++++++--- tests/unit/maxmemory.tcl | 13 +++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/networking.c b/src/networking.c index 23f1c21837..9f4fec0d71 100644 --- a/src/networking.c +++ b/src/networking.c @@ -2684,9 +2684,13 @@ int processMultibulkBuffer(client *c) { c->argv[c->argc++] = createObject(OBJ_STRING,c->querybuf); c->argv_len_sum += c->bulklen; sdsIncrLen(c->querybuf,-2); /* remove CRLF */ - /* Assume that if we saw a fat argument we'll see another one - * likely... */ - c->querybuf = sdsnewlen(SDS_NOINIT,c->bulklen+2); + /* Assume that if we saw a fat argument we'll see another one likely... + * But only if that fat argument is not too big compared to the memory limit. */ + if (!server.maxmemory || (size_t)c->bulklen < server.maxmemory / 32) { + c->querybuf = sdsnewlen(SDS_NOINIT,c->bulklen+2); + } else { + c->querybuf = sdsnewlen(SDS_NOINIT, PROTO_IOBUF_LEN); + } sdsclear(c->querybuf); querybuf_len = sdslen(c->querybuf); /* Update cached length */ } else { diff --git a/tests/unit/maxmemory.tcl b/tests/unit/maxmemory.tcl index 23a9daab89..62f5965a6b 100644 --- a/tests/unit/maxmemory.tcl +++ b/tests/unit/maxmemory.tcl @@ -1,4 +1,17 @@ start_server {tags {"maxmemory" "external:skip"}} { + + test {SET and RESTORE key nearly as large as the memory limit} { + r flushall + set used [s used_memory] + r config set maxmemory [expr {$used+10000000}] + r set foo [string repeat a 8000000] + set encoded [r dump foo] + r del foo + r restore foo 0 $encoded + r strlen foo + } {8000000} {logreqres:skip} + + r flushall r config set maxmemory 11mb r config set maxmemory-policy allkeys-lru set server_pid [s process_id]