#3695·seastar

Hash-flooding risk in HTTP header, query, trailer, and chunk-extension maps

Author: K-ANOYCreated Sep 15, 2026Updated Sep 15, 2026

Seastar's HTTP server inserts client-controlled field names into hash maps without adding a secret hash key or enforcing field-count limits. On builds with predictable standard-library string hashing, distinct names chosen for the same bucket can drive cumulative insertion toward quadratic CPU work. Request headers and query parameters are processed before route handlers; with the default non-streaming configuration, chunk extensions and trailers are also parsed before the handler runs.

Affected code

Input Container and insertion path
Request header names _headers (include/seastar/http/request.hh:68); Ragel assign_field calls try_emplace (src/http/request_parser.rl:70–78). Case-equivalent duplicate names append to the existing value.
Decoded query names Both _query_params and deprecated query_parameters (request.hh:70,95–97) are populated by request::add_query_param (src/http/request.cc:91–106). Every accepted parameter updates both maps.
Trailer names http_chunk_trailer_parser::_headers (src/http/chunk_parsers.rl:225) is populated at 176–185, then moved into request::trailing_headers (include/seastar/http/internal/content_source.hh:219).
Chunk-extension names The parser first inserts into its per-chunk _extensions map (chunk_parsers.rl:55–56,94). The result is merged into request-wide chunk_extensions, with duplicate-name values appended (content_source.hh:146–149). Distinct extension names can accumulate across chunks.

std::hash<sstring> delegates to std::hash<std::string_view> (include/seastar/core/sstring.hh:803–807). string_view_hash uses these same hashers; the header map's case_insensitive_hash lowercases a copy before hashing it (include/seastar/util/string_utils.hh:44–68). These wrappers add no secret seed.

Collision inputs must target the actual stored keys: case-folded header names, URL-decoded query names, and the parsed trailer/extension names. They must also account for the target map's bucket growth. The two query maps perform separate hash-table updates; the current one additionally preserves repeated values in a vector.

Execution order and limits

  • Headers and query: connection::read_one() parses headers at src/http/httpd.cc:196–204. generate_reply() parses query parameters at 366 before calling _routes.handle at 371. The request grammar, query loop, and Ragel parser base contain no explicit request-target/header byte cap or field-count cap (request_parser.rl:105–123; request.cc:112–127; include/seastar/core/ragel.hh).
  • Default body handling: _content_streaming defaults to false (include/seastar/http/httpd.hh:137). set_request_content() reads the entire body before generate_reply() (httpd.cc:159–170,260–264), so chunked metadata is parsed before route handling. When streaming is enabled, parsing follows body reads; the server also performs a post-handler read to check for unread content (272–276). Streaming alone does not establish that these parsers are unreachable.
  • The content-length setting does not bound these fields: _content_length_limit defaults to size_t::max() (httpd.hh:136). Its check compares the supplied Content-Length after header parsing (httpd.cc:228–235). It does not cap request-target/header bytes or accumulated chunk metadata. The chunk decoder receives no such limit and imposes no trailer/extension count cap (content_source.hh:149–160,208–225).

All maps belong to the current request or parser, so the collision workload can be contained in one request. The hash-table operations execute synchronously in parsing callbacks; excessive work can delay other tasks on the same reactor shard. Deployment-level request limits and access controls can restrict exposure.

Suggested mitigations

  • Use a hash-flooding-resistant keyed hasher while preserving each map's existing equality semantics.
  • Enforce request-target/header byte limits and explicit counts for headers, query fields, trailers, and chunk extensions, including cumulative extensions across chunks.
  • Apply chunk metadata limits independently of decoded body length. Consider retiring the deprecated query map to avoid duplicate insertion work.