Fixed GH-16233: Observer segfault when calling user function in internal function via trampoline #16252
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.
In the test, I have an internal
__call
function for_ZendTestMagicCallForward
that calls the global function with name$name
viacall_user_function
. Note that observer writes the pointer to the previously observed frame in the last temporary of the new call frame (*prev_observed_frame
).The following happens:
First, we call
$test->callee
, this will be handled via a trampoline with T=2 for the two arguments. The call frame is allocated at this point. This call frame is not observed because it hasZEND_ACC_CALL_VIA_TRAMPOLINE
set. Next we useZEND_CALL_TRAMPOLINE
to call the trampoline, this reuses the stack frame allocated earlier with T=2, but this time it is observed. The pointer to the previous frame is written outside of the call frame becauseT
is too small (should be 3). We are now in the internal function_ZendTestMagicCallForward::__call
where we call the global functioncallee
. This will push a new call frame which will overlap*prev_observed_frame
. This value gets overwritten byzend_init_func_execute_data
whenEX(opline)
is set because*prev_observed_frame
overlaps withEX(opline)
. From now on,*prev_observed_frame
is corrupted. Whenzend_observer_fcall_end
is called this will result in reading wrong value*prev_observed_frame
intocurrent_observed_frame
. This causes issues inzend_observer_fcall_end_all
leading to the segfault we observe.Despite function with
ZEND_ACC_CALL_VIA_TRAMPOLINE
not being observed, the reuse of call frames makes problems whenT
is not large enough. To fix this, we make sure to add 1 toT
ifZEND_OBSERVER_ENABLED
is true.