src: tighten header field/value loops

make-http-max-header-size-gyp-configurable
Fedor Indutny 10 years ago
parent 6132d1fefa
commit 0cb0ee672c

@ -128,6 +128,26 @@ do { \
} \ } \
} while (0) } while (0)
/* Don't allow the total size of the HTTP headers (including the status
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
* embedders against denial-of-service attacks where the attacker feeds
* us a never-ending header that the embedder keeps buffering.
*
* This check is arguably the responsibility of embedders but we're doing
* it on the embedder's behalf because most won't bother and this way we
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
* than any reasonable request or response so this should never affect
* day-to-day operation.
*/
#define COUNT_HEADER_SIZE(V) \
do { \
parser->nread += (V); \
if (parser->nread > (HTTP_MAX_HEADER_SIZE)) { \
SET_ERRNO(HPE_HEADER_OVERFLOW); \
goto error; \
} \
} while (0)
#define PROXY_CONNECTION "proxy-connection" #define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection" #define CONNECTION "connection"
@ -655,24 +675,8 @@ size_t http_parser_execute (http_parser *parser,
for (p=data; p != data + len; p++) { for (p=data; p != data + len; p++) {
ch = *p; ch = *p;
if (PARSING_HEADER(CURRENT_STATE())) { if (PARSING_HEADER(CURRENT_STATE()))
++parser->nread; COUNT_HEADER_SIZE(1);
/* Don't allow the total size of the HTTP headers (including the status
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
* embedders against denial-of-service attacks where the attacker feeds
* us a never-ending header that the embedder keeps buffering.
*
* This check is arguably the responsibility of embedders but we're doing
* it on the embedder's behalf because most won't bother and this way we
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
* than any reasonable request or response so this should never affect
* day-to-day operation.
*/
if (parser->nread > (HTTP_MAX_HEADER_SIZE)) {
SET_ERRNO(HPE_HEADER_OVERFLOW);
goto error;
}
}
reexecute_byte: reexecute_byte:
switch (CURRENT_STATE()) { switch (CURRENT_STATE()) {
@ -1293,9 +1297,14 @@ size_t http_parser_execute (http_parser *parser,
case s_header_field: case s_header_field:
{ {
c = TOKEN(ch); const char* start = p;
for (; p != data + len; p++) {
ch = *p;
c = TOKEN(ch);
if (!c)
break;
if (c) {
switch (parser->header_state) { switch (parser->header_state) {
case h_general: case h_general:
break; break;
@ -1396,6 +1405,12 @@ size_t http_parser_execute (http_parser *parser,
assert(0 && "Unknown header_state"); assert(0 && "Unknown header_state");
break; break;
} }
}
COUNT_HEADER_SIZE(p - start);
if (p == data + len) {
--p;
break; break;
} }
@ -1478,98 +1493,107 @@ size_t http_parser_execute (http_parser *parser,
case s_header_value: case s_header_value:
{ {
const char* start = p;
for (; p != data + len; p++) {
ch = *p;
if (ch == CR) {
UPDATE_STATE(s_header_almost_done);
CALLBACK_DATA(header_value);
break;
}
if (ch == CR) { if (ch == LF) {
UPDATE_STATE(s_header_almost_done); UPDATE_STATE(s_header_almost_done);
CALLBACK_DATA(header_value); COUNT_HEADER_SIZE(p - start);
break; CALLBACK_DATA_NOADVANCE(header_value);
} goto reexecute_byte;
}
if (ch == LF) { c = LOWER(ch);
UPDATE_STATE(s_header_almost_done);
CALLBACK_DATA_NOADVANCE(header_value);
goto reexecute_byte;
}
c = LOWER(ch); switch (parser->header_state) {
case h_general:
break;
switch (parser->header_state) { case h_connection:
case h_general: case h_transfer_encoding:
break; assert(0 && "Shouldn't get here.");
break;
case h_connection: case h_content_length:
case h_transfer_encoding: {
assert(0 && "Shouldn't get here."); uint64_t t;
break;
case h_content_length: if (ch == ' ') break;
{
uint64_t t;
if (ch == ' ') break; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
if (!IS_NUM(ch)) { t = parser->content_length;
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); t *= 10;
goto error; t += ch - '0';
}
t = parser->content_length; /* Overflow? Test against a conservative limit for simplicity. */
t *= 10; if ((ULLONG_MAX - 10) / 10 < parser->content_length) {
t += ch - '0'; SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
/* Overflow? Test against a conservative limit for simplicity. */ parser->content_length = t;
if ((ULLONG_MAX - 10) / 10 < parser->content_length) { break;
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
} }
parser->content_length = t; /* Transfer-Encoding: chunked */
break; case h_matching_transfer_encoding_chunked:
} parser->index++;
if (parser->index > sizeof(CHUNKED)-1
|| c != CHUNKED[parser->index]) {
parser->header_state = h_general;
} else if (parser->index == sizeof(CHUNKED)-2) {
parser->header_state = h_transfer_encoding_chunked;
}
break;
/* Transfer-Encoding: chunked */ /* looking for 'Connection: keep-alive' */
case h_matching_transfer_encoding_chunked: case h_matching_connection_keep_alive:
parser->index++; parser->index++;
if (parser->index > sizeof(CHUNKED)-1 if (parser->index > sizeof(KEEP_ALIVE)-1
|| c != CHUNKED[parser->index]) { || c != KEEP_ALIVE[parser->index]) {
parser->header_state = h_general; parser->header_state = h_general;
} else if (parser->index == sizeof(CHUNKED)-2) { } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
parser->header_state = h_transfer_encoding_chunked; parser->header_state = h_connection_keep_alive;
} }
break; break;
/* looking for 'Connection: keep-alive' */ /* looking for 'Connection: close' */
case h_matching_connection_keep_alive: case h_matching_connection_close:
parser->index++; parser->index++;
if (parser->index > sizeof(KEEP_ALIVE)-1 if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
|| c != KEEP_ALIVE[parser->index]) { parser->header_state = h_general;
parser->header_state = h_general; } else if (parser->index == sizeof(CLOSE)-2) {
} else if (parser->index == sizeof(KEEP_ALIVE)-2) { parser->header_state = h_connection_close;
parser->header_state = h_connection_keep_alive; }
} break;
break;
/* looking for 'Connection: close' */ case h_transfer_encoding_chunked:
case h_matching_connection_close: case h_connection_keep_alive:
parser->index++; case h_connection_close:
if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { if (ch != ' ') parser->header_state = h_general;
break;
default:
UPDATE_STATE(s_header_value);
parser->header_state = h_general; parser->header_state = h_general;
} else if (parser->index == sizeof(CLOSE)-2) { break;
parser->header_state = h_connection_close; }
} }
break;
case h_transfer_encoding_chunked: COUNT_HEADER_SIZE(p - start);
case h_connection_keep_alive:
case h_connection_close:
if (ch != ' ') parser->header_state = h_general;
break;
default: if (p == data + len)
UPDATE_STATE(s_header_value); --p;
parser->header_state = h_general;
break;
}
break; break;
} }

Loading…
Cancel
Save