Skip to content

Commit e2ea940

Browse files
authored
ext/gmp: Fix GMP operator RHS overflow for GMP values (#22656)
```php <?php $too_large = gmp_init("18446744073709551616"); var_dump(gmp_init(2) ** $too_large); var_dump(gmp_init(2) << $too_large); ``` ``` object(GMP)#3 (1) { ["num"]=> string(1) "1" } object(GMP)#2 (1) { ["num"]=> string(1) "2" } ``` This PR fix the overflow by rejecting large gmp objects in these calculation (throws ValueError)
1 parent a6f85c3 commit e2ea940

3 files changed

Lines changed: 67 additions & 16 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.6.0alpha3
44

5+
- GMP:
6+
. Fixed GMP power and shift operators to reject GMP right operands outside
7+
the unsigned long range instead of silently truncating them. (Weilin Du)
8+
59
- ODBC:
610
. Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
711
driver-reported display size). (iliaal)

ext/gmp/gmp.c

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,17 @@ 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 void 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+
}
336+
330337
static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) {
331338
zend_long shift = 0;
339+
gmp_ulong shift_ui = 0;
340+
bool have_shift_ui = false;
332341

333342
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
334343
if (UNEXPECTED(!IS_GMP(op2))) {
@@ -349,31 +358,36 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
349358
goto typeof_op_failure;
350359
}
351360
} else {
352-
// TODO We shouldn't cast the GMP object to int here
353-
shift = zval_get_long(op2);
361+
mpz_ptr gmpnum_shift = GET_GMP_FROM_ZVAL(op2);
362+
if (!mpz_fits_ulong_p(gmpnum_shift)) {
363+
gmp_shift_operator_range_error(opcode);
364+
return FAILURE;
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 {
366-
mpz_ptr gmpnum_op, gmpnum_result;
367-
368-
if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
369-
goto typeof_op_failure;
373+
if (!have_shift_ui) {
374+
if (shift < 0 || shift > ULONG_MAX) {
375+
gmp_shift_operator_range_error(opcode);
376+
return FAILURE;
370377
}
378+
shift_ui = (gmp_ulong) shift;
379+
}
371380

372-
INIT_GMP_RETVAL(gmpnum_result);
373-
op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
374-
return SUCCESS;
381+
mpz_ptr gmpnum_op, gmpnum_result;
382+
383+
if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
384+
goto typeof_op_failure;
375385
}
376386

387+
INIT_GMP_RETVAL(gmpnum_result);
388+
op(gmpnum_result, gmpnum_op, shift_ui);
389+
return SUCCESS;
390+
377391
typeof_op_failure: ;
378392
/* Returning FAILURE without throwing an exception would emit the
379393
* Unsupported operand types: GMP OP TypeOfOp2
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
try {
22+
var_dump(gmp_init(2) >> $too_large);
23+
} catch (ValueError $e) {
24+
echo $e->getMessage(), PHP_EOL;
25+
}
26+
27+
echo "Done\n";
28+
?>
29+
--EXPECTF--
30+
Exponent must be between 0 and %d
31+
Shift must be between 0 and %d
32+
Shift must be between 0 and %d
33+
Done

0 commit comments

Comments
 (0)