From 4f0d3118e14e6714dcb58830df625859fc2d06b3 Mon Sep 17 00:00:00 2001 From: Zijie Zhao Date: Mon, 16 Mar 2026 01:12:03 -0500 Subject: [PATCH] Fix listpack memory leak in zipmap-to-hash conversion on error path (#14878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When loading RDB_TYPE_HASH_ZIPMAP objects, a listpack is allocated for the zipmap-to-listpack conversion. If the conversion loop encounters a duplicate key, allocation failure, or oversized listpack, the error handler frees the dict, field, encoded buffer, and object — but not the listpack. Add the missing lpFree(lp) call and a regression test that RESTOREs a zipmap with duplicate keys to exercise this path. --- src/rdb.c | 1 + tests/integration/corrupt-dump.tcl | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/rdb.c b/src/rdb.c index c41585bb31..241804517b 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -2839,6 +2839,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, int dbid, int *error) rdbReportCorruptRDB("Hash zipmap with dup elements, or big length (%u)", flen); dictRelease(dupSearchDict); sdsfree(field); + lpFree(lp); zfree(encoded); o->ptr = NULL; decrRefCount(o); diff --git a/tests/integration/corrupt-dump.tcl b/tests/integration/corrupt-dump.tcl index 654b887061..59c7c8b3d3 100644 --- a/tests/integration/corrupt-dump.tcl +++ b/tests/integration/corrupt-dump.tcl @@ -377,6 +377,30 @@ test {corrupt payload: hash empty zipmap} { } } +test {corrupt payload: hash zipmap with duplicate keys} { + # Craft a structurally valid zipmap with duplicate key "a" to trigger the + # dictAdd failure path in rdbLoadObject (RDB_TYPE_HASH_ZIPMAP). + # This exercises the error-handling branch that must free the listpack + # allocated for the zipmap-to-listpack conversion. + # + # Zipmap layout (12 bytes): + # \x02 zmlen=2 + # \x01 a key "a" (len 1) + # \x01 \x00 b value "b" (len 1, free 0) + # \x01 a key "a" again (duplicate!) + # \x01 \x00 d value "d" (len 1, free 0) + # \xFF end + start_server [list overrides [list loglevel verbose use-exit-on-panic yes crash-memcheck-enabled no] ] { + r config set sanitize-dump-payload yes + r debug set-skip-checksum-validation 1 + catch { + r RESTORE _hash 0 "\x09\x0C\x02\x01\x61\x01\x00\x62\x01\x61\x01\x00\x64\xFF\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00" + } err + assert_match "*Bad data format*" $err + verify_log_message 0 "*Hash zipmap with dup elements*" 0 + } +} + test {corrupt payload: fuzzer findings - NPD in streamIteratorGetID} { start_server [list overrides [list loglevel verbose use-exit-on-panic yes crash-memcheck-enabled no] ] { r config set sanitize-dump-payload no