From e7628eafe0e7bdef98867a531007faf82f699c45 Mon Sep 17 00:00:00 2001 From: Luigi Colluto Date: Sat, 11 Jul 2026 19:10:18 +0200 Subject: [PATCH] Reject oversized snapshot token_count before allocating 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. --- ds4_distributed.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ds4_distributed.c b/ds4_distributed.c index d31c8e2a6..b1058c241 100644 --- a/ds4_distributed.c +++ b/ds4_distributed.c @@ -7000,6 +7000,17 @@ static int dist_worker_handle_snapshot_load( snprintf(err, sizeof(err), "invalid distributed snapshot load header"); } + /* Reject a token_count larger than the worker context up front, before the + * allocation and the read loop below. token_count is only bounded to + * ~UINT32_MAX/4 by the header check above, so without this a peer could + * drive a multi-GB malloc and a full-length socket read before the + * matching-state check further down (which also tests token_count > ctx_size) + * finally rejects the request. */ + if (!err[0] && begin.token_count > (uint32_t)state->ctx_size) { + dist_discard_bytes(upstream->fd, body_bytes); + snprintf(err, sizeof(err), "snapshot token count exceeds worker context size"); + } + int *tokens = NULL; if (!err[0]) { tokens = malloc((size_t)begin.token_count * sizeof(tokens[0]));