Skip to content

Commit 3b723c4

Browse files
committed
client: perform strict chunk size parsing
We can not rely on `strtoul()` to parse hexadecimal chunk sizes as it accepts a wider range of inputs than what is allowed by the HTTP spec. Decode the chunk sizes manually and fix skipping chunk extension headers while we're at it. Also ensure that there's no trailing garbage after the size and that we bail out on overflows. Fixes: #3 Signed-off-by: Jo-Philipp Wich <jo@mein.io>
1 parent 34a8a74 commit 3b723c4

File tree

1 file changed

+48
-4
lines changed

1 file changed

+48
-4
lines changed

client.c

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,52 @@ static void client_parse_header(struct client *cl, char *data)
404404
cl->state = CLIENT_STATE_HEADER;
405405
}
406406

407+
static int parse_chunksize(char *buf, char *end)
408+
{
409+
int size = 0;
410+
411+
if (buf == end)
412+
return -1; /* empty size */
413+
414+
while (buf < end) {
415+
int n;
416+
417+
if (*buf >= '0' && *buf <= '9')
418+
n = *buf - '0';
419+
else if (*buf >= 'a' && *buf <= 'f')
420+
n = 10 + *buf - 'a';
421+
else if (*buf >= 'A' && *buf <= 'F')
422+
n = 10 + *buf - 'A';
423+
else
424+
break;
425+
426+
if (size > INT_MAX / 16)
427+
return -1; /* overflow */
428+
429+
size *= 16;
430+
431+
if (size > INT_MAX - n)
432+
return -1; /* overflow */
433+
434+
size += n;
435+
buf++;
436+
}
437+
438+
/* if whitespace follows, there must be header extension as well... */
439+
char *p = buf;
440+
441+
while (p < end && (*p == ' ' || *p == '\t'))
442+
p++;
443+
444+
if (p > buf && *p != ';')
445+
return -1; /* whitespace after size but no chunk-ext */
446+
447+
if (p == buf && buf < end)
448+
return -1; /* garbage after size */
449+
450+
return size;
451+
}
452+
407453
void client_poll_post_data(struct client *cl)
408454
{
409455
struct dispatch *d = &cl->dispatch;
@@ -450,14 +496,12 @@ void client_poll_post_data(struct client *cl)
450496
if (!sep)
451497
break;
452498

453-
*sep = 0;
454-
455-
r->content_length = strtoul(buf + offset, &sep, 16);
499+
r->content_length = parse_chunksize(buf + offset, sep);
456500
r->transfer_chunked++;
457501
ustream_consume(cl->us, sep + 2 - buf);
458502

459503
/* invalid chunk length */
460-
if ((sep && *sep) || r->content_length < 0) {
504+
if (r->content_length < 0) {
461505
r->content_length = 0;
462506
r->transfer_chunked = 0;
463507
break;

0 commit comments

Comments
 (0)