[php-src] Issue #24801: JIT: SIGFPE in ir_fold MUL_OV constant folding for `$x = -1; $x *= PHP_INT_MIN` in a hot loop
| From: | waroad | Date: | Tue, 27 Oct 2026 23:24:16 +0000 |
| Subject: | [php-src] Issue #24801: JIT: SIGFPE in ir_fold MUL_OV constant folding for `$x = -1; $x *= PHP_INT_MIN` in a hot loop | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253255@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24801
Author: waroad
With the tracing JIT, compiling a trace that contains
$x = -1; $x *= PHP_INT_MIN
computes INT64_MIN / -1 inside the IR constant folder, which is undefined behaviour and
crashes the release build with SIGFPE.
```php
<?php
for ($k = 0; $k < 200; $k++) { $x = -1; $x *= PHP_INT_MIN; }
var_dump($x);
```
```
php -n -d opcache.enable_cli=1 -d opcache.file_update_protection=0 -d opcache.jit=tracing -d
opcache.jit_buffer_size=64M red.php
```
Release build: the process dies with SIGFPE (exit status 136) and prints nothing. A UBSan build
(UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1) reports:
```
ext/opcache/jit/ir/ir_fold.h:1004:37: runtime error: division of -9223372036854775808 by -1 cannot
be represented in type 'long int'
#0 ir_folding ext/opcache/jit/ir/ir_fold.h:1004
#1 ir_fold ext/opcache/jit/ir/ir.c:1305
#2 ir_fold2 ext/opcache/jit/ir/ir.c:1320
#3 zend_jit_math_long_long ext/opcache/jit/zend_jit_ir.c:5039
#4 zend_jit_math_helper ext/opcache/jit/zend_jit_ir.c:5382
```
I expected float(9.2233720368547758E+18), which is what -1 * PHP_INT_MIN
evaluates to without the JIT, and no crash.
Found at commit c65321c70f7de5f4a4f810f1d23112374d92d1a7 (UBSan report and release SIGFPE as above).
It still reproduces at 1b1aa3578034: the UBSan build reports the same division at ir_fold.h:1004,
and the release build exits with SIGFPE (136).
Looks like the overflow check in IR_FOLD(MUL_OV(C_I64, C_I64)) in
ext/opcache/jit/ir/ir_fold.h (res / op1_insn->val.i64 != op2_insn->val.i64)
divides by -1 when res is INT64_MIN. It may need a -1 guard
or __builtin_mul_overflow.
The trigger is a generated test case, not code from a real program.
Found with an LLM-based testing tool; I used Claude to reduce it and look for the cause.