-
Notifications
You must be signed in to change notification settings - Fork 661
Handle even more Content-Range responses #2461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
337cf19
28b78e9
73506d0
619829e
8a293c4
e67d884
82a28b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,8 @@ | |||||
| #include "HttpHdrContRange.h" | ||||||
| #include "HttpHeaderTools.h" | ||||||
|
|
||||||
| #include <limits> | ||||||
|
|
||||||
| /* | ||||||
| * Currently only byte ranges are supported | ||||||
| * | ||||||
|
|
@@ -86,6 +88,11 @@ httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int fl | |||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| if (last_pos == std::numeric_limits<decltype(last_pos)>::max()) { | ||||||
| debugs(68, 2, "unsupported huge last-byte-pos resp-range-spec near: '" << field << "'"); | ||||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| spec->length = size_diff(last_pos + 1, spec->offset); | ||||||
|
|
||||||
| /* we managed to parse, check if the result makes sense */ | ||||||
|
|
@@ -173,9 +180,6 @@ httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) | |||||
| /* Additional paranoidal check for BUG2155 - entity-length MUST be > 0 */ | ||||||
| debugs(68, 2, "invalid (entity-length is negative) content-range-spec near: '" << str << "'"); | ||||||
| return 0; | ||||||
| } else if (known_spec(range->spec.length) && range->elength < (range->spec.offset + range->spec.length)) { | ||||||
| debugs(68, 2, "invalid (range is outside entity-length) content-range-spec near: '" << str << "'"); | ||||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| // reject unsatisfied-range and such; we only use well-defined ranges today | ||||||
|
|
@@ -184,6 +188,24 @@ httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) | |||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| // Store I/O adds partial content offsets to the size of various objects and | ||||||
| // buffers (e.g., Store metadata, serialized HTTP headers, mem_node::data, | ||||||
| // and Store I/O buffer). Most such sums do not check for overflows, so we | ||||||
|
Comment on lines
+191
to
+193
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does HTTP Range header syntax validity have to do with Squid internal Store I/O buffer management?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The proposed additional checks do not check HTTP Range header syntax validity. The proposed code checks whether the (successfully parsed) value is safe to use in the rest of Squid code. |
||||||
| // check here while assuming that those sizes cannot exceed maximumSize. We | ||||||
| // further assume that most offsets use int64_t or a larger integer type. | ||||||
| static_assert(std::numeric_limits<decltype(range->spec.offset)>::max() >= std::numeric_limits<int64_t>::max()); | ||||||
| const auto maximumSize = int64_t(1024)*1024*1024*1024; // no in-memory Squid object/buffer size can exceed 1 TiB | ||||||
| const auto maximumOffset = std::numeric_limits<int64_t>::max() - maximumSize; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Comparison against
Suggested change
Because
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notice that with the correction
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Squid does not use
In summary,
const auto maximumOffset = min(...max(), ...max()) - 1;The above suggestion does not address the problem this PR is addressing. Subtracting
... along with the fix for the problem this PR is solving.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That was my point. This PR code as written rejects cases where the object is larger than Squid can transfer in its entirety, but in chunks small enough that Squid does already handle fine. For example; science and medical datasets have Petta-byte large objects going through in GiB or TiB sized blocks.
It is not appropriate for this constant to secretly try to account for a run-time length value. That is done as part of the if-statement condition, where it should be.
I am not at any point suggesting that See https://github.com/squid-cache/squid/pull/2461/changes#r3649826148.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What makes you think that?
AFAICT, PR code does not ban Petta-byte large objects going through in GiB or TiB sized blocks. Can you give a specific example of a
This is not about "length" it is about "offset", and I see no secrets hidden in PR code. I do not know how you want the "if-statement condition" look, so I cannot commit or reject the corresponding change.
AFAICT, proposed constants already contain appropriate or "matching" values, so I cannot tell what changes you are requesting. Please be more specific. AFAICT, your definition of "maximum offset" in
The suggestions in that change request have their own problems, but if you think that the two change requests threads are about the same PR problem, then let's resolve at least one of them to save time. |
||||||
| if (range->spec.length > maximumOffset || range->spec.offset > maximumOffset - range->spec.length) { | ||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is difficult for humans to grok quickly. It is essentially a safe version of if (range->spec.offset + range->spec.length > maximumOffset)There are probably other places in code where we do this, although most are going to compare with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI the only confusing thing here is the calculation used to generate value for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sorry, I do not understand what the last two sentences in the above comment are saying or what changes this change request is requesting (if any). Please detail/rephrase if this is still relevant after the clarifications in the other/primary change request thread.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take the if-statment conditions:
So When what is needed is a check that length is small enough for Squid to process.
So: When what is needed is to ensure that [UPDATE: these checks should really be split into two if-statements with unique error messages relating to the length vs offset which is found to be too big. ]
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We should not interpret this part of the actual condition in isolation. As I said when posting this PR, this part of the condition exists simply because C++ cannot express the actual condition we want to test without overflowing (or underflowing):
The three conditions in the above three bullets are mathematically equivalent, but the first one may overflow in C++ code, and the second one might underflow. Separating the two ORed expressions in the third/proposed condition and treating each as a stand-alone check creates more problems than it solves. Again, if this math is considered difficult to grok, we can add a wrapper function, so that the high-level test becomes something like this: if (SumExceeds(spec.offset, spec.length, maximumOffset))
debugs(68, 2, "huge content-range-spec near: '" << str << "'");or, with even more out-of-scope effort, we can achieve more readable code similar to this: if (BigSum(spec.offset, spec.length) > maximumOffset)
debugs(68, 2, "huge content-range-spec near: '" << str << "'");I will implement any of the two changes sketched above if you request them. Should I?
The proposed
A
No, it does not: In the hypothetical code (not proposed in this PR) quoted above, "length" in
No, that is not what is needed to ensure. The problem this PR is solving may happen even if
True but pretty much irrelevant because we need to ban more overflows than just overflows in the
The above request is based on a false assumption that the proposed check is meant for testing offset and length individually or separately. In reality, the proposed check tests the end offset (a single entity). It is a single check for all possible byte offsets (split into two conditions to prevent C++ integer overflows and underflows). Splitting the proposed single check into two checks will create more problems. If you propose a specific split, I should be able to identify and detail those problems, but all that will take time and is very unlikely to improve Squid. I recommend approving this PR instead. |
||||||
| debugs(68, 2, "huge content-range-spec near: '" << str << "'"); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of this function is to check validity of HTTP syntax.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The purpose of
This question is based on a false premise: Huge values are indeed rejected here, but not because of their syntax (which is actually fine). The error message text follows the pattern already used in this function. If you would like to see different wording, please suggest a specific replacement.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you say the functions purpose is to parse. It is already conflated with HTTP specification validation checks of that parsed input. My question is about why a function is now being given side effects unrelated to the parse result. For example; an alternative change would be to add
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I did not say that this function purpose is [just] to parse. I said that this function purpose is to convert, which includes several (related) sub-tasks or sub-purposes, including parsing. I enumerated some of those sub-tasks. This PR does not change this function purpose(s). This PR updates this function in according to its current purpose(s).
I am not sure I agree that such conflation exists, but even if it does exist, it is outside this PR scope.
The assertion that this PR "now gives" this function something that official function code does not contain is false.
That alternative is worse than the proposed solution on several levels. For example, it relies on folks remembering to check the limits every time they needed to be checked. As this PR development itself has proven multiple times (e.g., commit 73506d0 and commit 619829e), those cases are very easy to miss even when one is specifically looking for them. While a long-term solution would be different than the proposed one, the proposed one works reliably in all known cases and is easy to backport. AFAIK, no better small-footprint solution is known at this time. |
||||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| if (known_spec(range->elength) && range->elength < (range->spec.offset + range->spec.length)) { | ||||||
| debugs(68, 2, "invalid (range is outside entity-length) content-range-spec near: '" << str << "'"); | ||||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| debugs(68, 8, "parsed content-range field: " << | ||||||
| (long int) range->spec.offset << "-" << | ||||||
| (long int) range->spec.offset + range->spec.length - 1 << " / " << | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| #include "HttpReply.h" | ||
| #include "mem_node.h" | ||
| #include "MemObject.h" | ||
| #include "SquidMath.h" | ||
| #include "stmem.h" | ||
|
|
||
| /* | ||
|
|
@@ -311,7 +312,7 @@ mem_hdr::write (StoreIOBuffer const &writeBuffer) | |
| return false; | ||
| } | ||
|
|
||
| assert (writeBuffer.offset >= 0); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assertion is repeated in the above |
||
| Assure(IncreaseSum(writeBuffer.offset, writeBuffer.length)); | ||
|
|
||
| mem_node *target; | ||
| int64_t currentOffset = writeBuffer.offset; | ||
|
|
@@ -321,6 +322,7 @@ mem_hdr::write (StoreIOBuffer const &writeBuffer) | |
| while (len && (target = nodeToRecieve(currentOffset))) { | ||
| size_t wrote = writeAvailable(target, currentOffset, len, currentSource); | ||
| assert (wrote); | ||
| Assure(len >= wrote); | ||
| len -= wrote; | ||
| currentOffset += wrote; | ||
| currentSource += wrote; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.