From 80e9deb4d87a9588ab398adcec7f6acbff9da8f4 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Thu, 5 Mar 2026 13:37:17 +0000 Subject: [PATCH] restrict bitwise operations argument range to the safe-integer range Fixes #858. This is intended to match the restriction added in C++ Jsonnet in https://github.com/google/jsonnet/pull/1217 and updated in https://github.com/google/jsonnet/pull/1240 --- builtins.go | 18 ++++++++++++++---- testdata/bitwise_and3.golden | 2 +- testdata/bitwise_and7.golden | 2 +- testdata/bitwise_or9.golden | 11 ++++++++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/builtins.go b/builtins.go index a4855fea8..f64788fc1 100644 --- a/builtins.go +++ b/builtins.go @@ -1194,6 +1194,16 @@ var builtinIsDecimal = liftNumericToBoolean(func(f float64) bool { return frac != 0 }) +// IEEE-754 double precision floats can safely store integers in the range [-2**53+1, 2**53-1]. +// We restrict bitwise operations to arguments in this range, since operating on larger values is +// likely to be a mistake. +// https://jsonnet.org/ref/language.html#number +// See also Javascript Number.{MIN_SAFE_INTEGER,MAX_SAFE_INTEGER} +const ( + maxSafeIntValue float64 = (1 << 53) - 1 + minSafeIntValue float64 = -maxSafeIntValue +) + func liftBitwise(f func(int64, int64) int64, positiveRightArg bool) func(*interpreter, value, value) (value, error) { return func(i *interpreter, xv, yv value) (value, error) { x, err := i.getNumber(xv) @@ -1204,12 +1214,12 @@ func liftBitwise(f func(int64, int64) int64, positiveRightArg bool) func(*interp if err != nil { return nil, err } - if x.value < math.MinInt64 || x.value > math.MaxInt64 { - msg := fmt.Sprintf("Bitwise operator argument %v outside of range [%v, %v]", x.value, int64(math.MinInt64), int64(math.MaxInt64)) + if x.value < minSafeIntValue || x.value > maxSafeIntValue { + msg := fmt.Sprintf("Bitwise operator argument %v outside of range [%v, %v]", x.value, int64(minSafeIntValue), int64(maxSafeIntValue)) return nil, makeRuntimeError(msg, i.getCurrentStackTrace()) } - if y.value < math.MinInt64 || y.value > math.MaxInt64 { - msg := fmt.Sprintf("Bitwise operator argument %v outside of range [%v, %v]", y.value, int64(math.MinInt64), int64(math.MaxInt64)) + if y.value < minSafeIntValue || y.value > maxSafeIntValue { + msg := fmt.Sprintf("Bitwise operator argument %v outside of range [%v, %v]", y.value, int64(minSafeIntValue), int64(maxSafeIntValue)) return nil, makeRuntimeError(msg, i.getCurrentStackTrace()) } if positiveRightArg && y.value < 0 { diff --git a/testdata/bitwise_and3.golden b/testdata/bitwise_and3.golden index c71c52121..626589372 100644 --- a/testdata/bitwise_and3.golden +++ b/testdata/bitwise_and3.golden @@ -1,4 +1,4 @@ -RUNTIME ERROR: Bitwise operator argument 1e+30 outside of range [-9223372036854775808, 9223372036854775807] +RUNTIME ERROR: Bitwise operator argument 1e+30 outside of range [-9007199254740991, 9007199254740991] ------------------------------------------------- testdata/bitwise_and3:1:1-10 $ diff --git a/testdata/bitwise_and7.golden b/testdata/bitwise_and7.golden index 788ba2832..7a77fb363 100644 --- a/testdata/bitwise_and7.golden +++ b/testdata/bitwise_and7.golden @@ -1,4 +1,4 @@ -RUNTIME ERROR: Bitwise operator argument -1e+20 outside of range [-9223372036854775808, 9223372036854775807] +RUNTIME ERROR: Bitwise operator argument -1e+20 outside of range [-9007199254740991, 9007199254740991] ------------------------------------------------- testdata/bitwise_and7:1:1-11 $ diff --git a/testdata/bitwise_or9.golden b/testdata/bitwise_or9.golden index aef5454e6..3bcdce0e7 100644 --- a/testdata/bitwise_or9.golden +++ b/testdata/bitwise_or9.golden @@ -1 +1,10 @@ -4611686018427387904 +RUNTIME ERROR: Bitwise operator argument 4.611686018427388e+18 outside of range [-9007199254740991, 9007199254740991] +------------------------------------------------- + testdata/bitwise_or9:1:1-14 $ + +(1 << 62) | 1 + +------------------------------------------------- + During evaluation + +