summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHauke Mehrtens2026-05-03 20:47:29 +0000
committerHauke Mehrtens2026-05-03 23:59:31 +0000
commitd2551871b5e52b576236c0a2470f392eca81c2de (patch)
tree83b37b33298d242dd9107a6f956c1e8aa35a678f
parente619cb04cddba8316d6928ff99f55a49e6ddc561 (diff)
downloaduhttpd-d2551871b5e52b576236c0a2470f392eca81c2de.tar.gz
client: prevent transfer_chunked counter overflow
The transfer_chunked field is a uint8_t used as a tri-state marker: 0 means no chunked transfer, 1 means the first chunk header is next (no leading CRLF), and any value greater than 1 means a subsequent chunk header is next (expect a leading CRLF). The counter was incremented unconditionally on every parsed chunk, so after 255 chunks it wrapped back to 0. The outer loop then treats a wrapped value as "not chunked" and signals end-of-body, while later bytes on the wire are interpreted as the next pipelined request. An attacker could exploit this for HTTP request smuggling on a keep-alive connection by splitting a body into 256 or more small chunks. Cap the counter at 2 once the "subsequent chunk" state has been reached, since further increments are not meaningful. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
-rw-r--r--client.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/client.c b/client.c
index 4d343fe..6df2824 100644
--- a/client.c
+++ b/client.c
@@ -487,7 +487,8 @@ void client_poll_post_data(struct client *cl)
*sep = 0;
r->content_length = strtoul(buf + offset, &sep, 16);
- r->transfer_chunked++;
+ if (r->transfer_chunked < 2)
+ r->transfer_chunked++;
ustream_consume(cl->us, sep + 2 - buf);
/* invalid chunk length */