Fix exception that occurs with libc++ regex engine (#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
https://github.com/yhirose/cpp-httplib/pull/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.
This commit is contained in:
Matthew DeVore 2020-02-28 03:31:39 -08:00 committed by GitHub
parent 3da925d6fe
commit bf7700d192
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 9 deletions

View File

@ -1764,7 +1764,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
// the left or right side of the header value: // the left or right side of the header value:
// - https://stackoverflow.com/questions/50179659/ // - https://stackoverflow.com/questions/50179659/
// - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html // - 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; std::cmatch m;
if (std::regex_match(line_reader.ptr(), end, m, re)) { if (std::regex_match(line_reader.ptr(), end, m, re)) {

View File

@ -2049,7 +2049,8 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
EXPECT_EQ(header_value, "\v bar \e"); 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; Server svr;
svr.Get("/hi", [&](const Request & /*req*/, Response &res) { svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain"); res.set_content("ok", "text/plain");
@ -2066,19 +2067,60 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
// 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 =
"GET /hi HTTP/1.1\r\n"
" : "
" ";
ASSERT_TRUE(send_request(client_read_timeout_sec, req)); ASSERT_TRUE(send_request(client_read_timeout_sec, req));
svr.stop(); svr.stop();
t.join(); t.join();
EXPECT_TRUE(listen_thread_ok); 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++.
test_raw_request(
"GET /hi HTTP/1.1\r\n"
" : "
" "
);
}
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) { TEST(ServerStopTest, StopServerWithChunkedTransmission) {
Server svr; Server svr;