Skip to content

Commit 0bc13ae

Browse files
committed
ext/gmp: Fix GMP operator RHS overflow for GMP values
1 parent 93e2349 commit 0bc13ae

3 files changed

Lines changed: 53 additions & 10 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ PHP NEWS
3838
- GMP:
3939
. Fixed bug GH-22549 (Assertion failure / UB on a compound GMP power or shift
4040
assignment with a negative exponent). (iliaal)
41+
. Fixed GMP power and shift operators to reject GMP right operands outside
42+
the unsigned long range instead of silently truncating them. (Weilin Du)
4143

4244
- Intl:
4345
. Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to

ext/gmp/gmp.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,18 @@ static zend_result binop_operator_helper(gmp_binary_op_t gmp_op, zval *return_va
327327

328328
typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);
329329

330+
static zend_result gmp_shift_operator_range_error(uint8_t opcode) {
331+
zend_throw_error(
332+
zend_ce_value_error, "%s must be between 0 and %lu",
333+
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
334+
);
335+
return FAILURE;
336+
}
337+
330338
static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) {
331339
zend_long shift = 0;
340+
gmp_ulong shift_ui = 0;
341+
bool have_shift_ui = false;
332342

333343
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
334344
if (UNEXPECTED(!IS_GMP(op2))) {
@@ -349,28 +359,33 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
349359
goto typeof_op_failure;
350360
}
351361
} else {
352-
// TODO We shouldn't cast the GMP object to int here
353-
shift = zval_get_long(op2);
362+
mpz_ptr gmpnum_shift = GET_GMP_FROM_ZVAL(op2);
363+
if (!mpz_fits_ulong_p(gmpnum_shift)) {
364+
return gmp_shift_operator_range_error(opcode);
365+
}
366+
shift_ui = (gmp_ulong) mpz_get_ui(gmpnum_shift);
367+
have_shift_ui = true;
354368
}
355369
} else {
356370
shift = Z_LVAL_P(op2);
357371
}
358372

359-
if (shift < 0 || shift > ULONG_MAX) {
360-
zend_throw_error(
361-
zend_ce_value_error, "%s must be between 0 and %lu",
362-
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
363-
);
364-
return FAILURE;
365-
} else {
373+
if (!have_shift_ui) {
374+
if (shift < 0 || shift > ULONG_MAX) {
375+
return gmp_shift_operator_range_error(opcode);
376+
}
377+
shift_ui = (gmp_ulong) shift;
378+
}
379+
380+
{
366381
mpz_ptr gmpnum_op, gmpnum_result;
367382

368383
if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
369384
goto typeof_op_failure;
370385
}
371386

372387
INIT_GMP_RETVAL(gmpnum_result);
373-
op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
388+
op(gmpnum_result, gmpnum_op, shift_ui);
374389
return SUCCESS;
375390
}
376391

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GMP operator right operand rejects values outside the unsigned long range
3+
--EXTENSIONS--
4+
gmp
5+
--FILE--
6+
<?php
7+
$too_large = gmp_init("18446744073709551616");
8+
9+
try {
10+
var_dump(gmp_init(2) ** $too_large);
11+
} catch (ValueError $e) {
12+
echo $e->getMessage(), PHP_EOL;
13+
}
14+
15+
try {
16+
var_dump(gmp_init(2) << $too_large);
17+
} catch (ValueError $e) {
18+
echo $e->getMessage(), PHP_EOL;
19+
}
20+
21+
echo "Done\n";
22+
?>
23+
--EXPECTF--
24+
Exponent must be between 0 and %d
25+
Shift must be between 0 and %d
26+
Done

0 commit comments

Comments
 (0)