[php-src] Issue #24713: ext/gmp: operator-overloaded ** and << crash with SIGFPE on large operand (shift_operator_helper unbounded)

From: Date: Mon, 26 Oct 2026 04:06:08 +0000
Subject: [php-src] Issue #24713: ext/gmp: operator-overloaded ** and << crash with SIGFPE on large operand (shift_operator_helper unbounded)
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253203@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24713 Author: kunwaratulhax0r ### Description GMP object operator overloading for ** (pow) and << (left shift) crashes the PHP process with an uncatchable SIGFPE when given a sufficiently large second operand. This is the same underlying GMP overflow-detection abort as GH-16384 (gmp_pow() function-call form), but reached through a different, separate code path: shift_operator_helper() in ext/gmp/gmp.c, which backs the operator-overloaded forms rather than the gmp_pow()/gmp_mul() function calls. ```php <?php var_dump(gmp_init(2) ** 400000000000); ``` ```php <?php var_dump(gmp_init(2) << 400000000000); ``` Either script crashes the process immediately with no catchable PHP-level error. ### Root cause In shift_operator_helper(): ```c if (!have_shift_ui) { if (shift < 0 || shift > ULONG_MAX) { gmp_shift_operator_range_error(opcode); return FAILURE; } shift_ui = (gmp_ulong) shift; } ``` shift is a zend_long (signed 64-bit), so shift > ULONG_MAX can never be true — a signed 64-bit value can never reach the max of an unsigned 64-bit type. The effective upper bound is therefore nonexistent. This function is used for **, <<, and >> via gmp_do_operation_ex(): ```c case ZEND_POW: return shift_operator_helper(mpz_pow_ui, ...); case ZEND_SL: return shift_operator_helper(mpz_mul_2exp, ...); case ZEND_SR: return shift_operator_helper(mpz_fdiv_q_2exp, ...); ``` A huge shift reaches mpz_pow_ui/mpz_mul_2exp directly. Both need to allocate a result whose size scales with shift itself, and GMP's own internal size bookkeeping detects the resulting overflow and raises SIGFPE via its internal exception path — uncatchable by PHP. (>>/mpz_fdiv_q_2exp doesn't hit this, since a huge right-shift just truncates to 0 in roughly constant time.) ### Not a duplicate of GH-16384 GH-16384 is about ZEND_FUNCTION(gmp_pow) — the function-call form gmp_pow($base, $exp) — which has its own, separate copy of essentially the same broken bound check at a different line. This report is about the **operator-overloaded** form ($base ** $exp, $base << $n), a structurally distinct code path (shift_operator_helper) that was never touched by any prior fix for GH-16384. ### Affected versions Confirmed on current master. Also affects PHP-8.3/PHP-8.4 — the equivalent code on those branches has *no* upper-bound check at all (just if (shift < 0)), so the same mpz_pow_ui/mpz_mul_2exp call is reached with an attacker-controlled huge value there too. ### PHP Version master (and PHP-8.3, PHP-8.4) ### Operating System Linux (reproduced in a clean Ubuntu 24.04 container; not OS-specific)

« previous php.bugs (#253203) next »