Skip to content

Fix UAF in unblockClientOnKey when reprocessed command frees the client (CVE-2026-23479)#3615

Merged
madolson merged 2 commits into
valkey-io:9.0from
ranshid:9.0.0-CVE-2026-23479
May 6, 2026
Merged

Fix UAF in unblockClientOnKey when reprocessed command frees the client (CVE-2026-23479)#3615
madolson merged 2 commits into
valkey-io:9.0from
ranshid:9.0.0-CVE-2026-23479

Conversation

@ranshid

@ranshid ranshid commented May 5, 2026

Copy link
Copy Markdown
Member

When a blocked client is woken up on a key, unblockClientOnKey() re-executes its pending command via processCommandAndResetClient(). That function returns C_ERR when the client was freed as a side effect of the re-execution (it detects this by server.current_client becoming NULL inside freeClient()).

The pre-fix code ignored the return value and unconditionally accessed c->flag.blocked / c->flag.module afterwards -- a use-after-free on the freed client.

The fix mirrors the existing dead-client handling in processPendingCommandAndInputBuffer() (src/networking.c): on C_ERR, unwind the execution unit, restore server.current_client, and return without touching c.

Note on testing: reaching the C_ERR branch requires the reprocessed command to synchronously free the current client (for example a replica/primary-link teardown or a module path that calls freeClient()). None of the blocking commands routed through unblockClientOnKey (BLPOP/BLMOVE/BRPOPLPUSH/BLMPOP/ XREADGROUP and friends) take that path from a plain RESP client, so a Tcl-level integration reproducer is not feasible without adding a test-only debug hook; this patch does not add such a hook. A targeted gtest reproducer was also considered but rejected as net-negative: it would require ~200 lines of fake-client scaffolding to exercise a 7-line defensive guard and would become a "change detector" as warned against in src/unit/README.md. The existing tests/unit/type/list.tcl suite (288 tests) exercises the happy path of this function and continues to pass unchanged. Running the suite under AddressSanitizer (make SANITIZER=address) provides the strongest signal against regressions of this class of bug.

ikolomi and others added 2 commits April 29, 2026 13:42
…nt (CVE-2026-23479)

When a blocked client is woken up on a key, unblockClientOnKey() re-executes
its pending command via processCommandAndResetClient(). That function returns
C_ERR when the client was freed as a side effect of the re-execution (it
detects this by server.current_client becoming NULL inside freeClient()).

The pre-fix code ignored the return value and unconditionally accessed
c->flag.blocked / c->flag.module afterwards -- a use-after-free on the freed
client.

The fix mirrors the existing dead-client handling in
processPendingCommandAndInputBuffer() (src/networking.c): on C_ERR, unwind
the execution unit, restore server.current_client, and return without
touching c.

Note on testing: reaching the C_ERR branch requires the reprocessed command
to synchronously free the current client (for example a replica/primary-link
teardown or a module path that calls freeClient()). None of the blocking
commands routed through unblockClientOnKey (BLPOP/BLMOVE/BRPOPLPUSH/BLMPOP/
XREADGROUP and friends) take that path from a plain RESP client, so a
Tcl-level integration reproducer is not feasible without adding a test-only
debug hook; this patch does not add such a hook. A targeted gtest reproducer
was also considered but rejected as net-negative: it would require ~200 lines
of fake-client scaffolding to exercise a 7-line defensive guard and would
become a "change detector" as warned against in src/unit/README.md. The
existing tests/unit/type/list.tcl suite (288 tests) exercises the happy path
of this function and continues to pass unchanged. Running the suite under
AddressSanitizer (make SANITIZER=address) provides the strongest signal
against regressions of this class of bug.

Signed-off-by: ikolomi <ikolomin@amazon.com>
@madolson madolson marked this pull request as ready for review May 5, 2026 23:53
@madolson madolson merged commit 587ad8d into valkey-io:9.0 May 6, 2026
60 of 68 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants