diff --git a/httplib.h b/httplib.h index fe3b716..a2de258 100644 --- a/httplib.h +++ b/httplib.h @@ -6374,7 +6374,16 @@ inline constexpr unsigned int str2tag_core(const char *s, size_t l, } inline unsigned int str2tag(const std::string &s) { - return str2tag_core(s.data(), s.size(), 0); + // Iterative form of str2tag_core: the recursive constexpr version is kept + // for compile-time UDL evaluation of short string literals, but at runtime + // we may receive arbitrarily long inputs (e.g. fuzzed Content-Type) that + // would blow the stack with one frame per character. + unsigned int h = 0; + for (auto c : s) { + h = (((std::numeric_limits::max)() >> 6) & h * 33) ^ + static_cast(c); + } + return h; } namespace udl { diff --git a/test/fuzzing/corpus/clusterfuzz-testcase-minimized-header_parser_fuzzer-4645558454386688 b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-header_parser_fuzzer-4645558454386688 new file mode 100644 index 0000000..7a970af Binary files /dev/null and b/test/fuzzing/corpus/clusterfuzz-testcase-minimized-header_parser_fuzzer-4645558454386688 differ diff --git a/test/test.cc b/test/test.cc index f90a024..84123de 100644 --- a/test/test.cc +++ b/test/test.cc @@ -767,6 +767,13 @@ TEST(ParseAcceptHeaderTest, InvalidCases) { EXPECT_EQ(result[0], "text/*"); } +// Regression test for OSS-Fuzz #508087118: a long Content-Type ran str2tag +// recursively (one stack frame per character) and overflowed the stack. +TEST(Str2tagTest, LongInputDoesNotOverflowStack) { + std::string long_content_type(60000, 'x'); + EXPECT_NO_THROW(detail::can_compress_content_type(long_content_type)); +} + TEST(ParseAcceptHeaderTest, ContentTypesPopulatedAndInvalidHeaderHandling) { Server svr;