[php-src] Issue #24675: JIT: function with >~1.7 MB of machine code corrupts JIT buffer accounting; next compilation crashes (SIGBUS on arm64)
| From: | ChrisOrlando | Date: | Sun, 25 Oct 2026 10:28:54 +0000 |
| Subject: | [php-src] Issue #24675: JIT: function with >~1.7 MB of machine code corrupts JIT buffer accounting; next compilation crashes (SIGBUS on arm64) | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253174@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24675
Author: ChrisOrlando
### Description
With function JIT (
opcache.jit=function, also 1204), compiling a single
function whose
generated machine code exceeds roughly 1.7 MB makes
opcache_get_status()['jit']['buffer_free']
**increase** by about the size of that code, instead of decreasing. The next compilation that
emits code then crashes the process with SIGBUS (exit status 138).
The following code:
```php
<?php
// php -d opcache.enable_cli=1 -d opcache.jit=function -d opcache.jit_buffer_size=64M \
// -d opcache.file_update_protection=0 -d memory_limit=1G repro.php
$body = '';
for ($k = 0; $k < 3000; $k++) {
$body .= " \$r[" . ($k % 32) . "] = \$v + $k;\n";
}
file_put_contents(__DIR__ . '/big.php', "<?php\nfunction big(array &\$r, int
\$v) {\n$body}\n");
file_put_contents(__DIR__ . '/next.php', "<?php\necho \"next file
compiled\\n\";\n");
$free = fn() => opcache_get_status(false)['jit']['buffer_free'];
$before = $free();
include __DIR__ . '/big.php';
printf("JIT buffer_free changed by %+d bytes (should be negative: code was added)\n",
$free() - $before);
include __DIR__ . '/next.php';
echo "survived\n";
```
Resulted in this output:
```
JIT buffer_free changed by +1859668 bytes (should be negative: code was added)
Bus error: 10 (exit status 138)
```
But I expected this output instead:
```
JIT buffer_free changed by -1859668 bytes (should be negative: code was added)
next file compiled
survived
```
### Observations
- The threshold is the size of one function's machine code, not the input: with this shape,
2850 statements add 1,766,964 bytes and work; 2851 statements report buffer_free
going
*up* by 1,767,384 and the next compilation crashes. Closures and named functions, by-value
and by-reference array parameters all behave the same.
- The big function itself can be called fine afterwards; it's the *next* compilation that
faults, which fits the buffer's free pointer having moved backwards.
- Reproduces with opcache.jit=function and 1204 and JIT buffer sizes of
16M, 64M and 128M.
Doesn't with 1201 (optimization level 1), 1215 (compile on first
call, since big()
isn't called here), tracing (nothing compiled at load), or a 1M buffer (presumably it
doesn't
fit at all).
- opcache.file_update_protection=0 is only there so the freshly written files are
cached
and JIT-compiled; with pre-existing files the flag isn't needed.
- Found in a PHP emulator that generates large PHP functions at run time; it showed up there as
"zend_mm_heap corrupted", "Undefined array key" on impossible lines, and bus
errors.
### PHP Version
```
PHP 8.5.12 (cli) (built: Oct 19 2026 17:02:46) (NTS) [Homebrew]
```
### Operating System
macOS (Darwin 27.0.0), Apple M1 (arm64)