mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-24 12:04:40 +00:00
Reject a Range first-byte-pos that overflows ssize_t (#2580)
* Reject a Range first-byte-pos that overflows ssize_t parse_range_header initializes first to the -1 sentinel that means "no first-byte-pos" and only overwrites it when detail::from_chars succeeds. On std::errc::result_out_of_range the assignment is skipped and -1 survives, so "bytes=9223372036854775808-100" is parsed as the suffix range "bytes=-100" and range_error serves the last 100 bytes instead of returning 416. Before the parser was rewritten onto detail::from_chars, std::stoll threw std::out_of_range on the same input, the catch arm added in 8f8761e for issue #705 returned false, and the request was answered with 416. The catch arm is still there but from_chars reports through an error code, so nothing reaches it any more. get_header_value_u64 and parse_port already reject an out-of-range value at their from_chars call sites; this was the remaining one that dropped the error. The last-byte-pos side is deliberately unchanged: -1 there is the documented RFC 9110 14.1.2 "remainder of the representation" value, so an oversized last-byte-pos stays accepted. * Simplify the Range first-byte-pos overflow check Parse the first-byte-pos straight into first, since a failed parse now returns before first is read, and fold the overflow test into the existing batch of rejected ranges. Also note on the last-byte-pos side why an overflow there deliberately keeps -1. Claude-Session: https://claude.ai/code/session_01JYPWKpbp4a881EdpEf2xSi --------- Co-authored-by: yhirose <yuji.hirose.bug@gmail.com>
This commit is contained in:
1 parent
515b8f84af
commit
8d25b6a3ac
2 files changed
+25
-3
No files matched your search
@@ -9049,13 +9049,20 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
|
|||||||
|
|
||||||
ssize_t first = -1;
|
ssize_t first = -1;
|
||||||
if (!lhs.empty()) {
|
if (!lhs.empty()) {
|
||||||
ssize_t v;
|
// Reject an overflowing first-byte-pos; treating it as absent (-1)
|
||||||
auto res = detail::from_chars(lhs.data(), lhs.data() + lhs.size(), v);
|
// would turn the range into a suffix range.
|
||||||
if (res.ec == std::errc{}) { first = v; }
|
auto res =
|
||||||
|
detail::from_chars(lhs.data(), lhs.data() + lhs.size(), first);
|
||||||
|
if (res.ec != std::errc{}) {
|
||||||
|
all_valid_ranges = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t last = -1;
|
ssize_t last = -1;
|
||||||
if (!rhs.empty()) {
|
if (!rhs.empty()) {
|
||||||
|
// An overflowing last-byte-pos is past any content length, so keeping
|
||||||
|
// -1 ("remainder", RFC 9110 14.1.2) is correct here.
|
||||||
ssize_t v;
|
ssize_t v;
|
||||||
auto res = detail::from_chars(rhs.data(), rhs.data() + rhs.size(), v);
|
auto res = detail::from_chars(rhs.data(), rhs.data() + rhs.size(), v);
|
||||||
if (res.ec == std::errc{}) { last = v; }
|
if (res.ec == std::errc{}) { last = v; }
|
||||||
|
|||||||
@@ -2010,8 +2010,23 @@ TEST(ParseHeaderValueTest, Range) {
|
|||||||
EXPECT_FALSE(detail::parse_range_header("bytes=0--1", ranges));
|
EXPECT_FALSE(detail::parse_range_header("bytes=0--1", ranges));
|
||||||
EXPECT_FALSE(detail::parse_range_header("bytes=0- 1", ranges));
|
EXPECT_FALSE(detail::parse_range_header("bytes=0- 1", ranges));
|
||||||
EXPECT_FALSE(detail::parse_range_header("bytes=0 -1", ranges));
|
EXPECT_FALSE(detail::parse_range_header("bytes=0 -1", ranges));
|
||||||
|
// Overflows ssize_t; must not be read as the suffix range "bytes=-100".
|
||||||
|
EXPECT_FALSE(
|
||||||
|
detail::parse_range_header("bytes=9223372036854775808-100", ranges));
|
||||||
EXPECT_TRUE(ranges.empty());
|
EXPECT_TRUE(ranges.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// RFC 9110 14.1.2: a last-byte-pos greater than the content length is the
|
||||||
|
// remainder of the representation, so it stays accepted.
|
||||||
|
Ranges ranges;
|
||||||
|
auto ret =
|
||||||
|
detail::parse_range_header("bytes=0-99999999999999999999", ranges);
|
||||||
|
EXPECT_TRUE(ret);
|
||||||
|
ASSERT_EQ(1u, ranges.size());
|
||||||
|
EXPECT_EQ(0, ranges[0].first);
|
||||||
|
EXPECT_EQ(-1, ranges[0].second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseAcceptEncoding1, AcceptEncoding) {
|
TEST(ParseAcceptEncoding1, AcceptEncoding) {
|
||||||
|
|||||||
Reference in new issue
Block a user