Skip to content

Reject oversized snapshot token_count before allocating (distributed DoS)#546

Open
gigioneggiando wants to merge 1 commit into
antirez:mainfrom
gigioneggiando:fix/dist-snapshot-check-order
Open

Reject oversized snapshot token_count before allocating (distributed DoS)#546
gigioneggiando wants to merge 1 commit into
antirez:mainfrom
gigioneggiando:fix/dist-snapshot-check-order

Conversation

@gigioneggiando

Copy link
Copy Markdown

Summary

In the SNAPSHOT_LOAD_BEGIN worker path, begin.token_count is only bounded to ~UINT32_MAX/4 by the header check (expected_token_bytes <= UINT32_MAX). The code then malloc()s token_count * 4 bytes and runs the full token read loop, and only afterwards does the matching-state check reject token_count > ctx_size.

Because the distributed protocol is unauthenticated (any peer reaching the port is treated as a cluster member), an untrusted peer can drive a multi-GB allocation and a full-length socket read before the request is rejected.

Fix

Move the token_count > ctx_size test ahead of the allocation and the read loop (discarding the body bytes, consistent with the header-validation path), so an over-large count is rejected up front. The existing later check is left in place for the other state fields.

Verification

Source-confirmed and compile-checked (verified the patched translation unit builds cleanly under the ASan/UBSan flags). I did not build a runtime PoC for this one — triggering it needs a fully loaded worker plus a socket peer, which I couldn't stand up cheaply. The exact pre-existing line ordering (malloc + read loop before the token_count > ctx_size check) is straightforward to confirm by eye.

Found during a coordinated security review of ds4.

In the SNAPSHOT_LOAD_BEGIN worker path, token_count is only bounded to
~UINT32_MAX/4 by the header check (expected_token_bytes <= UINT32_MAX).
The code then malloc()s token_count*4 bytes and runs the full token read
loop, and only afterwards does the matching-state check reject
token_count > ctx_size. An untrusted peer (the distributed protocol is
unauthenticated) can therefore drive a multi-GB allocation and a
full-length socket read before the request is rejected.

Move the token_count > ctx_size test ahead of the allocation and read
loop, discarding the body bytes, so an over-large count is rejected up
front.
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.

1 participant