From e053c6b0a506886d3cda00a5aac18816f9b04af5 Mon Sep 17 00:00:00 2001 From: Khaled Alam Date: Sun, 11 Jan 2026 00:15:47 +0400 Subject: [PATCH 1/3] ext/gmp: Reject too-large inputs in gmp_fact() to avoid overflow Fixes GH-16878 --- ext/gmp/gmp.c | 20 +++++++++++-- ext/gmp/tests/gh16878.phpt | 58 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 ext/gmp/tests/gh16878.phpt diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index f31d6ab810232..30e1ff5538e6c 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -1276,6 +1276,7 @@ ZEND_FUNCTION(gmp_fact) { zval *a_arg; mpz_ptr gmpnum_result; + zend_long num_long; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){ RETURN_THROWS(); @@ -1286,21 +1287,36 @@ ZEND_FUNCTION(gmp_fact) zend_argument_value_error(1, "must be greater than or equal to 0"); RETURN_THROWS(); } + num_long = Z_LVAL_P(a_arg); } else { mpz_ptr gmpnum; gmp_temp_t temp_a; FETCH_GMP_ZVAL(gmpnum, a_arg, temp_a, 1); - FREE_GMP_TEMP(temp_a); if (mpz_sgn(gmpnum) < 0) { + FREE_GMP_TEMP(temp_a); zend_argument_value_error(1, "must be greater than or equal to 0"); RETURN_THROWS(); } + + if (!mpz_fits_ulong_p(gmpnum)) { + FREE_GMP_TEMP(temp_a); + zend_argument_value_error(1, "is too large"); + RETURN_THROWS(); + } + + num_long = (zend_long) mpz_get_ui(gmpnum); + FREE_GMP_TEMP(temp_a); + } + + if (num_long > 100000) { + zend_argument_value_error(1, "is too large"); + RETURN_THROWS(); } INIT_GMP_RETVAL(gmpnum_result); - mpz_fac_ui(gmpnum_result, zval_get_long(a_arg)); + mpz_fac_ui(gmpnum_result, (unsigned long) num_long); } /* }}} */ diff --git a/ext/gmp/tests/gh16878.phpt b/ext/gmp/tests/gh16878.phpt new file mode 100644 index 0000000000000..597d49d6a7ae2 --- /dev/null +++ b/ext/gmp/tests/gh16878.phpt @@ -0,0 +1,58 @@ +--TEST-- +GH-16878: Core dump when gmp_fact allocates huge memory +--EXTENSIONS-- +gmp +--FILE-- +getMessage() . "\n"; +} catch (\Error $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + +echo "\nTest 2: Another large value\n"; +try { + $value = 1000000000000; // 1 trillion + echo "Calculating factorial of: $value\n"; + $result = gmp_fact($value); + echo "Result: " . gmp_strval($result) . "\n"; +} catch (\ValueError $e) { + echo "ValueError: " . $e->getMessage() . "\n"; +} catch (\Error $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + +echo "\nTest 3: Moderately large value that should work\n"; +try { + $value = 100; + echo "Calculating factorial of: $value\n"; + $result = gmp_fact($value); + echo "Result length: " . strlen(gmp_strval($result)) . " digits\n"; +} catch (\ValueError $e) { + echo "ValueError: " . $e->getMessage() . "\n"; +} catch (\Error $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + +echo "\nDone\n"; +?> +--EXPECTF-- +Test 1: Factorial of 2^50 + 1 +Calculating factorial of: 1125899906842625 +ValueError: %s + +Test 2: Another large value +Calculating factorial of: 1000000000000 +ValueError: %s + +Test 3: Moderately large value that should work +Calculating factorial of: 100 +Result length: 158 digits + +Done From 8d8a8bdb3bdf31a8e8c3c2d9748042604a47da76 Mon Sep 17 00:00:00 2001 From: Khaled Alam Date: Sun, 11 Jan 2026 00:32:44 +0400 Subject: [PATCH 2/3] feat: remove unrelated changes. --- ext/standard/tests/file/bug69442.phpt | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/standard/tests/file/bug69442.phpt b/ext/standard/tests/file/bug69442.phpt index d1b1affe646e8..82e186eec9ef9 100644 --- a/ext/standard/tests/file/bug69442.phpt +++ b/ext/standard/tests/file/bug69442.phpt @@ -1,6 +1,5 @@ --TEST-- proc_open with PTY closes incorrect file descriptor ---FLAKY-- --SKIPIF-- Date: Sun, 11 Jan 2026 01:15:07 +0400 Subject: [PATCH 3/3] test: fix gmp_fact test case on x32. --- ext/gmp/tests/gh16878.phpt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/gmp/tests/gh16878.phpt b/ext/gmp/tests/gh16878.phpt index 597d49d6a7ae2..ed42d20e14362 100644 --- a/ext/gmp/tests/gh16878.phpt +++ b/ext/gmp/tests/gh16878.phpt @@ -6,8 +6,8 @@ gmp