diff --git a/lib/isc/picohttpparser.c b/lib/isc/picohttpparser.c index df8f62d92f..708265f60e 100644 --- a/lib/isc/picohttpparser.c +++ b/lib/isc/picohttpparser.c @@ -54,16 +54,16 @@ #define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u) -#define CHECK_EOF() \ - if (buf == buf_end) { \ - *ret = -2; \ - return (NULL); \ +#define CHECK_EOF() \ + if (buf == buf_end) { \ + *ret = -2; \ + return NULL; \ } #define EXPECT_CHAR_NO_CHECK(ch) \ if (*buf++ != ch) { \ *ret = -1; \ - return (NULL); \ + return NULL; \ } #define EXPECT_CHAR(ch) \ @@ -88,7 +88,7 @@ *buf == '\177') \ { \ *ret = -1; \ - return (NULL); \ + return NULL; \ } \ } \ ++buf; \ @@ -154,17 +154,19 @@ get_token_to_eol(const char *buf, const char *buf_end, const char **token, "\177\177"; /* allow chars w. MSB set */ int found; buf = findchar_fast(buf, buf_end, ranges1, 6, &found); - if (found) + if (found) { goto FOUND_CTL; + } #else /* find non-printable char within the next 8 bytes, this is the hottest * code; manually inlined */ while (likely(buf_end - buf >= 8)) { -#define DOIT() \ - do { \ - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ - goto NonPrintable; \ - ++buf; \ +#define DOIT() \ + do { \ + if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ + goto NonPrintable; \ + } \ + ++buf; \ } while (0) DOIT(); DOIT(); @@ -246,7 +248,7 @@ is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) { if (*buf < '0' || '9' < *buf) { \ buf++; \ *ret = -1; \ - return (NULL); \ + return NULL; \ } \ *(valp_) = (mul_) * (*buf++ - '0'); @@ -603,6 +605,8 @@ phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t dst = 0, src = 0, bufsz = *_bufsz; ssize_t ret = -2; /* incomplete */ + decoder->_total_read += bufsz; + while (1) { switch (decoder->_state) { case CHUNKED_IN_CHUNK_SIZE: @@ -616,6 +620,20 @@ phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, ret = -1; goto Exit; } + /* the only characters that may appear + * after the chunk size are BWS, + * semicolon, or CRLF */ + switch (buf[src]) { + case ' ': + case '\011': + case ';': + case '\012': + case '\015': + break; + default: + ret = -1; + goto Exit; + } break; } if (decoder->_hex_count == sizeof(size_t) * 2) { @@ -727,6 +745,15 @@ Exit: memmove(buf + dst, buf + src, bufsz - src); } *_bufsz = dst; + /* if incomplete but the overhead of the chunked encoding is >=100KB and + * >80%, signal an error */ + if (ret == -2) { + decoder->_total_overhead += bufsz - dst; + if (decoder->_total_overhead >= 100 * 1024 && + decoder->_total_read - decoder->_total_overhead < + decoder->_total_read / 4) + ret = -1; + } return ret; } diff --git a/lib/isc/picohttpparser.h b/lib/isc/picohttpparser.h index 0657cb29c4..27df3b92d9 100644 --- a/lib/isc/picohttpparser.h +++ b/lib/isc/picohttpparser.h @@ -29,6 +29,7 @@ #ifndef picohttpparser_h #define picohttpparser_h +#include #include #ifdef _MSC_VER @@ -74,6 +75,8 @@ struct phr_chunked_decoder { char consume_trailer; /* if trailing headers should be consumed */ char _hex_count; char _state; + uint64_t _total_read; + uint64_t _total_overhead; }; /* the function rewrites the buffer given as (buf, bufsz) removing the chunked-