Prechádzať zdrojové kódy

Check for overflows in ipow()

Dmitry Panov 2 rokov pred
rodič
commit
b196ae5149
3 zmenil súbory, kde vykonal 66 pridanie a 39 odobranie
  1. 1 1
      builtin_math.go
  2. 32 31
      ipow.go
  3. 33 7
      runtime_test.go

+ 1 - 1
builtin_math.go

@@ -183,7 +183,7 @@ NaNLoop:
 
 
 func pow(x, y Value) Value {
 func pow(x, y Value) Value {
 	if x, ok := x.(valueInt); ok {
 	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 {
 			if y == 0 {
 				return intToValue(1)
 				return intToValue(1)
 			}
 			}

+ 32 - 31
ipow.go

@@ -1,8 +1,27 @@
 package goja
 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,
 	0, 1, 2, 2, 3, 3, 3, 3,
 	4, 4, 4, 4, 4, 4, 4, 4,
 	4, 4, 4, 4, 4, 4, 4, 4,
 	5, 5, 5, 5, 5, 5, 5, 5,
 	5, 5, 5, 5, 5, 5, 5, 5,
@@ -10,38 +29,11 @@ var highest_bit_set = [256]byte{
 	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, 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) {
 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 {
 		if base == 1 {
 			return 1
 			return 1
 		}
 		}
@@ -51,6 +43,15 @@ func ipow(base, exp int64) (result int64) {
 		}
 		}
 
 
 		return 0
 		return 0
+	}
+
+	if base > overflows[exp] || -base > overflows[exp] {
+		return 0
+	}
+
+	result = 1
+
+	switch highestBitSet[byte(exp)] {
 	case 6:
 	case 6:
 		if exp&1 != 0 {
 		if exp&1 != 0 {
 			result *= base
 			result *= base

+ 33 - 7
runtime_test.go

@@ -398,18 +398,44 @@ func TestArgsKeys(t *testing.T) {
 
 
 func TestIPowOverflow(t *testing.T) {
 func TestIPowOverflow(t *testing.T) {
 	const SCRIPT = `
 	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) {
 func TestInterrupt(t *testing.T) {