From 714cbb2dfbea9a521960b82b6239a991a64e3b9a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 23 Mar 2020 12:44:24 +0100 Subject: [PATCH] Fix ABI breakage Fix ABI breakage introduced in commit 7d5c99d ("Support multi-coding Transfer-Encoding") by undoing the change in `sizeof(http_parser)`. Restore the size of the `flags` field and shrink the `index` field from 7 to 5 bits. It track strings up to `strlen("Transfer-Encoding")` bytes so 2^5 == 32 is wide enough. Fixes: https://github.com/nodejs/http-parser/issues/502 PR-URL: https://github.com/nodejs/http-parser/pull/503 Reviewed-By: Fedor Indutny Reviewed-By: Matteo Collina Reviewed-By: Sam Roberts --- http_parser.c | 11 +++++++---- http_parser.h | 13 +++++++------ test.c | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/http_parser.c b/http_parser.c index 0f76b6a..95ff42f 100644 --- a/http_parser.c +++ b/http_parser.c @@ -731,6 +731,7 @@ reexecute: if (ch == CR || ch == LF) break; parser->flags = 0; + parser->extra_flags = 0; parser->content_length = ULLONG_MAX; if (ch == 'H') { @@ -768,6 +769,7 @@ reexecute: if (ch == CR || ch == LF) break; parser->flags = 0; + parser->extra_flags = 0; parser->content_length = ULLONG_MAX; if (ch == 'H') { @@ -925,6 +927,7 @@ reexecute: if (ch == CR || ch == LF) break; parser->flags = 0; + parser->extra_flags = 0; parser->content_length = ULLONG_MAX; if (UNLIKELY(!IS_ALPHA(ch))) { @@ -1338,7 +1341,7 @@ reexecute: parser->header_state = h_general; } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { parser->header_state = h_transfer_encoding; - parser->flags |= F_TRANSFER_ENCODING; + parser->extra_flags |= F_TRANSFER_ENCODING >> 8; } break; @@ -1800,7 +1803,7 @@ reexecute: /* Cannot us transfer-encoding and a content-length header together per the HTTP specification. (RFC 7230 Section 3.3.3) */ - if ((parser->flags & F_TRANSFER_ENCODING) && + if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) && (parser->flags & F_CONTENTLENGTH)) { /* Allow it for lenient parsing as long as `Transfer-Encoding` is * not `chunked` @@ -1886,7 +1889,7 @@ reexecute: /* chunked encoding - ignore Content-Length header, * prepare for a chunk */ UPDATE_STATE(s_chunk_size_start); - } else if (parser->flags & F_TRANSFER_ENCODING) { + } else if (parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) { if (parser->type == HTTP_REQUEST && !lenient) { /* RFC 7230 3.3.3 */ @@ -2162,7 +2165,7 @@ http_message_needs_eof (const http_parser *parser) } /* RFC 7230 3.3.3, see `s_headers_almost_done` */ - if ((parser->flags & F_TRANSFER_ENCODING) && + if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) && (parser->flags & F_CHUNKED) == 0) { return 1; } diff --git a/http_parser.h b/http_parser.h index 983d88a..d36c80d 100644 --- a/http_parser.h +++ b/http_parser.h @@ -225,7 +225,7 @@ enum flags , F_UPGRADE = 1 << 5 , F_SKIPBODY = 1 << 6 , F_CONTENTLENGTH = 1 << 7 - , F_TRANSFER_ENCODING = 1 << 8 + , F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */ }; @@ -272,13 +272,13 @@ enum flags "unexpected content-length header") \ XX(INVALID_CHUNK_SIZE, \ "invalid character in chunk size header") \ - XX(INVALID_TRANSFER_ENCODING, \ - "request has invalid transfer-encoding") \ XX(INVALID_CONSTANT, "invalid constant string") \ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ XX(STRICT, "strict mode assertion failed") \ XX(PAUSED, "parser is paused") \ - XX(UNKNOWN, "an unknown error occurred") + XX(UNKNOWN, "an unknown error occurred") \ + XX(INVALID_TRANSFER_ENCODING, \ + "request has invalid transfer-encoding") \ /* Define HPE_* values for each errno value above */ @@ -296,11 +296,12 @@ enum http_errno { struct http_parser { /** PRIVATE **/ unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ unsigned int state : 7; /* enum state from http_parser.c */ unsigned int header_state : 7; /* enum header_state from http_parser.c */ - unsigned int index : 7; /* index into current matcher */ + unsigned int index : 5; /* index into current matcher */ + unsigned int extra_flags : 2; unsigned int lenient_http_headers : 1; - unsigned int flags : 16; /* F_* values from 'flags' enum; semi-public */ uint32_t nread; /* # bytes read in various scenarios */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ diff --git a/test.c b/test.c index d088179..7983424 100644 --- a/test.c +++ b/test.c @@ -4221,6 +4221,7 @@ main (void) printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version); printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser)); + assert(sizeof(http_parser) == 4 + 4 + 8 + 2 + 2 + 4 + sizeof(void *)); //// API test_preserve_data();