From 350e8fa3a94838bcc936782315b3472615fe6517 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 5 Oct 2010 22:01:19 +0000 Subject: http: raise empty backtrace for HttpParserError It's expensive to generate a backtrace and this exception is only triggered by bad clients. So make it harder for them to DoS us by sending bad requests. --- ext/unicorn_http/global_variables.h | 4 +++- ext/unicorn_http/unicorn_http.rl | 21 +++++++++++++++------ test/unit/test_http_parser_ng.rb | 11 +++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/ext/unicorn_http/global_variables.h b/ext/unicorn_http/global_variables.h index 7319bcd..8377704 100644 --- a/ext/unicorn_http/global_variables.h +++ b/ext/unicorn_http/global_variables.h @@ -35,13 +35,15 @@ static VALUE g_HEAD; static const char * const MAX_##N##_LENGTH_ERR = \ "HTTP element " # N " is longer than the " # length " allowed length." +NORETURN(static void parser_error(const char *)); + /** * Validates the max length of given input and throws an HttpParserError * exception if over. */ #define VALIDATE_MAX_LENGTH(len, N) do { \ if (len > MAX_##N##_LENGTH) \ - rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); \ + parser_error(MAX_##N##_LENGTH_ERR); \ } while (0) /** Defines global strings in the init method. */ diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl index 1ad2a5d..390bbe8 100644 --- a/ext/unicorn_http/unicorn_http.rl +++ b/ext/unicorn_http/unicorn_http.rl @@ -48,6 +48,15 @@ struct http_parser { static void finalize_header(struct http_parser *hp, VALUE req); +static void parser_error(const char *msg) +{ + VALUE exc = rb_exc_new2(eHttpParserError, msg); + VALUE bt = rb_ary_new(); + + rb_funcall(exc, rb_intern("set_backtrace"), 1, bt); + rb_exc_raise(exc); +} + #define REMAINING (unsigned long)(pe - p) #define LEN(AT, FPC) (FPC - buffer - hp->AT) #define MARK(M,FPC) (hp->M = (FPC) - buffer) @@ -132,7 +141,7 @@ http_version(struct http_parser *hp, VALUE req, const char *ptr, size_t len) static inline void hp_invalid_if_trailer(struct http_parser *hp) { if (HP_FL_TEST(hp, INTRAILER)) - rb_raise(eHttpParserError, "invalid Trailer"); + parser_error("invalid Trailer"); } static void write_cont_value(struct http_parser *hp, @@ -141,7 +150,7 @@ static void write_cont_value(struct http_parser *hp, char *vptr; if (hp->cont == Qfalse) - rb_raise(eHttpParserError, "invalid continuation line"); + parser_error("invalid continuation line"); if (NIL_P(hp->cont)) return; /* we're ignoring this header (probably Host:) */ @@ -192,7 +201,7 @@ static void write_value(VALUE req, struct http_parser *hp, } else if (f == g_content_length) { hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v)); if (hp->len.content < 0) - rb_raise(eHttpParserError, "invalid Content-Length"); + parser_error("invalid Content-Length"); HP_FL_SET(hp, HASBODY); hp_invalid_if_trailer(hp); } else if (f == g_http_transfer_encoding) { @@ -285,7 +294,7 @@ static void write_value(VALUE req, struct http_parser *hp, action add_to_chunk_size { hp->len.chunk = step_incr(hp->len.chunk, fc, 16); if (hp->len.chunk < 0) - rb_raise(eHttpParserError, "invalid chunk size"); + parser_error("invalid chunk size"); } action header_done { finalize_header(hp, req); @@ -550,7 +559,7 @@ static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data) } if (hp->cs == http_parser_error) - rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails."); + parser_error("Invalid HTTP format, parsing fails."); return Qnil; } @@ -643,7 +652,7 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data) hp->s.dest_offset = 0; http_parser_execute(hp, buf, dptr, dlen); if (hp->cs == http_parser_error) - rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails."); + parser_error("Invalid HTTP format, parsing fails."); assert(hp->s.dest_offset <= hp->offset && "destination buffer overflow"); diff --git a/test/unit/test_http_parser_ng.rb b/test/unit/test_http_parser_ng.rb index cb30f32..8e8c933 100644 --- a/test/unit/test_http_parser_ng.rb +++ b/test/unit/test_http_parser_ng.rb @@ -440,6 +440,17 @@ class HttpParserNgTest < Test::Unit::TestCase end end + def test_backtrace_is_empty + begin + @parser.headers({}, "AAADFSFDSFD\r\n\r\n") + assert false, "should never get here line:#{__LINE__}" + rescue HttpParserError => e + assert_equal [], e.backtrace + return + end + assert false, "should never get here line:#{__LINE__}" + end + def test_ignore_version_header http = "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n" req = {} -- cgit v1.2.3-24-ge0c7