JIT: Fix run_time_cache offset stored without map_ptr dereference#22459
Closed
iliaal wants to merge 1 commit into
Closed
JIT: Fix run_time_cache offset stored without map_ptr dereference#22459iliaal wants to merge 1 commit into
iliaal wants to merge 1 commit into
Conversation
zend_jit_do_fcall() loaded the callee run_time_cache through the closure direct-pointer shortcut whenever the call frame was flagged as a closure call. That flag is set for every ZEND_INIT_DYNAMIC_CALL, but a dynamic call may resolve to a non-closure function whose run_time_cache is a zend_map_ptr offset. The raw offset was then stored into EX(run_time_cache) without resolving it through CG(map_ptr_base), and a later cache lookup dereferenced a bogus address. Restrict the shortcut to statically-known closures. Unknown dynamic calls fall through to the general path, which resolves both offsets and direct pointers. Fixes phpGH-22443
arnaud-lb
approved these changes
Jun 26, 2026
arnaud-lb
left a comment
Member
There was a problem hiding this comment.
Looks good to me!
The only case when TRACE_FRAME_IS_CLOSURE_CALL(JIT_G(current_frame)->call)) would be true (for an actual closure) while (func && (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) is not, is when the closure is not in SHM, which should be rare, so the change shouldn't result in performance regression.
The IS_CLOSURE_CALL flag could be removed entirely in master as a follow-up, since this is the only usage and the name is misleading.
Member
|
@iliaal This test takes 1 minute with asan on my (very fast) computer. Is that really necessary to trigger the bug? At the very least it should be marked as a slow test. |
Member
|
Should be addressed in 21204bf. |
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.
The tracing JIT could store a raw zend_map_ptr offset into EX(run_time_cache) for a polymorphic dynamic call (
$callable(...)) that resolves to a non-closure function or method, so the interpreter dereferenced a bogus address on the next cached lookup. The codegen took the closure direct-pointer shortcut based on TRACE_FRAME_IS_CLOSURE_CALL, but that flag is set for every ZEND_INIT_DYNAMIC_CALL regardless of the resolved callee.Restricting the shortcut to statically-known closures is safe: an invoked closure object always carries a resolved direct run_time_cache pointer (zend_closures.c), even when its persistent template is offset-tagged under opcache SHM or file_cache. Regression from 6872432; see GH-22443 for the report and the reproduction.