You've already forked cpp-httplib
Merge commit from fork
* Fix Persistency of Unbounded Memory Allocation in Chunked/No-Length Requests Vulnerability * Revert HTTP status code from 413 to 400
This commit is contained in:
94
httplib.h
94
httplib.h
@ -4642,52 +4642,79 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
inline bool read_content_without_length(Stream &strm,
|
||||
ContentReceiverWithProgress out) {
|
||||
enum class ReadContentResult {
|
||||
Success, // Successfully read the content
|
||||
PayloadTooLarge, // The content exceeds the specified payload limit
|
||||
Error // An error occurred while reading the content
|
||||
};
|
||||
|
||||
inline ReadContentResult
|
||||
read_content_without_length(Stream &strm, size_t payload_max_length,
|
||||
ContentReceiverWithProgress out) {
|
||||
char buf[CPPHTTPLIB_RECV_BUFSIZ];
|
||||
uint64_t r = 0;
|
||||
for (;;) {
|
||||
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
|
||||
if (n == 0) { return true; }
|
||||
if (n < 0) { return false; }
|
||||
if (n == 0) { return ReadContentResult::Success; }
|
||||
if (n < 0) { return ReadContentResult::Error; }
|
||||
|
||||
if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
|
||||
// Check if adding this data would exceed the payload limit
|
||||
if (r > payload_max_length ||
|
||||
payload_max_length - r < static_cast<uint64_t>(n)) {
|
||||
return ReadContentResult::PayloadTooLarge;
|
||||
}
|
||||
|
||||
if (!out(buf, static_cast<size_t>(n), r, 0)) {
|
||||
return ReadContentResult::Error;
|
||||
}
|
||||
r += static_cast<uint64_t>(n);
|
||||
}
|
||||
|
||||
return true;
|
||||
return ReadContentResult::Success;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool read_content_chunked(Stream &strm, T &x,
|
||||
ContentReceiverWithProgress out) {
|
||||
inline ReadContentResult read_content_chunked(Stream &strm, T &x,
|
||||
size_t payload_max_length,
|
||||
ContentReceiverWithProgress out) {
|
||||
const auto bufsiz = 16;
|
||||
char buf[bufsiz];
|
||||
|
||||
stream_line_reader line_reader(strm, buf, bufsiz);
|
||||
|
||||
if (!line_reader.getline()) { return false; }
|
||||
if (!line_reader.getline()) { return ReadContentResult::Error; }
|
||||
|
||||
unsigned long chunk_len;
|
||||
uint64_t total_len = 0;
|
||||
while (true) {
|
||||
char *end_ptr;
|
||||
|
||||
chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
|
||||
|
||||
if (end_ptr == line_reader.ptr()) { return false; }
|
||||
if (chunk_len == ULONG_MAX) { return false; }
|
||||
if (end_ptr == line_reader.ptr()) { return ReadContentResult::Error; }
|
||||
if (chunk_len == ULONG_MAX) { return ReadContentResult::Error; }
|
||||
|
||||
if (chunk_len == 0) { break; }
|
||||
|
||||
if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
|
||||
return false;
|
||||
// Check if adding this chunk would exceed the payload limit
|
||||
if (total_len > payload_max_length ||
|
||||
payload_max_length - total_len < chunk_len) {
|
||||
return ReadContentResult::PayloadTooLarge;
|
||||
}
|
||||
|
||||
if (!line_reader.getline()) { return false; }
|
||||
total_len += chunk_len;
|
||||
|
||||
if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
|
||||
if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
|
||||
return ReadContentResult::Error;
|
||||
}
|
||||
|
||||
if (!line_reader.getline()) { return false; }
|
||||
if (!line_reader.getline()) { return ReadContentResult::Error; }
|
||||
|
||||
if (strcmp(line_reader.ptr(), "\r\n") != 0) {
|
||||
return ReadContentResult::Error;
|
||||
}
|
||||
|
||||
if (!line_reader.getline()) { return ReadContentResult::Error; }
|
||||
}
|
||||
|
||||
assert(chunk_len == 0);
|
||||
@ -4704,14 +4731,18 @@ inline bool read_content_chunked(Stream &strm, T &x,
|
||||
//
|
||||
// According to the reference code in RFC 9112, cpp-httplib now allows
|
||||
// chunked transfer coding data without the final CRLF.
|
||||
if (!line_reader.getline()) { return true; }
|
||||
if (!line_reader.getline()) { return ReadContentResult::Success; }
|
||||
|
||||
size_t trailer_header_count = 0;
|
||||
while (strcmp(line_reader.ptr(), "\r\n") != 0) {
|
||||
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
|
||||
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
|
||||
return ReadContentResult::Error;
|
||||
}
|
||||
|
||||
// Check trailer header count limit
|
||||
if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
|
||||
if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {
|
||||
return ReadContentResult::Error;
|
||||
}
|
||||
|
||||
// Exclude line terminator
|
||||
constexpr auto line_terminator_len = 2;
|
||||
@ -4724,10 +4755,10 @@ inline bool read_content_chunked(Stream &strm, T &x,
|
||||
|
||||
trailer_header_count++;
|
||||
|
||||
if (!line_reader.getline()) { return false; }
|
||||
if (!line_reader.getline()) { return ReadContentResult::Error; }
|
||||
}
|
||||
|
||||
return true;
|
||||
return ReadContentResult::Success;
|
||||
}
|
||||
|
||||
inline bool is_chunked_transfer_encoding(const Headers &headers) {
|
||||
@ -4801,9 +4832,26 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
||||
auto exceed_payload_max_length = false;
|
||||
|
||||
if (is_chunked_transfer_encoding(x.headers)) {
|
||||
ret = read_content_chunked(strm, x, out);
|
||||
auto result = read_content_chunked(strm, x, payload_max_length, out);
|
||||
if (result == ReadContentResult::Success) {
|
||||
ret = true;
|
||||
} else if (result == ReadContentResult::PayloadTooLarge) {
|
||||
exceed_payload_max_length = true;
|
||||
ret = false;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
} else if (!has_header(x.headers, "Content-Length")) {
|
||||
ret = read_content_without_length(strm, out);
|
||||
auto result =
|
||||
read_content_without_length(strm, payload_max_length, out);
|
||||
if (result == ReadContentResult::Success) {
|
||||
ret = true;
|
||||
} else if (result == ReadContentResult::PayloadTooLarge) {
|
||||
exceed_payload_max_length = true;
|
||||
ret = false;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
auto is_invalid_value = false;
|
||||
auto len = get_header_value_u64(
|
||||
|
Reference in New Issue
Block a user