From 9f59cd926f242b5aa0bc04149368d1cb0078a461 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 9 Jun 2009 15:16:36 +0200 Subject: [PATCH] Check for buffer overflow attacks. --- README.md | 3 ++- http_parser.h | 7 ++++++ http_parser.rl | 64 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 577d159..e0c2b9c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ HTTP Parser This is a parser for HTTP messages written in C. It parses both requests and responses. The parser is designed to be used in performance HTTP applications. It does not make any allocations, it does not buffer data, and -it can be interrupted at anytime. It only requires about 100 bytes of data +it can be interrupted at anytime. It only requires about 128 bytes of data per message stream (in a web server that is per connection). Features: @@ -22,6 +22,7 @@ Features: * http version * request path, query string, fragment * message body + * Defends against buffer overflow attacks. Usage ----- diff --git a/http_parser.h b/http_parser.h index c83a319..08163a4 100644 --- a/http_parser.h +++ b/http_parser.h @@ -71,14 +71,21 @@ struct http_parser { size_t chunk_size; unsigned eating:1; + unsigned buffer_overflow:1; size_t body_read; const char *header_field_mark; + size_t header_field_size; const char *header_value_mark; + size_t header_value_size; const char *query_string_mark; + size_t query_string_size; const char *path_mark; + size_t path_size; const char *uri_mark; + size_t uri_size; const char *fragment_mark; + size_t fragment_size; /** READ-ONLY **/ unsigned short status_code; /* responses only */ diff --git a/http_parser.rl b/http_parser.rl index c136d66..cb4c7f3 100644 --- a/http_parser.rl +++ b/http_parser.rl @@ -41,13 +41,24 @@ static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 #define MIN(a,b) (a < b ? a : b) #define NULL (void*)(0) +#define MAX_FIELD_SIZE 80*1024 + #define REMAINING (pe - p) #define CALLBACK(FOR) \ - if (parser->FOR##_mark && parser->on_##FOR) { \ - callback_return_value = \ - parser->on_##FOR(parser, parser->FOR##_mark, \ - p - parser->FOR##_mark); \ - } +do { \ + if (parser->FOR##_mark) { \ + parser->FOR##_size += p - parser->FOR##_mark; \ + if (parser->FOR##_size > MAX_FIELD_SIZE) { \ + parser->buffer_overflow = TRUE; \ + return 0; \ + } \ + if (parser->on_##FOR) { \ + callback_return_value = parser->on_##FOR(parser, \ + parser->FOR##_mark, \ + p - parser->FOR##_mark); \ + } \ + } \ +} while(0) #define RESET_PARSER(parser) \ parser->chunk_size = 0; \ @@ -100,47 +111,76 @@ do { \ %%{ machine http_parser; - action mark_header_field { parser->header_field_mark = p; } - action mark_header_value { parser->header_value_mark = p; } - action mark_fragment { parser->fragment_mark = p; } - action mark_query_string { parser->query_string_mark = p; } - action mark_request_path { parser->path_mark = p; } - action mark_request_uri { parser->uri_mark = p; } + action mark_header_field { + parser->header_field_mark = p; + parser->header_field_size = 0; + } + + action mark_header_value { + parser->header_value_mark = p; + parser->header_value_size = 0; + } + + action mark_fragment { + parser->fragment_mark = p; + parser->fragment_size = 0; + } + + action mark_query_string { + parser->query_string_mark = p; + parser->query_string_size = 0; + } + + action mark_request_path { + parser->path_mark = p; + parser->path_size = 0; + } + + action mark_request_uri { + parser->uri_mark = p; + parser->uri_size = 0; + } action header_field { CALLBACK(header_field); if (callback_return_value != 0) fbreak; parser->header_field_mark = NULL; + parser->header_field_size = 0; } action header_value { CALLBACK(header_value); if (callback_return_value != 0) fbreak; parser->header_value_mark = NULL; + parser->header_value_size = 0; } action request_uri { CALLBACK(uri); if (callback_return_value != 0) fbreak; parser->uri_mark = NULL; + parser->uri_size = 0; } action fragment { CALLBACK(fragment); if (callback_return_value != 0) fbreak; parser->fragment_mark = NULL; + parser->fragment_size = 0; } action query_string { CALLBACK(query_string); if (callback_return_value != 0) fbreak; parser->query_string_mark = NULL; + parser->query_string_size = 0; } action request_path { CALLBACK(path); if (callback_return_value != 0) fbreak; parser->path_mark = NULL; + parser->path_size = 0; } action headers_complete { @@ -347,6 +387,7 @@ http_parser_init (http_parser *parser, enum http_parser_type type) %% write init; parser->cs = cs; parser->type = type; + parser->buffer_overflow = 0; parser->on_message_begin = NULL; parser->on_path = NULL; @@ -405,6 +446,7 @@ out: int http_parser_has_error (http_parser *parser) { + if (parser->buffer_overflow) return TRUE; return parser->cs == http_parser_error; }