Double-free / Memory safety
Description
A double-free memory safety vulnerability exists during RDB/RESTORE loading of a stream when a consumer's Pending Entries List (PEL) contains a duplicate entry for the same pending ID. In the flawed path, if inserting into the consumer's PEL fails (due to a duplicate), the code would call streamFreeNACK(s, nack) even though the NACK is already owned by the group's PEL (cgroup->pel). This could lead to freeing the same object twice during stream/group teardown, causing a crash or memory corruption. The fix modifies the error path to stop freeing the NACK in this case and rely on decrRefCount(o) for cleanup, consistent with ownership where the NACK belongs to cgroup->pel. This eliminates the double-free scenario and resolves the vulnerability in affected versions.
Commit Details
Author: Shubham S Taple
Date: 2026-05-14 06:17 UTC
Message:
Fix double free when loading streams with duplicate consumer PEL entries (#15095)
Fixes #15082
## Problem
Loading a stream from RDB/RESTORE with a malformed consumer PEL (the
same pending ID listed twice for one consumer) hit an error path that
called streamFreeNACK() on a nack that was still referenced from the
group’s global PEL (cgroup->pel). Teardown then freed that nack again
while destroying the stream, causing a double free and a possible server
crash.
## Fix
On the duplicate consumer PEL branch in src/rdb.c, stop calling
streamFreeNACK(s, nack) when raxTryInsert(consumer->pel, …) fails. Keep
reporting corruption and rely on decrRefCount(o) for cleanup, consistent
with other paths where the nack is owned only by cgroup->pel.
Triage Assessment
Vulnerability Type: Double-free / Memory safety
Confidence: HIGH
Reasoning:
Addresses a memory safety issue (double free) that could lead to server crash. The fix ensures the NACK is not freed in the error path when a duplicate consumer PEL entry is encountered, preventing a potential crash/exploitation via memory corruption.
Verification Assessment
Vulnerability Type: Double-free / Memory safety
Confidence: HIGH
Affected Versions: 8.6.x prior to this fix; specifically 8.6.2 and earlier in the 8.6 line.
Code Diff
diff --git a/src/rdb.c b/src/rdb.c
index d6fb3f3d23f..a450aff6e69 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -3584,7 +3584,6 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, int dbid, int *error)
rdbReportCorruptRDB("Duplicated consumer PEL entry "
" loading a stream consumer "
"group");
- streamFreeNACK(s, nack);
decrRefCount(o);
return NULL;
}