Repository navigation
Fix AArch64 JIT stub handlers pointing at discarded veneers after opcache restart - #24383
Closed
austinderrick wants to merge 1 commit into
Closed
austinderrick wants to merge 1 commit into
austinderrick wants to merge 1 commit into
Conversation
…ache restart zend_jit_commit_veneers() replaces zend_jit_stub_handlers[] entries with veneers emitted inside trace code. zend_jit_restart() discards that code but kept the table, so traces compiled after opcache_reset() branched into unrelated code and PHP-FPM workers crashed or hung. Save the table at startup and restore it on restart.
Member
|
Is this a duplicate of #24273 |
Author
|
Closing as a duplicate of #24273, which fixes the same bug and was opened first. I posted the NTS reproduction there. |
pavimus
added a commit
to pavimus/php-src
that referenced
this pull request
Oct 17, 2026
Based on the reproducer by Derrick Austin from php#24383: more than 1 MB of tracing JIT code, opcache_reset(), then different code. Without the fix the server crashes on AArch64 after the reset. Co-authored-by: Derrick Austin <derrick.austin@interworks.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On AArch64,
opcache_reset()with the tracing JIT leaveszend_jit_stub_handlers[]pointing at veneers in code that the restart discarded. Traces compiled afterwards overwrite those veneers, so a later branch to an affected stub jumps into unrelated machine code. PHP-FPM workers then crash with SIGSEGV or hang at 100% CPU until FPM is restarted.Cause
zend_jit_stub_handlers[]lives at the start of the JIT buffer.zend_jit_commit_veneers()replaceszend_jit_stub_handlers[i]with the veneer address.zend_jit_restart()resets the buffer position (dasm_ptr[0] = dasm_ptr[1]) but keeps the table.In a Laravel application, a gdb dump of the table showed six entries (
exception_handler,exception_handler_undef,interrupt_handler,leave_function_handler,icall_throw,leave_throw) moving from the stub area to about 1 MB into the buffer after warm-up. Afteropcache_reset(), they still pointed there while the buffer was back to 3.9 KB in use.The failure needs the code compiled after the reset to differ from the code before it. When the same code runs in the same order, IR rebuilds the same veneers at the same addresses.
Fix
Save the stub table at the end of
zend_jit_startup()and restore it, with the pending-veneer slots cleared, inzend_jit_restart().Reproduction
Official
php:8.4-fpm-trixieimage (8.4.23, arm64) behind nginx,pm = staticwith 4 children:Two generated workloads: A is 1,500 classes with hot loops, and B is 3,000 classes with polymorphic branches, closures and caught exceptions (about 8 MB of JIT code). Page
b.phpruns B; pageab.phpruns A and then B;reset.phpcallsopcache_reset().b.phpx2,ab.phpx2ab.phpin 0.3 to 0.8 sb.phpx2,reset.php,ab.phpab.phpnever returns; workers at 100% CPUopcache.jit=disableIn the Laravel application, the same sequence crashes every worker with SIGSEGV inside
zend_jit_trace_exitor in JIT code callingzend_error_unchecked, also on 8.4.26. With this patch applied to 8.4.23, repeated resets return 200 with no crashes.This may be the same bug as GH-11609.
Reproduction scripts
Put the files in one directory with this layout and run
./run.shon an arm64 host with Docker. It generates both workloads, then startsphp:8.4-fpmbehind nginx twice: once without a reset and once withreset.phpbetween the warm-up andab.php.Output on arm64 (8.4.23 and 8.4.26 behave the same):
With
JIT=disable ./run.sh, with./run.sh php:8.4-fpm --platform linux/amd64, and with this patch applied to 8.4.23, every request returns 200.run.shgen.phpgen2.phpconf/jit.iniconf/www.confconf/nginx.confapp/a.incapp/b.incapp/b.phpapp/ab.phpapp/reset.php