Skip to content

Commit ba68459

Browse files
committed
Fix #2014
1 parent 343a0fc commit ba68459

File tree

2 files changed

+67
-11
lines changed

2 files changed

+67
-11
lines changed

httplib.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,18 +2012,34 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) {
20122012
callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
20132013
}
20142014

2015+
inline bool is_numeric(const std::string &str) {
2016+
return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
2017+
}
2018+
20152019
inline uint64_t get_header_value_u64(const Headers &headers,
20162020
const std::string &key, uint64_t def,
2017-
size_t id) {
2021+
size_t id, bool &is_invalid_value) {
2022+
is_invalid_value = false;
20182023
auto rng = headers.equal_range(key);
20192024
auto it = rng.first;
20202025
std::advance(it, static_cast<ssize_t>(id));
20212026
if (it != rng.second) {
2022-
return std::strtoull(it->second.data(), nullptr, 10);
2027+
if (is_numeric(it->second)) {
2028+
return std::strtoull(it->second.data(), nullptr, 10);
2029+
} else {
2030+
is_invalid_value = true;
2031+
}
20232032
}
20242033
return def;
20252034
}
20262035

2036+
inline uint64_t get_header_value_u64(const Headers &headers,
2037+
const std::string &key, uint64_t def,
2038+
size_t id) {
2039+
bool dummy = false;
2040+
return get_header_value_u64(headers, key, def, id, dummy);
2041+
}
2042+
20272043
} // namespace detail
20282044

20292045
inline uint64_t Request::get_header_value_u64(const std::string &key,
@@ -4433,8 +4449,14 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
44334449
} else if (!has_header(x.headers, "Content-Length")) {
44344450
ret = read_content_without_length(strm, out);
44354451
} else {
4436-
auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0);
4437-
if (len > payload_max_length) {
4452+
auto is_invalid_value = false;
4453+
auto len = get_header_value_u64(x.headers, "Content-Length",
4454+
std::numeric_limits<uint64_t>::max(),
4455+
0, is_invalid_value);
4456+
4457+
if (is_invalid_value) {
4458+
ret = false;
4459+
} else if (len > payload_max_length) {
44384460
exceed_payload_max_length = true;
44394461
skip_content_with_length(strm, len);
44404462
ret = false;

test/test.cc

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,15 @@ TEST(GetHeaderValueTest, RegularValueInt) {
511511
EXPECT_EQ(100ull, val);
512512
}
513513

514+
TEST(GetHeaderValueTest, RegularInvalidValueInt) {
515+
Headers headers = {{"Content-Length", "x"}};
516+
auto is_invalid_value = false;
517+
auto val = detail::get_header_value_u64(headers, "Content-Length", 0, 0,
518+
is_invalid_value);
519+
EXPECT_EQ(0ull, val);
520+
EXPECT_TRUE(is_invalid_value);
521+
}
522+
514523
TEST(GetHeaderValueTest, Range) {
515524
{
516525
Headers headers = {make_range_header({{1, -1}})};
@@ -7496,9 +7505,9 @@ TEST(MultipartFormDataTest, CloseDelimiterWithoutCRLF) {
74967505
"text2"
74977506
"\r\n------------";
74987507

7499-
std::string resonse;
7500-
ASSERT_TRUE(send_request(1, req, &resonse));
7501-
ASSERT_EQ("200", resonse.substr(9, 3));
7508+
std::string response;
7509+
ASSERT_TRUE(send_request(1, req, &response));
7510+
ASSERT_EQ("200", response.substr(9, 3));
75027511
}
75037512

75047513
TEST(MultipartFormDataTest, ContentLength) {
@@ -7543,11 +7552,10 @@ TEST(MultipartFormDataTest, ContentLength) {
75437552
"text2"
75447553
"\r\n------------\r\n";
75457554

7546-
std::string resonse;
7547-
ASSERT_TRUE(send_request(1, req, &resonse));
7548-
ASSERT_EQ("200", resonse.substr(9, 3));
7555+
std::string response;
7556+
ASSERT_TRUE(send_request(1, req, &response));
7557+
ASSERT_EQ("200", response.substr(9, 3));
75497558
}
7550-
75517559
#endif
75527560

75537561
TEST(TaskQueueTest, IncreaseAtomicInteger) {
@@ -8007,6 +8015,32 @@ TEST(InvalidHeaderCharsTest, OnServer) {
80078015
}
80088016
}
80098017

8018+
TEST(InvalidHeaderValueTest, InvalidContentLength) {
8019+
auto handled = false;
8020+
8021+
Server svr;
8022+
svr.Post("/test", [&](const Request &, Response &) { handled = true; });
8023+
8024+
thread t = thread([&] { svr.listen(HOST, PORT); });
8025+
auto se = detail::scope_exit([&] {
8026+
svr.stop();
8027+
t.join();
8028+
ASSERT_FALSE(svr.is_running());
8029+
ASSERT_FALSE(handled);
8030+
});
8031+
8032+
svr.wait_until_ready();
8033+
8034+
auto req = "POST /test HTTP/1.1\r\n"
8035+
"Content-Length: x\r\n"
8036+
"\r\n";
8037+
8038+
std::string response;
8039+
ASSERT_TRUE(send_request(1, req, &response));
8040+
ASSERT_EQ("HTTP/1.1 400 Bad Request",
8041+
response.substr(0, response.find("\r\n")));
8042+
}
8043+
80108044
#ifndef _WIN32
80118045
TEST(Expect100ContinueTest, ServerClosesConnection) {
80128046
static constexpr char reject[] = "Unauthorized";

0 commit comments

Comments
 (0)