@@ -2506,6 +2506,10 @@ void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
25062506 }
25072507}
25082508
2509+ // Recursive form retained so operator""_t below can compute hashes for
2510+ // switch-case labels at compile time (C++11 constexpr forbids loops). Do not
2511+ // call from runtime paths with arbitrary-length inputs — use str2tag()
2512+ // instead, which is iterative and stack-safe.
25092513constexpr unsigned int str2tag_core(const char *s, size_t l,
25102514 unsigned int h) {
25112515 return (l == 0)
@@ -2519,7 +2523,16 @@ constexpr unsigned int str2tag_core(const char *s, size_t l,
25192523}
25202524
25212525unsigned int str2tag(const std::string &s) {
2522- return str2tag_core(s.data(), s.size(), 0);
2526+ // Iterative form of str2tag_core: the recursive constexpr version is kept
2527+ // for compile-time UDL evaluation of short string literals, but at runtime
2528+ // we may receive arbitrarily long inputs (e.g. fuzzed Content-Type) that
2529+ // would blow the stack with one frame per character.
2530+ unsigned int h = 0;
2531+ for (auto c : s) {
2532+ h = (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^
2533+ static_cast<unsigned char>(c);
2534+ }
2535+ return h;
25232536}
25242537
25252538namespace udl {
@@ -9777,7 +9790,15 @@ bool ClientImpl::process_request(Stream &strm, Request &req,
97779790 output_error_log(error, &req);
97789791 return false;
97799792 }
9780- res.body.reserve(static_cast<size_t>(len));
9793+ // Cap the reservation by payload_max_length_ to avoid OOM when a
9794+ // hostile or malformed server sends an enormous Content-Length.
9795+ // The actual body read below is bounded by payload_max_length_,
9796+ // so reserving more than that is never useful.
9797+ auto reserve_len = static_cast<size_t>(len);
9798+ if (payload_max_length_ > 0 && reserve_len > payload_max_length_) {
9799+ reserve_len = payload_max_length_;
9800+ }
9801+ res.body.reserve(reserve_len);
97819802 }
97829803 }
97839804
0 commit comments