diff options
| author | Hauke Mehrtens | 2026-05-03 20:47:29 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2026-05-03 23:59:31 +0000 |
| commit | d2551871b5e52b576236c0a2470f392eca81c2de (patch) | |
| tree | 83b37b33298d242dd9107a6f956c1e8aa35a678f | |
| parent | e619cb04cddba8316d6928ff99f55a49e6ddc561 (diff) | |
| download | uhttpd-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.c | 3 |
1 files changed, 2 insertions, 1 deletions
@@ -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 */ |