Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add guards to oapi cufa opcode check #708

Merged
merged 3 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions agent/php_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -1934,6 +1934,7 @@ static void nr_php_observer_attempt_call_cufa_handler(NR_EXECUTE_PROTO) {
nrl_verbosedebug(NRL_AGENT, "%s: cannot get previous execute data", __func__);
return;
}

if (NULL == execute_data->prev_execute_data->opline) {
nrl_verbosedebug(NRL_AGENT, "%s: cannot get previous opline", __func__);
return;
Expand Down Expand Up @@ -1966,20 +1967,43 @@ static void nr_php_observer_attempt_call_cufa_handler(NR_EXECUTE_PROTO) {
* cause additional performance overhead, this should be considered a last
* resort.
*/
const zend_op* prev_opline = execute_data->prev_execute_data->opline - 1;

/*
* When Observer API is used, this code executes in the context of
* zend_execute and not in the context of VM (as was the case pre-OAPI),
* therefore we need to ensure we're dealing with a user function. We cannot
* safely access the opline of internal functions, and we only want to
* instrument cufa calls from user functions anyway.
*/
if (UNEXPECTED(NULL == execute_data->prev_execute_data->func)) {
nrl_verbosedebug(NRL_AGENT, "%s: cannot get previous function", __func__);
return;
}
if (!ZEND_USER_CODE(execute_data->prev_execute_data->func->type)) {
nrl_verbosedebug(NRL_AGENT, "%s: caller is php internal function", __func__);
return;
}

const zend_op* prev_opline = execute_data->prev_execute_data->opline;

/*
* Extra safety check. Previously, we instrumented by overwritting ZEND_DO_FCALL.
* Within OAPI, for consistency's sake, we will ensure the same
*/
if (ZEND_DO_FCALL != prev_opline->opcode) {
nrl_verbosedebug(NRL_AGENT, "%s: cannot get previous function name", __func__);
return;
}
prev_opline -= 1; // Checks previous opcode
lavarou marked this conversation as resolved.
Show resolved Hide resolved
if (ZEND_CHECK_UNDEF_ARGS == prev_opline->opcode) {
prev_opline = execute_data->prev_execute_data->opline - 2;
prev_opline -= 1; // Checks previous opcode
}
if (ZEND_SEND_ARRAY == prev_opline->opcode) {

if (UNEXPECTED((NULL == execute_data->func))) {
nrl_verbosedebug(NRL_AGENT, "%s: cannot get current function", __func__);
return;
}
if (UNEXPECTED(NULL == execute_data->prev_execute_data->func)) {
zsistla marked this conversation as resolved.
Show resolved Hide resolved
nrl_verbosedebug(NRL_AGENT, "%s: cannot get previous function", __func__);
return;
}

if (UNEXPECTED(NULL == execute_data->prev_execute_data->func->common.function_name)) {
nrl_verbosedebug(NRL_AGENT, "%s: cannot get previous function name", __func__);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

/*EXPECT
g
f
h
g
Expand Down Expand Up @@ -70,4 +71,13 @@ function f() {
}
}

/*
* Initiates a non-flattened call stack of internal->user_code
* to ensure that cufa instrumentation properly handles skipping
* opline lookups of internal functions
*/
$function = new ReflectionFunction('g');
$function->invoke();


do_action("f");
Loading