Skip to content

Commit e053c6b

Browse files
committed
ext/gmp: Reject too-large inputs in gmp_fact() to avoid overflow
Fixes GH-16878
1 parent 4f25ea8 commit e053c6b

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

ext/gmp/gmp.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,7 @@ ZEND_FUNCTION(gmp_fact)
12761276
{
12771277
zval *a_arg;
12781278
mpz_ptr gmpnum_result;
1279+
zend_long num_long;
12791280

12801281
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
12811282
RETURN_THROWS();
@@ -1286,21 +1287,36 @@ ZEND_FUNCTION(gmp_fact)
12861287
zend_argument_value_error(1, "must be greater than or equal to 0");
12871288
RETURN_THROWS();
12881289
}
1290+
num_long = Z_LVAL_P(a_arg);
12891291
} else {
12901292
mpz_ptr gmpnum;
12911293
gmp_temp_t temp_a;
12921294

12931295
FETCH_GMP_ZVAL(gmpnum, a_arg, temp_a, 1);
1294-
FREE_GMP_TEMP(temp_a);
12951296

12961297
if (mpz_sgn(gmpnum) < 0) {
1298+
FREE_GMP_TEMP(temp_a);
12971299
zend_argument_value_error(1, "must be greater than or equal to 0");
12981300
RETURN_THROWS();
12991301
}
1302+
1303+
if (!mpz_fits_ulong_p(gmpnum)) {
1304+
FREE_GMP_TEMP(temp_a);
1305+
zend_argument_value_error(1, "is too large");
1306+
RETURN_THROWS();
1307+
}
1308+
1309+
num_long = (zend_long) mpz_get_ui(gmpnum);
1310+
FREE_GMP_TEMP(temp_a);
1311+
}
1312+
1313+
if (num_long > 100000) {
1314+
zend_argument_value_error(1, "is too large");
1315+
RETURN_THROWS();
13001316
}
13011317

13021318
INIT_GMP_RETVAL(gmpnum_result);
1303-
mpz_fac_ui(gmpnum_result, zval_get_long(a_arg));
1319+
mpz_fac_ui(gmpnum_result, (unsigned long) num_long);
13041320
}
13051321
/* }}} */
13061322

ext/gmp/tests/gh16878.phpt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
GH-16878: Core dump when gmp_fact allocates huge memory
3+
--EXTENSIONS--
4+
gmp
5+
--FILE--
6+
<?php
7+
echo "Test 1: Factorial of 2^50 + 1\n";
8+
try {
9+
$value = 2**50 + 1;
10+
echo "Calculating factorial of: $value\n";
11+
$result = gmp_fact($value);
12+
echo "Result: " . gmp_strval($result) . "\n";
13+
} catch (\ValueError $e) {
14+
echo "ValueError: " . $e->getMessage() . "\n";
15+
} catch (\Error $e) {
16+
echo "Error: " . $e->getMessage() . "\n";
17+
}
18+
19+
echo "\nTest 2: Another large value\n";
20+
try {
21+
$value = 1000000000000; // 1 trillion
22+
echo "Calculating factorial of: $value\n";
23+
$result = gmp_fact($value);
24+
echo "Result: " . gmp_strval($result) . "\n";
25+
} catch (\ValueError $e) {
26+
echo "ValueError: " . $e->getMessage() . "\n";
27+
} catch (\Error $e) {
28+
echo "Error: " . $e->getMessage() . "\n";
29+
}
30+
31+
echo "\nTest 3: Moderately large value that should work\n";
32+
try {
33+
$value = 100;
34+
echo "Calculating factorial of: $value\n";
35+
$result = gmp_fact($value);
36+
echo "Result length: " . strlen(gmp_strval($result)) . " digits\n";
37+
} catch (\ValueError $e) {
38+
echo "ValueError: " . $e->getMessage() . "\n";
39+
} catch (\Error $e) {
40+
echo "Error: " . $e->getMessage() . "\n";
41+
}
42+
43+
echo "\nDone\n";
44+
?>
45+
--EXPECTF--
46+
Test 1: Factorial of 2^50 + 1
47+
Calculating factorial of: 1125899906842625
48+
ValueError: %s
49+
50+
Test 2: Another large value
51+
Calculating factorial of: 1000000000000
52+
ValueError: %s
53+
54+
Test 3: Moderately large value that should work
55+
Calculating factorial of: 100
56+
Result length: 158 digits
57+
58+
Done

0 commit comments

Comments
 (0)