Skip to content

Commit e0a0e21

Browse files
committed
ext/gmp: gmp_pow fix FPE with large values.
even without sanitizers, it is reproducible but with the following ``` <?php $g = gmp_init(256); var_dump(gmp_pow($g, PHP_INT_MAX)); ``` we get this ``` AddressSanitizer:DEADLYSIGNAL ================================================================= ==286922==ERROR: AddressSanitizer: FPE on unknown address 0x03e8000460ca (pc 0x7faf6c69de5c bp 0x400000000000004 sp 0x7ffe9843c740 T0) #0 0x7faf6c69de5c in __pthread_kill_implementation nptl/pthread_kill.c:44 #1 0x7faf6c649c81 in __GI_raise ../sysdeps/posix/raise.c:26 #2 0x7faf6db9386c in __gmp_exception (/lib/x86_64-linux-gnu/libgmp.so.10+0xd86c) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38) #3 0x7faf6db938d3 in __gmp_overflow_in_mpz (/lib/x86_64-linux-gnu/libgmp.so.10+0xd8d3) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38) #4 0x7faf6dbac95c in __gmpz_realloc (/lib/x86_64-linux-gnu/libgmp.so.10+0x2695c) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38) #5 0x7faf6dba9038 in __gmpz_n_pow_ui (/lib/x86_64-linux-gnu/libgmp.so.10+0x23038) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38) #6 0x5565ae1ccd9f in zif_gmp_pow /home/dcarlier/Contribs/php-src/ext/gmp/gmp.c:1286 #7 0x5565aee96ea9 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:1312 #8 0x5565af144320 in execute_ex /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:56075 #9 0x5565af160f07 in zend_execute /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:60439 #10 0x5565aed6fafe in zend_execute_scripts /home/dcarlier/Contribs/php-src/Zend/zend.c:1842 #11 0x5565aeae70a8 in php_execute_script /home/dcarlier/Contribs/php-src/main/main.c:2578 #12 0x5565af532f4e in do_cli /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:964 #13 0x5565af535877 in main /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:1334 #14 0x7faf6c633d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #15 0x7faf6c633e24 in __libc_start_main_impl ../csu/libc-start.c:360 #16 0x5565adc04040 in _start (/home/dcarlier/Contribs/php-src/sapi/cli/php+0x2604040) (BuildId: 949049955bdf8b7197390b1978a1dfc3ef6fdf38) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: FPE nptl/pthread_kill.c:44 in __pthread_kill_implementation ==286922==ABORTING ``` close GH-16384
1 parent e1e1e64 commit e0a0e21

File tree

5 files changed

+119
-35
lines changed

5 files changed

+119
-35
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ PHP NEWS
6464
. Fixed bug GH-16411 (gmp_export() can cause overflow). (cmb)
6565
. Fixed bug GH-16501 (gmp_random_bits() can cause overflow).
6666
(David Carlier)
67+
. Fixed gmp_pow() overflow bug with large base/exponents.
68+
(David Carlier)
6769

6870
- MBstring:
6971
. Fixed bug GH-16361 (mb_substr overflow on start/length arguments).

ext/gmp/gmp.c

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,39 +1282,27 @@ ZEND_FUNCTION(gmp_pow)
12821282
RETURN_THROWS();
12831283
}
12841284

1285+
double powmax = log((double)ZEND_LONG_MAX);
1286+
12851287
if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
12861288
INIT_GMP_RETVAL(gmpnum_result);
1287-
if (exp >= INT_MAX) {
1288-
mpz_t base_num, exp_num, mod;
1289-
mpz_init(base_num);
1290-
mpz_init(exp_num);
1291-
mpz_init(mod);
1292-
mpz_set_si(base_num, Z_LVAL_P(base_arg));
1293-
mpz_set_si(exp_num, exp);
1294-
mpz_set_ui(mod, UINT_MAX);
1295-
mpz_powm(gmpnum_result, base_num, exp_num, mod);
1296-
mpz_clear(mod);
1297-
mpz_clear(exp_num);
1298-
mpz_clear(base_num);
1299-
} else {
1300-
mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
1289+
if ((log(Z_LVAL_P(base_arg)) * exp) > powmax) {
1290+
zend_value_error("base and exponent overflow");
1291+
RETURN_THROWS();
13011292
}
1293+
mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
13021294
} else {
13031295
mpz_ptr gmpnum_base;
1296+
zend_ulong gmpnum;
13041297
FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
13051298
INIT_GMP_RETVAL(gmpnum_result);
1306-
if (exp >= INT_MAX) {
1307-
mpz_t exp_num, mod;
1308-
mpz_init(exp_num);
1309-
mpz_init(mod);
1310-
mpz_set_si(exp_num, exp);
1311-
mpz_set_ui(mod, UINT_MAX);
1312-
mpz_powm(gmpnum_result, gmpnum_base, exp_num, mod);
1313-
mpz_clear(mod);
1314-
mpz_clear(exp_num);
1315-
} else {
1316-
mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
1299+
gmpnum = mpz_get_ui(gmpnum_base);
1300+
if ((log(gmpnum) * exp) > powmax) {
1301+
FREE_GMP_TEMP(temp_base);
1302+
zend_value_error("base and exponent overflow");
1303+
RETURN_THROWS();
13171304
}
1305+
mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
13181306
FREE_GMP_TEMP(temp_base);
13191307
}
13201308
}

ext/gmp/tests/gmp_pow.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
gmp_pow() basic tests
33
--EXTENSIONS--
44
gmp
5+
--SKIPIF--
6+
<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
57
--FILE--
68
<?php
79

ext/gmp/tests/gmp_pow_32bits.phpt

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
gmp_pow() basic tests
3+
--EXTENSIONS--
4+
gmp
5+
--SKIPIF--
6+
<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only"); ?>
7+
--FILE--
8+
<?php
9+
10+
var_dump(gmp_strval(gmp_pow(2,10)));
11+
var_dump(gmp_strval(gmp_pow(-2,10)));
12+
var_dump(gmp_strval(gmp_pow(-2,11)));
13+
var_dump(gmp_strval(gmp_pow("2",10)));
14+
var_dump(gmp_strval(gmp_pow("2",0)));
15+
try {
16+
gmp_pow("2", -1);
17+
} catch (ValueError $exception) {
18+
echo $exception->getMessage() . "\n";
19+
}
20+
var_dump(gmp_strval(gmp_pow("-2",10)));
21+
try {
22+
gmp_pow(20,10);
23+
} catch (ValueError $exception) {
24+
echo $exception->getMessage() . "\n";
25+
}
26+
try {
27+
gmp_pow(50,10);
28+
} catch (ValueError $exception) {
29+
echo $exception->getMessage() . "\n";
30+
}
31+
try {
32+
gmp_pow(50,-5);
33+
} catch (ValueError $exception) {
34+
echo $exception->getMessage() . "\n";
35+
}
36+
try {
37+
$n = gmp_init("20");
38+
gmp_pow($n,10);
39+
} catch (ValueError $exception) {
40+
echo $exception->getMessage() . "\n";
41+
}
42+
try {
43+
$n = gmp_init("-20");
44+
gmp_pow($n,10);
45+
} catch (ValueError $exception) {
46+
echo $exception->getMessage() . "\n";
47+
}
48+
try {
49+
var_dump(gmp_pow(2,array()));
50+
} catch (TypeError $e) {
51+
echo $e->getMessage(), "\n";
52+
}
53+
54+
try {
55+
var_dump(gmp_pow(array(),10));
56+
} catch (\TypeError $e) {
57+
echo $e->getMessage() . \PHP_EOL;
58+
}
59+
60+
echo "Done\n";
61+
?>
62+
--EXPECT--
63+
string(4) "1024"
64+
string(4) "1024"
65+
string(5) "-2048"
66+
string(4) "1024"
67+
string(1) "1"
68+
gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0
69+
string(4) "1024"
70+
base and exponent overflow
71+
base and exponent overflow
72+
gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0
73+
base and exponent overflow
74+
base and exponent overflow
75+
gmp_pow(): Argument #2 ($exponent) must be of type int, array given
76+
gmp_pow(): Argument #1 ($num) must be of type GMP|string|int, array given
77+
Done

ext/gmp/tests/gmp_pow_fpe.phpt

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,30 @@ gmp
66
<?php
77
$g = gmp_init(256);
88

9-
var_dump(gmp_pow($g, PHP_INT_MAX));
10-
var_dump(gmp_pow(256, PHP_INT_MAX));
11-
?>
12-
--EXPECTF--
13-
object(GMP)#2 (1) {
14-
["num"]=>
15-
string(%d) "%s"
9+
try {
10+
gmp_pow($g, PHP_INT_MAX);
11+
} catch (\ValueError $e) {
12+
echo $e->getMessage() . PHP_EOL;
1613
}
17-
object(GMP)#2 (1) {
18-
["num"]=>
19-
string(%d) "%s"
14+
try {
15+
gmp_pow(256, PHP_INT_MAX);
16+
} catch (\ValueError $e) {
17+
echo $e->getMessage() . PHP_EOL;
18+
}
19+
20+
try {
21+
gmp_pow(gmp_add(gmp_mul(gmp_init(PHP_INT_MAX), gmp_init(PHP_INT_MAX)), 3), 256);
22+
} catch (\ValueError $e) {
23+
echo $e->getMessage() . PHP_EOL;
2024
}
25+
try {
26+
gmp_pow(gmp_init(PHP_INT_MAX), 256);
27+
} catch (\ValueError $e) {
28+
echo $e->getMessage();
29+
}
30+
?>
31+
--EXPECTF--
32+
base and exponent overflow
33+
base and exponent overflow
34+
base and exponent overflow
35+
base and exponent overflow

0 commit comments

Comments
 (0)