diff options
| author | Hauke Mehrtens | 2026-03-31 20:56:47 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2026-05-15 00:29:10 +0000 |
| commit | 9404e6c62bb7dc452a294a62c33eb18beb9ef433 (patch) | |
| tree | f7ed0a25078037222a14de53949c4bf475bf3a74 | |
| parent | 2c869c094c25e1b6b7c1ba786f22280a6c153447 (diff) | |
| download | uhttpd-9404e6c62bb7dc452a294a62c33eb18beb9ef433.tar.gz | |
client: parse chunked transfer chunk size safely
The chunk-size hex field is parsed into content_length (int). The
previous strtoul could wrap negative-looking inputs into huge
positive values; switching to strtol exposes negatives but leaves
the 64-bit INT_MAX overflow window open, where a long value above
INT_MAX truncates implementation-defined when narrowed.
Parse into a long, check errno for strtol overflow, and reject any
chunk size above INT_MAX. On malformed or out-of-range input the
chunked transfer state is torn down as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
| -rw-r--r-- | client.c | 15 |
1 files changed, 10 insertions, 5 deletions
@@ -457,6 +457,7 @@ void client_poll_post_data(struct client *cl) while (1) { char *sep; + long chunk_len; int offset = 0; int cur_len; @@ -492,18 +493,22 @@ void client_poll_post_data(struct client *cl) *sep = 0; - r->content_length = strtoul(buf + offset, &sep, 16); - if (r->transfer_chunked < 2) - r->transfer_chunked++; - ustream_consume(cl->us, sep + 2 - buf); + errno = 0; + chunk_len = strtol(buf + offset, &sep, 16); /* invalid chunk length */ - if ((sep && *sep) || r->content_length < 0) { + if ((sep && *sep) || errno || chunk_len < 0 || chunk_len > INT_MAX) { + ustream_consume(cl->us, sep + 2 - buf); r->content_length = 0; r->transfer_chunked = 0; break; } + if (r->transfer_chunked < 2) + r->transfer_chunked++; + ustream_consume(cl->us, sep + 2 - buf); + r->content_length = (int)chunk_len; + /* empty chunk == eof */ if (!r->content_length) { r->transfer_chunked = false; |