Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0alpha3

- GMP:
. Fixed GMP power and shift operators to reject GMP right operands outside
the unsigned long range instead of silently truncating them. (Weilin Du)

- ODBC:
. Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
driver-reported display size). (iliaal)
Expand Down
46 changes: 30 additions & 16 deletions ext/gmp/gmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,17 @@ static zend_result binop_operator_helper(gmp_binary_op_t gmp_op, zval *return_va

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

static void gmp_shift_operator_range_error(uint8_t opcode) {
zend_throw_error(
zend_ce_value_error, "%s must be between 0 and %lu",
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
);
}

static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) {
zend_long shift = 0;
gmp_ulong shift_ui = 0;
bool have_shift_ui = false;

if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
if (UNEXPECTED(!IS_GMP(op2))) {
Expand All @@ -349,31 +358,36 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
goto typeof_op_failure;
}
} else {
// TODO We shouldn't cast the GMP object to int here
shift = zval_get_long(op2);
mpz_ptr gmpnum_shift = GET_GMP_FROM_ZVAL(op2);
if (!mpz_fits_ulong_p(gmpnum_shift)) {
gmp_shift_operator_range_error(opcode);
return FAILURE;
}
shift_ui = (gmp_ulong) mpz_get_ui(gmpnum_shift);
have_shift_ui = true;
}
} else {
shift = Z_LVAL_P(op2);
}

if (shift < 0 || shift > ULONG_MAX) {
zend_throw_error(
zend_ce_value_error, "%s must be between 0 and %lu",
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
);
return FAILURE;
} else {
mpz_ptr gmpnum_op, gmpnum_result;

if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
goto typeof_op_failure;
if (!have_shift_ui) {
if (shift < 0 || shift > ULONG_MAX) {
gmp_shift_operator_range_error(opcode);
return FAILURE;
}
shift_ui = (gmp_ulong) shift;
}

INIT_GMP_RETVAL(gmpnum_result);
op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
return SUCCESS;
mpz_ptr gmpnum_op, gmpnum_result;

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

INIT_GMP_RETVAL(gmpnum_result);
op(gmpnum_result, gmpnum_op, shift_ui);
return SUCCESS;

typeof_op_failure: ;
/* Returning FAILURE without throwing an exception would emit the
* Unsupported operand types: GMP OP TypeOfOp2
Expand Down
33 changes: 33 additions & 0 deletions ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
GMP operator right operand rejects values outside the unsigned long range
--EXTENSIONS--
gmp
--FILE--
<?php
$too_large = gmp_init("18446744073709551616");

try {
var_dump(gmp_init(2) ** $too_large);
} catch (ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

try {
var_dump(gmp_init(2) << $too_large);
} catch (ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

try {
var_dump(gmp_init(2) >> $too_large);
} catch (ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

echo "Done\n";
?>
--EXPECTF--
Exponent must be between 0 and %d
Shift must be between 0 and %d
Shift must be between 0 and %d
Done
Loading