Conversation
parseDurationMillis guarded its float64 to int64 conversion with ts > float64(math.MaxInt64). float64(math.MaxInt64) rounds up to exactly 2^63, which int64 cannot hold, so a value landing on 2^63 passed the guard and the conversion overflowed to a negative duration. NaN passed as well, since it compares false against every bound. Three parameters go through this one parser, and two of them happen to catch the overflowed value downstream: step is rejected by result.Step <= 0 and max_source_resolution by maxSourceResolution < 0. lookback_delta has no such check, so the negative value reached the request, and because re-encoding is gated on LookbackDelta > 0 the parameter was then dropped from the request forwarded to the querier. The user got 200 with no error and their query ran with the default lookback instead. The lower bound stays exclusive on purpose: float64(math.MinInt64) is exactly -2^63 and is representable, so -9223372036854775.808 is a valid duration, and a test pins it. Signed-off-by: manon <youdie006@users.noreply.github.com>
Signed-off-by: manon <youdie006@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9011 +/- ##
==========================================
+ Coverage 64.52% 64.54% +0.01%
==========================================
Files 289 289
Lines 37366 37366
==========================================
+ Hits 24111 24117 +6
+ Misses 11157 11156 -1
+ Partials 2098 2093 -5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Changes
parseDurationMillis(pkg/queryfrontend/queryrange_codec.go:214) guards its float64 to int64 conversion withts > float64(math.MaxInt64).float64(math.MaxInt64)rounds up to exactly 2^63, which int64 cannot hold, so a value landing on 2^63 passes the guard and the conversion overflows. NaN passes too, since it compares false against every bound.Three parameters go through this one parser, and two of them happen to catch the overflowed value downstream —
stepbyresult.Step <= 0(:79-81),max_source_resolutionbymaxSourceResolution < 0(:250-252).lookback_deltahas no such check.Decoded and re-encoded through the production codec on
main:So a user who sets
lookback_deltato such a value gets HTTP 200 with no error, and the parameter is silently removed from the request forwarded to the querier — because re-encoding is gated onthanosReq.LookbackDelta > 0(:197) — so the query runs with the server default instead. The bogus value also lands in the response cache key (pkg/queryfrontend/cache.go:112). Two sibling parameters parsed by the very same function return 400 for the same input.parseLookbackDeltais shared with the instant-query codec (pkg/queryfrontend/queryinstant_codec.go:159, same> 0re-encode gate at:219), so this one change covers both paths.The lower bound deliberately stays exclusive. Unlike the upper one,
float64(math.MinInt64)is exactly -2^63 and is representable, so-9223372036854775.808is a valid duration; a test row pins that so the two bounds do not get "fixed" into symmetry.Not touched here, mentioned as follow-ups: the identical guard at
pkg/api/query/v1.go:1605(open PR #8817 is already in that file) and atinternal/cortex/querier/queryrange/query_range.go:866(vendored Cortex fork).Verification
Rows added to the existing
TestQueryRangeCodec_DecodeRequesttable.mainand pass here; the negative-bound row passes on both, guarding the asymmetry.>=weakened back to>; themath.IsNaNcheck removed; and the lower bound changed to<=.gofmtclean,go vet ./pkg/queryfrontend/clean.go test ./pkg/queryfrontend/ -run TestQueryRangeCodec_DecodeRequestpasses. The package as a whole has one failure,TestLabelsCodec_DecodeResponse, which panics with aslice bounds out of rangeinsideencoding/json; it does the same on an unmodified tree here (Go 1.26.3 locally against the repo's declared 1.26.0), so it is unrelated to this change.Go makes an out-of-range float to int conversion implementation-defined — amd64 gives
MinInt64, arm64 saturates toMaxInt64— so the added tests assert only that an error is returned.AI assistance disclosure: this patch was found and written with Claude Code. The table above is verbatim from running the codec against
main.