mirror of
https://github.com/prometheus/prometheus.git
synced 2026-05-13 00:28:42 +02:00
Merge pull request #18639 from alexmchughdev/promql-duration-overflow-nan
promql: reject NaN/Inf and fix overflow bound in duration expressions
This commit is contained in:
commit
6299449664
@ -87,10 +87,21 @@ func (v *durationVisitor) calculateDuration(expr parser.Expr, allowedNegative bo
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Reject NaN and infinities up front. NaN compares false against everything,
|
||||
// so without this guard a NaN duration would slip past the bounds check
|
||||
// below and produce an implementation-defined int64 in the time.Duration
|
||||
// conversion at the end of this function.
|
||||
if math.IsNaN(duration) || math.IsInf(duration, 0) {
|
||||
return 0, fmt.Errorf("%d:%d: duration is NaN or infinite", expr.PositionRange().Start, expr.PositionRange().End)
|
||||
}
|
||||
if duration <= 0 && !allowedNegative {
|
||||
return 0, fmt.Errorf("%d:%d: duration must be greater than 0", expr.PositionRange().Start, expr.PositionRange().End)
|
||||
}
|
||||
if duration > 1<<63-1 || duration < -1<<63 {
|
||||
// duration is in seconds; the conversion below produces nanoseconds in an
|
||||
// int64, so the safe input range is +/- math.MaxInt64 / 1e9 seconds. Match
|
||||
// the bound used by the parser for duration literals (see
|
||||
// offset_duration_expr in generated_parser.y).
|
||||
if duration > 1<<63/1e9 || duration < -(1<<63)/1e9 {
|
||||
return 0, fmt.Errorf("%d:%d: duration is out of range", expr.PositionRange().Start, expr.PositionRange().End)
|
||||
}
|
||||
return time.Duration(duration*1000) * time.Millisecond, nil
|
||||
|
||||
@ -266,6 +266,37 @@ func TestCalculateDuration(t *testing.T) {
|
||||
},
|
||||
errorMessage: "modulo by zero",
|
||||
},
|
||||
{
|
||||
name: "NaN from negative base raised to fractional power",
|
||||
expr: &parser.DurationExpr{
|
||||
LHS: &parser.NumberLiteral{Val: -1},
|
||||
RHS: &parser.NumberLiteral{Val: 0.5},
|
||||
Op: parser.POW,
|
||||
},
|
||||
errorMessage: "duration is NaN or infinite",
|
||||
allowedNegative: true,
|
||||
},
|
||||
{
|
||||
name: "infinity from huge power",
|
||||
expr: &parser.DurationExpr{
|
||||
LHS: &parser.NumberLiteral{Val: 2},
|
||||
RHS: &parser.NumberLiteral{Val: 1e10},
|
||||
Op: parser.POW,
|
||||
},
|
||||
errorMessage: "duration is NaN or infinite",
|
||||
},
|
||||
{
|
||||
name: "duration exceeds time.Duration range",
|
||||
expr: &parser.NumberLiteral{Val: 1e10},
|
||||
errorMessage: "duration is out of range",
|
||||
},
|
||||
{
|
||||
name: "duration just below the upper bound is accepted",
|
||||
expr: &parser.NumberLiteral{Val: 1e9},
|
||||
// 1e9 seconds is below 1<<63/1e9 seconds (~9.22e9), so it is
|
||||
// representable as a time.Duration. The exact value is preserved.
|
||||
expected: time.Duration(1e9) * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user