Skip to content

Commit bf7700d

Browse files
authored
Fix exception that occurs with libc++ regex engine (yhirose#368)
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.
1 parent 3da925d commit bf7700d

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

httplib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
17641764
// the left or right side of the header value:
17651765
// - https://stackoverflow.com/questions/50179659/
17661766
// - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
1767-
static const std::regex re(R"((.+?):[\t ]*(.+))");
1767+
static const std::regex re(R"(([^:]+):[\t ]*(.+))");
17681768

17691769
std::cmatch m;
17701770
if (std::regex_match(line_reader.ptr(), end, m, re)) {

test/test.cc

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,7 +2049,8 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
20492049
EXPECT_EQ(header_value, "\v bar \e");
20502050
}
20512051

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

2070+
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
2071+
svr.stop();
2072+
t.join();
2073+
EXPECT_TRUE(listen_thread_ok);
2074+
}
2075+
2076+
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
20692077
// A certain header line causes an exception if the header property is parsed
20702078
// naively with a single regex. This occurs with libc++ but not libstdc++.
2071-
const std::string req =
2079+
test_raw_request(
20722080
"GET /hi HTTP/1.1\r\n"
20732081
" : "
2074-
" ";
2082+
" "
2083+
);
2084+
}
20752085

2076-
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
2077-
svr.stop();
2078-
t.join();
2079-
EXPECT_TRUE(listen_thread_ok);
2086+
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
2087+
// A certain header line causes an exception if the header property *name* is
2088+
// parsed with a regular expression starting with "(.+?):" - this is a non-
2089+
// greedy matcher and requires backtracking when there are a lot of ":"
2090+
// characters.
2091+
// This occurs with libc++ but not libstdc++.
2092+
test_raw_request(
2093+
"GET /hi HTTP/1.1\r\n"
2094+
":-:::::::::::::::::::::::::::-::::::::::::::::::::::::@-&&&&&&&&&&&"
2095+
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
2096+
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-:::::"
2097+
"::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::::::::::::::::::::"
2098+
":::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::"
2099+
"::::::::-:::::::::::::::::@-&&&&&&&--:::::::-::::::::::::::::::::::"
2100+
":::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::"
2101+
"::::::::::-:::::::::::::::::@-&&&&&::::::::::::-:::::::::::::::::@-"
2102+
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
2103+
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
2104+
"::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::@-&&"
2105+
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
2106+
"::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&"
2107+
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
2108+
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&"
2109+
"&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&"
2110+
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
2111+
"-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::"
2112+
"::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::"
2113+
":::::@-&&&&&&&&&&&::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::"
2114+
":::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::"
2115+
"::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-"
2116+
":::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&---&&:&"
2117+
"&&.0------------:-:::::::::::::::::::::::::::::-:::::::::::::::::@-"
2118+
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
2119+
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
2120+
"::::@-&&&&&&&&&&&---&&:&&&.0------------O--------\rH PUTHTTP/1.1\r\n"
2121+
"&&&%%%");
20802122
}
20812123

20822124
TEST(ServerStopTest, StopServerWithChunkedTransmission) {

0 commit comments

Comments
 (0)