Skip to content

Commit

Permalink
Fix exception that occurs with libc++ regex engine (yhirose#368)
Browse files Browse the repository at this point in the history
The regex that parses header lines potentially causes an unlimited
amount of backtracking, which can cause an exception in the libc++ regex
engine.

The exception that occurs looks like this and is identical to the
message of the exception fixed in
yhirose#280:

	libc++abi.dylib: terminating with uncaught exception of type
	std::__1::regex_error: The complexity of an attempted match
	against a regular expression exceeded a pre-set level.

This commit eliminates the problematic backtracking.
  • Loading branch information
matvore authored Feb 28, 2020
1 parent 3da925d commit bf7700d
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 8 deletions.
2 changes: 1 addition & 1 deletion httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
// the left or right side of the header value:
// - https://stackoverflow.com/questions/50179659/
// - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
static const std::regex re(R"((.+?):[\t ]*(.+))");
static const std::regex re(R"(([^:]+):[\t ]*(.+))");

std::cmatch m;
if (std::regex_match(line_reader.ptr(), end, m, re)) {
Expand Down
56 changes: 49 additions & 7 deletions test/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,8 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
EXPECT_EQ(header_value, "\v bar \e");
}

TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
// Sends a raw request and verifies that there isn't a crash or exception.
static void test_raw_request(const std::string& req) {
Server svr;
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
Expand All @@ -2066,17 +2067,58 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

ASSERT_TRUE(send_request(client_read_timeout_sec, req));
svr.stop();
t.join();
EXPECT_TRUE(listen_thread_ok);
}

TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
// A certain header line causes an exception if the header property is parsed
// naively with a single regex. This occurs with libc++ but not libstdc++.
const std::string req =
test_raw_request(
"GET /hi HTTP/1.1\r\n"
" : "
" ";
" "
);
}

ASSERT_TRUE(send_request(client_read_timeout_sec, req));
svr.stop();
t.join();
EXPECT_TRUE(listen_thread_ok);
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
// A certain header line causes an exception if the header property *name* is
// parsed with a regular expression starting with "(.+?):" - this is a non-
// greedy matcher and requires backtracking when there are a lot of ":"
// characters.
// This occurs with libc++ but not libstdc++.
test_raw_request(
"GET /hi HTTP/1.1\r\n"
":-:::::::::::::::::::::::::::-::::::::::::::::::::::::@-&&&&&&&&&&&"
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-:::::"
"::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::::::::::::::::::::"
":::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::"
"::::::::-:::::::::::::::::@-&&&&&&&--:::::::-::::::::::::::::::::::"
":::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::"
"::::::::::-:::::::::::::::::@-&&&&&::::::::::::-:::::::::::::::::@-"
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
"::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::@-&&"
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
"::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&"
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&"
"&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&"
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
"-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::"
"::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::"
":::::@-&&&&&&&&&&&::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::"
":::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::"
"::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-"
":::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&---&&:&"
"&&.0------------:-:::::::::::::::::::::::::::::-:::::::::::::::::@-"
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
"::::@-&&&&&&&&&&&---&&:&&&.0------------O--------\rH PUTHTTP/1.1\r\n"
"&&&%%%");
}

TEST(ServerStopTest, StopServerWithChunkedTransmission) {
Expand Down

0 comments on commit bf7700d

Please sign in to comment.