Skip to content
This repository was archived by the owner on Nov 6, 2022. It is now read-only.

Commit 3beda5f

Browse files
committed
count headers, URL and start lines separately
Counting headers, URL and start lines separately because it was counting everything as headers before the headers_done state, throwing HPE_HEADER_OVERFLOW errors even when what really caused the overflow was the request URL being too long Creating an HPE_URL_OVERFLOW err to make sure embedders still have a request line limit, but now differentiating what went over such limit. Refs: nodejs/node#26553
1 parent 0d0a24e commit 3beda5f

File tree

3 files changed

+135
-52
lines changed

3 files changed

+135
-52
lines changed

http_parser.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <limits.h>
2727

2828
static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE;
29+
static uint32_t max_url_size = HTTP_MAX_URL_SIZE;
2930

3031
#ifndef ULLONG_MAX
3132
# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
@@ -160,6 +161,24 @@ do { \
160161
} \
161162
} while (0)
162163

164+
/* Don't allow the total size of the request uri to exceed
165+
* the max_url_size. If we don't check the request uri size and
166+
* it exceeds the max_header_size, the embedder will get an
167+
* HPE_HEADER_OVERFLOW, which is ambiguous in situations where
168+
* what caused the overflow matters, e.g. servers deciding to
169+
* reply either 414 or 431 status.
170+
*/
171+
#include <stdio.h>
172+
#define COUNT_URL_SIZE(V, O) \
173+
do { \
174+
nread += (uint32_t)(V); \
175+
url_offset = (uint8_t)(O); \
176+
if (UNLIKELY(nread > url_offset && \
177+
(nread - url_offset) > max_url_size)) { \
178+
SET_ERRNO(HPE_URL_OVERFLOW); \
179+
goto error; \
180+
} \
181+
} while(0) \
163182

164183
#define PROXY_CONNECTION "proxy-connection"
165184
#define CONNECTION "connection"
@@ -173,12 +192,17 @@ do { \
173192

174193
static const char *method_strings[] =
175194
{
176-
#define XX(num, name, string) #string,
195+
#define XX(num, name, string, length) #string,
177196
HTTP_METHOD_MAP(XX)
178197
#undef XX
179198
};
180199

181-
200+
static const uint8_t method_lengths[] =
201+
{
202+
#define XX(num, name, string, length) length,
203+
HTTP_METHOD_MAP(XX)
204+
#undef XX
205+
};
182206
/* Tokens as defined by rfc 2616. Also lowercases them.
183207
* token = 1*<any CHAR except CTLs or separators>
184208
* separators = "(" | ")" | "<" | ">" | "@"
@@ -358,9 +382,9 @@ enum state
358382
, s_message_done
359383
};
360384

361-
362-
#define PARSING_HEADER(state) (state <= s_headers_done)
363-
385+
#define PARSING_HEADER(state) (state > s_req_http_end && state <= s_headers_done)
386+
#define PARSING_URL(state) (state > s_req_spaces_before_url && state < s_req_http_start)
387+
#define PARSING_START_LINE(state) (state <= s_req_spaces_before_url || (state >= s_req_http_start && state <= s_req_http_end))
364388

365389
enum header_states
366390
{ h_general = 0
@@ -642,6 +666,8 @@ size_t http_parser_execute (http_parser *parser,
642666
{
643667
char c, ch;
644668
int8_t unhex_val;
669+
uint8_t url_offset;
670+
uint32_t spaces_before_url = 0;
645671
const char *p = data;
646672
const char *header_field_mark = 0;
647673
const char *header_value_mark = 0;
@@ -707,9 +733,14 @@ size_t http_parser_execute (http_parser *parser,
707733
for (p=data; p != data + len; p++) {
708734
ch = *p;
709735

710-
if (PARSING_HEADER(CURRENT_STATE()))
736+
if (PARSING_HEADER(CURRENT_STATE())) {
711737
COUNT_HEADER_SIZE(1);
712-
738+
} else if (PARSING_URL(CURRENT_STATE())) {
739+
COUNT_URL_SIZE(1, method_lengths[parser->method] + spaces_before_url);
740+
} else if (PARSING_START_LINE(CURRENT_STATE())) {
741+
nread++;
742+
}
743+
713744
reexecute:
714745
switch (CURRENT_STATE()) {
715746

@@ -1015,7 +1046,10 @@ size_t http_parser_execute (http_parser *parser,
10151046

10161047
case s_req_spaces_before_url:
10171048
{
1018-
if (ch == ' ') break;
1049+
if (ch == ' ') {
1050+
++spaces_before_url;
1051+
break;
1052+
}
10191053

10201054
MARK(url);
10211055
if (parser->method == HTTP_CONNECT) {
@@ -2499,3 +2533,8 @@ void
24992533
http_parser_set_max_header_size(uint32_t size) {
25002534
max_header_size = size;
25012535
}
2536+
2537+
void
2538+
http_parser_set_max_uri_size(uint32_t size) {
2539+
max_url_size = size;
2540+
}

http_parser.h

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ typedef unsigned __int64 uint64_t;
6363
# define HTTP_MAX_HEADER_SIZE (80*1024)
6464
#endif
6565

66+
/* Maximium uri size allowed. If the macro is not defined
67+
* before including this header then the default is used. To
68+
* change the maximum uri size, define the macro in the build
69+
* environment (e.g. -DHTTP_MAX_URL_SIZE=<value>). To remove
70+
* the effective limit on the size of the uri, define the macro
71+
* to a very large number (e.g. -DHTTP_MAX_URL_SIZE=0x7fffffff)
72+
*/
73+
#ifndef HTTP_MAX_URL_SIZE
74+
# define HTTP_MAX_URL_SIZE (80*1024)
75+
#endif
76+
6677
typedef struct http_parser http_parser;
6778
typedef struct http_parser_settings http_parser_settings;
6879

@@ -160,53 +171,53 @@ enum http_status
160171

161172

162173
/* Request Methods */
163-
#define HTTP_METHOD_MAP(XX) \
164-
XX(0, DELETE, DELETE) \
165-
XX(1, GET, GET) \
166-
XX(2, HEAD, HEAD) \
167-
XX(3, POST, POST) \
168-
XX(4, PUT, PUT) \
169-
/* pathological */ \
170-
XX(5, CONNECT, CONNECT) \
171-
XX(6, OPTIONS, OPTIONS) \
172-
XX(7, TRACE, TRACE) \
173-
/* WebDAV */ \
174-
XX(8, COPY, COPY) \
175-
XX(9, LOCK, LOCK) \
176-
XX(10, MKCOL, MKCOL) \
177-
XX(11, MOVE, MOVE) \
178-
XX(12, PROPFIND, PROPFIND) \
179-
XX(13, PROPPATCH, PROPPATCH) \
180-
XX(14, SEARCH, SEARCH) \
181-
XX(15, UNLOCK, UNLOCK) \
182-
XX(16, BIND, BIND) \
183-
XX(17, REBIND, REBIND) \
184-
XX(18, UNBIND, UNBIND) \
185-
XX(19, ACL, ACL) \
186-
/* subversion */ \
187-
XX(20, REPORT, REPORT) \
188-
XX(21, MKACTIVITY, MKACTIVITY) \
189-
XX(22, CHECKOUT, CHECKOUT) \
190-
XX(23, MERGE, MERGE) \
191-
/* upnp */ \
192-
XX(24, MSEARCH, M-SEARCH) \
193-
XX(25, NOTIFY, NOTIFY) \
194-
XX(26, SUBSCRIBE, SUBSCRIBE) \
195-
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
196-
/* RFC-5789 */ \
197-
XX(28, PATCH, PATCH) \
198-
XX(29, PURGE, PURGE) \
199-
/* CalDAV */ \
200-
XX(30, MKCALENDAR, MKCALENDAR) \
201-
/* RFC-2068, section 19.6.1.2 */ \
202-
XX(31, LINK, LINK) \
203-
XX(32, UNLINK, UNLINK) \
204-
/* icecast */ \
205-
XX(33, SOURCE, SOURCE) \
174+
#define HTTP_METHOD_MAP(XX) \
175+
XX(0, DELETE, DELETE, 6) \
176+
XX(1, GET, GET, 3) \
177+
XX(2, HEAD, HEAD, 4) \
178+
XX(3, POST, POST, 4) \
179+
XX(4, PUT, PUT, 3) \
180+
/* pathological */ \
181+
XX(5, CONNECT, CONNECT, 7) \
182+
XX(6, OPTIONS, OPTIONS, 7) \
183+
XX(7, TRACE, TRACE, 5) \
184+
/* WebDAV */ \
185+
XX(8, COPY, COPY, 4) \
186+
XX(9, LOCK, LOCK, 4) \
187+
XX(10, MKCOL, MKCOL, 5) \
188+
XX(11, MOVE, MOVE, 4) \
189+
XX(12, PROPFIND, PROPFIND, 8) \
190+
XX(13, PROPPATCH, PROPPATCH, 9) \
191+
XX(14, SEARCH, SEARCH, 6) \
192+
XX(15, UNLOCK, UNLOCK, 6) \
193+
XX(16, BIND, BIND, 4) \
194+
XX(17, REBIND, REBIND, 6) \
195+
XX(18, UNBIND, UNBIND, 6) \
196+
XX(19, ACL, ACL, 3) \
197+
/* subversion */ \
198+
XX(20, REPORT, REPORT, 6) \
199+
XX(21, MKACTIVITY, MKACTIVITY, 10) \
200+
XX(22, CHECKOUT, CHECKOUT, 8) \
201+
XX(23, MERGE, MERGE, 5) \
202+
/* upnp */ \
203+
XX(24, MSEARCH, M-SEARCH, 7) \
204+
XX(25, NOTIFY, NOTIFY, 6) \
205+
XX(26, SUBSCRIBE, SUBSCRIBE, 9) \
206+
XX(27, UNSUBSCRIBE, UNSUBSCRIBE, 11) \
207+
/* RFC-5789 */ \
208+
XX(28, PATCH, PATCH, 5) \
209+
XX(29, PURGE, PURGE, 5) \
210+
/* CalDAV */ \
211+
XX(30, MKCALENDAR, MKCALENDAR, 10) \
212+
/* RFC-2068, section 19.6.1.2 */ \
213+
XX(31, LINK, LINK, 4) \
214+
XX(32, UNLINK, UNLINK, 6) \
215+
/* icecast */ \
216+
XX(33, SOURCE, SOURCE, 6) \
206217

207218
enum http_method
208219
{
209-
#define XX(num, name, string) HTTP_##name = num,
220+
#define XX(num, name, string, length) HTTP_##name = num,
210221
HTTP_METHOD_MAP(XX)
211222
#undef XX
212223
};
@@ -252,6 +263,8 @@ enum flags
252263
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
253264
XX(HEADER_OVERFLOW, \
254265
"too many header bytes seen; overflow detected") \
266+
XX(URL_OVERFLOW, \
267+
"too many url bytes seen; overflow detected") \
255268
XX(CLOSED_CONNECTION, \
256269
"data received after completed connection: close message") \
257270
XX(INVALID_VERSION, "invalid HTTP version") \
@@ -433,6 +446,9 @@ int http_body_is_final(const http_parser *parser);
433446
/* Change the maximum header size provided at compile time. */
434447
void http_parser_set_max_header_size(uint32_t size);
435448

449+
/* Change the maximum uri size provided at compile time. */
450+
void http_parser_set_max_uri_size(uint32_t size);
451+
436452
#ifdef __cplusplus
437453
}
438454
#endif

test.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3756,6 +3756,32 @@ test_header_overflow_error (int req)
37563756
abort();
37573757
}
37583758

3759+
void
3760+
test_URL_OVERFLOW_error ()
3761+
{
3762+
http_parser parser;
3763+
http_parser_init(&parser, HTTP_REQUEST);
3764+
size_t parsed;
3765+
const char *buf;
3766+
buf = "GET /";
3767+
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3768+
assert(parsed == strlen(buf));
3769+
3770+
buf = "a";
3771+
size_t buflen = strlen(buf);
3772+
3773+
int i;
3774+
for (i = 0; i < HTTP_MAX_URL_SIZE - 1; i++) { //-1 because it started with a slash
3775+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3776+
if (parsed != buflen) {
3777+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_URL_OVERFLOW);
3778+
return;
3779+
}
3780+
}
3781+
3782+
fprintf(stderr, "\n*** Error expected but none in uri overflow test ***\n");
3783+
abort();
3784+
}
37593785

37603786
void
37613787
test_header_nread_value ()
@@ -4159,6 +4185,8 @@ main (void)
41594185
//// OVERFLOW CONDITIONS
41604186
test_no_overflow_parse_url();
41614187

4188+
test_URL_OVERFLOW_error();
4189+
41624190
test_header_overflow_error(HTTP_REQUEST);
41634191
test_no_overflow_long_body(HTTP_REQUEST, 1000);
41644192
test_no_overflow_long_body(HTTP_REQUEST, 100000);

0 commit comments

Comments
 (0)