Fix remote stack overflow and uninitialized read in Content-Type/Content-Encoding parsing - #506
Merged
Merged
Conversation
xroche
force-pushed
the
fix-header-sscanf-overflow
branch
from
July 8, 2026 05:24
b74bd54 to
a3db365
Compare
…arsing
treathead() parsed the Content-Type and Content-Encoding response-header
values with a width-less sscanf("%s") into a 1100-byte stack scratch,
bounds-checking the result only after the copy. The receive path in
back_wait hands treathead a line buffer of up to 2000 bytes (htsback.c
rcvd[2048], binput cap 2000), so a hostile server sending a header value
longer than 1099 non-whitespace bytes overflowed the stack. Bound both
scans to the buffer (%1099s), matching the existing %31s/%255s idiom in
the request-line parsers.
The Content-Encoding branch also ignored the sscanf return, so an empty
value (e.g. "Content-Encoding:") left tempo uninitialized and then ran
strlen() over it: an uninitialized read that can also over-read past the
buffer. The Content-Type branch already guarded this; mirror it.
Found via the P3-3 static-analysis baseline. New -#test=headerlong plus
empty-value cases in 01_engine-header.test build the hostile inputs in
the handler (the CLI caps argument length); they abort on the pre-fix
binary under ASan (overflow) and MSan (uninitialized read).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
xroche
force-pushed
the
fix-header-sscanf-overflow
branch
from
July 8, 2026 06:46
a3db365 to
95d0fe7
Compare
xroche
added a commit
that referenced
this pull request
Jul 8, 2026
P3-5 fuzz Tier-2. Two more harnesses over hostile-input parsers that read structured bytes into fixed buffers: fuzz-header drives treatfirstline plus treathead on each response-header line (the Content-Type/-Encoding path hardened in #506, and the cookie/Location/Content-Range fields); fuzz-cachendx drives the hts-cache/*.ndx length-prefixed scan that cache_readex_new loads on --update. fuzz-cachendx found the over-advance fixed in the parent commit; its seed corpus carries the two crash reproducers as replay regressions. The cache harness stops at the scan rather than the trailing coucal insert, whose murmur hash trips a separate pointer-overflow the .ndx parser does not own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Xavier Roche <roche@httrack.com>
xroche
added a commit
that referenced
this pull request
Jul 8, 2026
P3-5 fuzz Tier-2. Two more harnesses over hostile-input parsers that read structured bytes into fixed buffers: fuzz-header drives treatfirstline plus treathead on each response-header line (the Content-Type/-Encoding path hardened in #506, and the cookie/Location/Content-Range fields); fuzz-cachendx drives the hts-cache/*.ndx length-prefixed scan that cache_readex_new loads on --update. fuzz-cachendx found the over-advance fixed in the parent commit; its seed corpus carries the two crash reproducers as replay regressions. The cache harness stops at the scan rather than the trailing coucal insert, whose murmur hash trips a separate pointer-overflow the .ndx parser does not own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Xavier Roche <roche@httrack.com>
xroche
added a commit
that referenced
this pull request
Jul 8, 2026
…508) P3-5 fuzz Tier-2. Two more harnesses over hostile-input parsers that read structured bytes into fixed buffers: fuzz-header drives treatfirstline plus treathead on each response-header line (the Content-Type/-Encoding path hardened in #506, and the cookie/Location/Content-Range fields); fuzz-cachendx drives the hts-cache/*.ndx length-prefixed scan that cache_readex_new loads on --update. fuzz-cachendx found the over-advance fixed in the parent commit; its seed corpus carries the two crash reproducers as replay regressions. The cache harness stops at the scan rather than the trailing coucal insert, whose murmur hash trips a separate pointer-overflow the .ndx parser does not own. Signed-off-by: Xavier Roche <roche@httrack.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
xroche
added a commit
that referenced
this pull request
Jul 8, 2026
…reland) (#509) * Add libFuzzer harnesses for the HTTP header and cache-index parsers P3-5 fuzz Tier-2. Two more harnesses over hostile-input parsers that read structured bytes into fixed buffers: fuzz-header drives treatfirstline plus treathead on each response-header line (the Content-Type/-Encoding path hardened in #506, and the cookie/Location/Content-Range fields); fuzz-cachendx drives the hts-cache/*.ndx length-prefixed scan that cache_readex_new loads on --update. fuzz-cachendx found the over-advance fixed in the parent commit; its seed corpus carries the two crash reproducers as replay regressions. The cache harness stops at the scan rather than the trailing coucal insert, whose murmur hash trips a separate pointer-overflow the .ndx parser does not own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Xavier Roche <roche@httrack.com> * Correct copyright year on the new fuzz harnesses (2026) fuzz-header.c and fuzz-cachendx.c are new in the 2026 audit cycle; match the sibling *_selftest.c files rather than the boilerplate 1998. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Xavier Roche <roche@httrack.com> --------- Signed-off-by: Xavier Roche <roche@httrack.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two memory-safety bugs in the HTTP response-header parser
treathead(), both reachable from any crawl that fetches a URL from a hostile or compromised server.First, a remote stack buffer overflow:
Content-TypeandContent-Encodingvalues are read with a width-lesssscanf("%s")into an 1100-byte stack scratch buffer, with the length check happening only after the copy. Theback_waitreceive path handstreatheada line buffer of up to 2000 bytes (htsback.crcvd[2048],binputcapped at 2000), so a header value longer than 1099 non-whitespace bytes smashes the stack. Both scans are now bounded to the buffer (%1099s), matching the%31s/%255sidiom already used in the request-line parsers.Second, the
Content-Encodingbranch ignored thesscanfreturn value, so an empty value (e.g. a bareContent-Encoding:header) left the scratch buffer uninitialized and then ranstrlen()over it, an uninitialized read that can also over-read past the buffer. TheContent-Typebranch already guarded this case; this mirrors the guard.Found via the P3-3 static-analysis baseline. The new
-#test=headerlongself-test plus empty-value cases in01_engine-header.testbuild the hostile inputs inside the handler (the CLI caps argument length) and abort on the pre-fix binary under AddressSanitizer (the overflow) and MemorySanitizer (the uninitialized read); both sanitizer CI jobs cover the regression.