diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index ef06d0fda30c..9bf2d65f2df2 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -365,11 +365,37 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val return FAILURE; } else { mpz_ptr gmpnum_op, gmpnum_result; - + size_t bits; + if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) { goto typeof_op_failure; } + bits = mpz_sizeinbase(gmpnum_op, 2); + if (bits == 0) { + bits = 1; + } + + if (opcode == ZEND_POW) { + if ((size_t) shift > (SIZE_MAX - 5) / bits) { + zend_value_error( + "exponent results in a value that exceeds the supported size" + ); + return FAILURE; + } + } + + if (opcode == ZEND_SL) { + size_t max_shift = ((size_t)INT_MAX * GMP_NUMB_BITS); + + if ((size_t) shift > max_shift) { + zend_value_error( + "shift count results in a value that exceeds the supported size" + ); + return FAILURE; + } + } + INIT_GMP_RETVAL(gmpnum_result); op(gmpnum_result, gmpnum_op, (gmp_ulong) shift); return SUCCESS; @@ -1125,14 +1151,22 @@ ZEND_FUNCTION(gmp_pow) mpz_ptr gmpnum_result; mpz_ptr gmpnum_base; zend_long exp; + size_t bits; ZEND_PARSE_PARAMETERS_START(2, 2) GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_base) Z_PARAM_LONG(exp) ZEND_PARSE_PARAMETERS_END(); - if (exp < 0 || exp > ULONG_MAX) { - zend_argument_value_error(2, "must be between 0 and %lu", ULONG_MAX); + if (exp < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); + } + + bits = mpz_sizeinbase(gmpnum_base, 2); + + if (exp > (SIZE_MAX - 5) / bits) { + zend_argument_value_error(2, "results in a value that exceeds the supported size"); RETURN_THROWS(); } diff --git a/ext/gmp/tests/gh22351.phpt b/ext/gmp/tests/gh22351.phpt new file mode 100644 index 000000000000..d5aa0cec8fa9 --- /dev/null +++ b/ext/gmp/tests/gh22351.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-22351: gmp_pow with PHP_INT_MAX should not crash +--EXTENSIONS-- +gmp +--FILE-- +getMessage() . PHP_EOL; +} + +echo "Done\n"; +?> +--EXPECTF-- +Testing gmp_pow overflow safety +ValueError: gmp_pow(): Argument #2 ($exponent) results in a value that exceeds the supported size +Done diff --git a/ext/gmp/tests/gmp_overflow_llp64.phpt b/ext/gmp/tests/gmp_overflow_llp64.phpt index ea242bc52155..7a9e0adeeb12 100644 --- a/ext/gmp/tests/gmp_overflow_llp64.phpt +++ b/ext/gmp/tests/gmp_overflow_llp64.phpt @@ -49,7 +49,7 @@ try { echo "Done\n"; ?> --EXPECTF-- -gmp_pow(): Argument #2 ($exponent) must be between 0 and %d +gmp_pow(): Argument #2 ($exponent) results in a value that exceeds the supported size gmp_binomial(): Argument #2 ($k) must be between 0 and %d gmp_root(): Argument #2 ($nth) must be between 1 and %d gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d diff --git a/ext/gmp/tests/gmp_pow.phpt b/ext/gmp/tests/gmp_pow.phpt index 36d0d16d8ccc..6a61cf43a617 100644 --- a/ext/gmp/tests/gmp_pow.phpt +++ b/ext/gmp/tests/gmp_pow.phpt @@ -49,11 +49,11 @@ string(4) "1024" string(5) "-2048" string(4) "1024" string(1) "1" -gmp_pow(): Argument #2 ($exponent) must be between 0 and %d +gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0 string(4) "1024" string(14) "10240000000000" string(17) "97656250000000000" -gmp_pow(): Argument #2 ($exponent) must be between 0 and %d +gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0 string(14) "10240000000000" string(14) "10240000000000" gmp_pow(): Argument #2 ($exponent) must be of type int, array given