Skip to content

Conversation

@intrigus-lgtm
Copy link
Contributor

@intrigus-lgtm intrigus-lgtm commented Dec 18, 2025

Header values in picohttp are not null-terminated so strstr would happily search outside the header value and exceed the length of the header value.
This could lead to DoS in artifical circumstances which don't apply to fd in practice. There is always a null byte in memory somewhere after the header value even if it is not actually part of the header value. This is because fd_http_server_ws_frames are allocated after the request buffer that contains the headers and the ws_frames contain four bytes of padding that is zero, because the whole memory we're operating on, has been allocated with mmap anonymously which zeroes the memory QED.

Drive-by fix: the Upgrade header field's token has to contain the value "websocket", but should be treated as an ASCII case-insensitive value: https://datatracker.ietf.org/doc/html/rfc6455#section-4.2.1

@github-actions
Copy link

Performance Measurements ⏳

Suite Baseline New Change
backtest mainnet-368528500-perf per slot 0.050625 s 0.050644 s 0.038%
backtest mainnet-368528500-perf snapshot load 1.63 s 1.618 s -0.736%
backtest mainnet-368528500-perf total elapsed 50.62463 s 50.644245 s 0.039%
firedancer mem usage with mainnet.toml 1005.23 GiB 1005.23 GiB 0.000%

for( ulong i=0UL; i<num_headers; i++ ) {
if( FD_LIKELY( headers[ i ].name_len==22UL && !strncasecmp( headers[ i ].name, "Sec-WebSocket-Protocol", 22UL ) && strstr( headers[ i ].value, "compress-zstd" ) ) ) {
// Use `memmem` as the header is not guaranteed to be null terminated
// `memmem` also matches "randomprefix_compress-zstd_randomsuffix" but that's fine, we don't want to fully parse the header value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to just check value_len == 13 and use strncasecmp here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that there might be cases where there are multiple protocols specified in the header, but the existing code and documentation quite explicitly say that this should just be a single value.
Changed to use value_len == 13 and strncmp, because the header field value is usually case-sensitive.

Copy link
Contributor

@mmcgee-jump mmcgee-jump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Header values in picohttp are not null-terminated so `strstr` would
happily search outside the header value and exceed the length of the
header value.
This could lead to DoS in artifical circumstances which don't apply to
fd in practice. There is always a null byte in memory _somewhere_ after
the header value even if it is not actually part of the header value.
This is because `fd_http_server_ws_frame`s are allocated after the
request buffer that contains the headers and the ws_frames contain
four bytes of padding that is zero, because the whole memory we're
operating on, has been allocated with `mmap` anonymously which
zeroes the memory QED.
@intrigus-lgtm intrigus-lgtm force-pushed the intrigus/fix/http-server-ws-strstr-oob branch from 05a7711 to b06639f Compare December 18, 2025 23:31
@github-actions
Copy link

Performance Measurements ⏳

Suite Baseline New Change
backtest mainnet-368528500-perf per slot 0.050523 s 0.050555 s 0.063%
backtest mainnet-368528500-perf snapshot load 1.642 s 1.638 s -0.244%
backtest mainnet-368528500-perf total elapsed 50.523247 s 50.554749 s 0.062%
firedancer mem usage with mainnet.toml 1005.23 GiB 1005.23 GiB 0.000%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants