Conversation
httpHeaderParseOffset() accepts exactly 9223372036854775807 for the last-byte-pos, and HttpHdrRangeSpec::parseInit() then computes the exclusive range end as last_pos + 1, which is signed overflow when last_pos is INT64_MAX. Under UBSan the worker aborts on a single Range: bytes=0-9223372036854775807 request header from any client. Reject last_pos == INT64_MAX before the addition instead; no real object can be that large. Same fix in httpHdrRangeRespSpecParseInit() for the Content-Range response header.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Thank you for improving this code.
Since this is your first contribution to Squid (according to GitHub), please add your record to the CONTRIBUTORS file to pass our automated CI checks (or ask for manual help to bypass that check).
N.B. There is no need to squash commits or rebase your PR branch as you address change requests or resolve merge conflicts. Just push new commits as needed. They will be automatically squashed when staging and merging your PR.
| // size_diff() below computes last_pos + 1, which overflows when last_pos | ||
| // is INT64_MAX. No real object can be that large, so reject the spec. |
There was a problem hiding this comment.
Let's avoid unnecessary and arguably incorrect claims about "real objects":
| // size_diff() below computes last_pos + 1, which overflows when last_pos | |
| // is INT64_MAX. No real object can be that large, so reject the spec. | |
| // prevent `last_pos + 1` overflows |
Please adjust PR description to remove similar claims about offsets "reality". Just focus on overflow prevention.
| // size_diff() below computes last_pos + 1, which overflows when last_pos | ||
| // is INT64_MAX. No real object can be that large, so reject the spec. | ||
| if (last_pos == INT64_MAX) { | ||
| debugs(68, 2, "invalid (last-byte-pos too large) resp-range-spec near: '" << field << "'"); |
There was a problem hiding this comment.
Since the value is syntactically valid and can even be parsed by Squid, let's adjust this diagnostics:
| debugs(68, 2, "invalid (last-byte-pos too large) resp-range-spec near: '" << field << "'"); | |
| debugs(68, 2, "unsupported (last-byte-pos too large) resp-range-spec near: '" << field << "'"); |
|
|
||
| // size_diff() below computes last_pos + 1, which overflows when last_pos | ||
| // is INT64_MAX. No real object can be that large, so reject the spec. | ||
| if (last_pos == INT64_MAX) { |
There was a problem hiding this comment.
Please avoid type duplication (and conditionally present constants). An expression similar to the suggestion below should work. If we find ourselves doing this often, we will declare an AtMax() or similar wrapper.
| if (last_pos == INT64_MAX) { | |
| if (last_pos == std::numeric_limits<decltype(last_pos)>::max()) { |
You may need to add #include <limits>. Do it after #include "HttpHeaderTools.h", leaving an empty line between them.
| // The range end below is exclusive, computed as last_pos + 1. | ||
| // That addition overflows when last_pos is INT64_MAX, which no | ||
| // real object can reach, so reject instead of overflowing. | ||
| if (last_pos == INT64_MAX) { |
There was a problem hiding this comment.
Please apply my src/HttpHdrContRange.cc change requests to this file as well.
|
FYI, reduced the title length even further. It would hit these same Anubis complain on backport when that long. |
…claims - Use std::numeric_limits<decltype(last_pos)>::max() instead of INT64_MAX (needs <limits>, added after HttpHeaderTools.h in both TUs). - Report the rejected spec as 'unsupported' rather than 'invalid': the value is syntactically valid and parseable. - Drop claims about real-world object sizes from comments.
|
Thanks for the review — all four points are addressed in the new commit:
One thing I could not do: I cannot find a CREDENTIALS file anywhere in the tree (checked the repo root, |
Yes, please add yourself to |
Per maintainer request on PR squid-cache#2512.
|
Done — added myself to CONTRIBUTORS in bd63c7e. Thanks for clarifying! |
Just to clarify: Anubis does not differentiate backports from original work: If Anubis is happy with the original PR title, it will accept that same title in the backport. Anubis does reject excessively long PR tittles, and we have started to see those rejections in backported PRs a few months(?) ago. AFAICT, what we observe is the effect of backporting software changing the original PR title (by adding a suffix with the original PR number). Those extra (and, IMO, unwanted) characters may indeed violate the title character limit, triggering a rejection (among other bad side effects!). The correct solution here is to fix that backporting software rather than to make the original PR titles shorter than they need to be. |
rousskov
left a comment
There was a problem hiding this comment.
I adjusted PR title to emphasize that we are rejecting these Range headers. Perhaps we should say "Range and Content-Range" instead of just "Range" because we are rejecting both headers AFAICT?
| Adam Ciarcinski | ||
| Adam Majer <amajer@suse.de> | ||
| Adrian Chadd <adrian@squid-cache.org> | ||
| Adrian Martinez <107548841+kalt2212@users.noreply.github.com> |
There was a problem hiding this comment.
Please keep the human-friendly name but use one of the two emails that our CI sees for this PR. You can see those email inside the expanded log of the failed GitHub Action.
|
Agreed — both parsers reject these now (HttpHdrRange and HttpHdrContRange), so "Range and Content-Range" is accurate. I updated the title accordingly, keeping it short. |
I hit this while fuzzing header parsing with UBSan on current
master.
Sending
Range: bytes=0-9223372036854775807makesHttpHdrRangeSpec::parseInit()computelast_pos + 1withlast_pos == INT64_MAX. That is signed overflow. UBSan aborts theworker on it, so any client can crash the process with one request
header. In a normal build it wraps quietly and the range handling
silently degenerates.
The parser (
httpHeaderParseOffset) accepts exactly INT64_MAX andrejects anything bigger, so INT64_MAX is the one value that reaches
the addition and breaks it.
I verified it by compiling the real translation units with
-fsanitize=address,undefined -fno-sanitize-recover=alland callingHttpHdrRange::ParseCreate("bytes=0-9223372036854775807")directly:The same pattern exists in
httpHdrRangeRespSpecParseInit()for theContent-Range response header (
HttpHdrContRange.cc), so I fixedboth. The fix just rejects
last_pos == INT64_MAXbefore the+ 1;to prevent the overflow, and
bytes=0-9223372036854775806still parses fine afterwards.