Skip to content

Commit

Permalink
Check for overflows in ipow()
Browse files Browse the repository at this point in the history
  • Loading branch information
dop251 committed Jul 6, 2023
1 parent ba8a63e commit b196ae5
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 39 deletions.
2 changes: 1 addition & 1 deletion builtin_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ NaNLoop:

func pow(x, y Value) Value {
if x, ok := x.(valueInt); ok {
if y, ok := y.(valueInt); ok && y >= 0 && y < 64 {
if y, ok := y.(valueInt); ok && y >= 0 {
if y == 0 {
return intToValue(1)
}
Expand Down
63 changes: 32 additions & 31 deletions ipow.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,39 @@
package goja

// ported from https://gist.github.com/orlp/3551590
// inspired by https://gist.github.com/orlp/3551590

var highest_bit_set = [256]byte{
var overflows = [64]int64{
9223372036854775807, 9223372036854775807, 3037000499, 2097151,
55108, 6208, 1448, 511,
234, 127, 78, 52,
38, 28, 22, 18,
15, 13, 11, 9,
8, 7, 7, 6,
6, 5, 5, 5,
4, 4, 4, 4,
3, 3, 3, 3,
3, 3, 3, 3,
2, 2, 2, 2,
2, 2, 2, 2,
2, 2, 2, 2,
2, 2, 2, 2,
2, 2, 2, 2,
2, 2, 2, 2,
}

var highestBitSet = [63]byte{
0, 1, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 255, // anything past 63 is a guaranteed overflow with base > 1
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
6, 6, 6, 6, 6, 6, 6,
}

func ipow(base, exp int64) (result int64) {
result = 1

switch highest_bit_set[byte(exp)] {
case 255: // we use 255 as an overflow marker and return 0 on overflow/underflow
if exp >= 63 {
if base == 1 {
return 1
}
Expand All @@ -51,6 +43,15 @@ func ipow(base, exp int64) (result int64) {
}

return 0
}

if base > overflows[exp] || -base > overflows[exp] {
return 0
}

result = 1

switch highestBitSet[byte(exp)] {
case 6:
if exp&1 != 0 {
result *= base
Expand Down
40 changes: 33 additions & 7 deletions runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,18 +398,44 @@ func TestArgsKeys(t *testing.T) {

func TestIPowOverflow(t *testing.T) {
const SCRIPT = `
Math.pow(65536, 6)
assert.sameValue(Math.pow(65536, 6), 7.922816251426434e+28);
assert.sameValue(Math.pow(10, 19), 1e19);
assert.sameValue(Math.pow(2097151, 3), 9223358842721534000);
assert.sameValue(Math.pow(2097152, 3), 9223372036854776000);
assert.sameValue(Math.pow(-2097151, 3), -9223358842721534000);
assert.sameValue(Math.pow(-2097152, 3), -9223372036854776000);
assert.sameValue(Math.pow(9007199254740992, 0), 1);
assert.sameValue(Math.pow(-9007199254740992, 0), 1);
assert.sameValue(Math.pow(0, 0), 1);
`

testScript(SCRIPT, floatToValue(7.922816251426434e+28), t)
testScriptWithTestLib(SCRIPT, _undefined, t)
}

func TestIPowZero(t *testing.T) {
const SCRIPT = `
Math.pow(0, 0)
`
func TestIPow(t *testing.T) {
if res := ipow(-9223372036854775808, 1); res != -9223372036854775808 {
t.Fatal(res)
}

testScript(SCRIPT, intToValue(1), t)
if res := ipow(9223372036854775807, 1); res != 9223372036854775807 {
t.Fatal(res)
}

if res := ipow(-9223372036854775807, 1); res != -9223372036854775807 {
t.Fatal(res)
}

if res := ipow(9223372036854775807, 0); res != 1 {
t.Fatal(res)
}

if res := ipow(-9223372036854775807, 0); res != 1 {
t.Fatal(res)
}

if res := ipow(-9223372036854775808, 0); res != 1 {
t.Fatal(res)
}
}

func TestInterrupt(t *testing.T) {
Expand Down

0 comments on commit b196ae5

Please sign in to comment.