From 34f1266a9c038c57a8a6319a056b53cd93986655 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 23 Mar 2020 17:10:54 +0100 Subject: [PATCH] Handle NULL caller_call_opline This can happen if there is an EXIT in the call arguments, in which case the DO_CALL opcode may be eliminated as unreachable. --- ext/opcache/Optimizer/dfa_pass.c | 9 ++++++--- ext/opcache/Optimizer/sccp.c | 3 ++- ext/opcache/Optimizer/zend_call_graph.c | 8 +++++++- ext/opcache/Optimizer/zend_inference.c | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 3bff500de1..01baadd1e7 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -301,8 +301,10 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op while (call_info) { call_info->caller_init_opline -= shiftlist[call_info->caller_init_opline - op_array->opcodes]; - call_info->caller_call_opline -= - shiftlist[call_info->caller_call_opline - op_array->opcodes]; + if (call_info->caller_call_opline) { + call_info->caller_call_opline -= + shiftlist[call_info->caller_call_opline - op_array->opcodes]; + } call_info = call_info->next_callee; } } @@ -388,7 +390,8 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) zend_call_info *call_info = func_info->callee_info; do { - if (call_info->caller_call_opline->opcode == ZEND_DO_ICALL + if (call_info->caller_call_opline + && call_info->caller_call_opline->opcode == ZEND_DO_ICALL && call_info->callee_func && ZSTR_LEN(call_info->callee_func->common.function_name) == sizeof("in_array")-1 && memcmp(ZSTR_VAL(call_info->callee_func->common.function_name), "in_array", sizeof("in_array")-1) == 0 diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 1ec3a51532..53549a6208 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -1251,7 +1251,8 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } call = ctx->call_map[opline - ctx->scdf.op_array->opcodes]; - if (IS_TOP(op1) || !call || call->caller_call_opline->opcode != ZEND_DO_ICALL) { + if (IS_TOP(op1) || !call || !call->caller_call_opline + || call->caller_call_opline->opcode != ZEND_DO_ICALL) { return; } diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index 4dde298280..109fc72353 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -171,6 +171,10 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f call_info->num_args = -1; } break; + case ZEND_EXIT: + /* In this case the DO_CALL opcode may have been dropped + * and caller_call_opline will be NULL. */ + break; } opline++; } @@ -280,7 +284,9 @@ zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, z for (call = info->callee_info; call; call = call->next_callee) { int i; map[call->caller_init_opline - op_array->opcodes] = call; - map[call->caller_call_opline - op_array->opcodes] = call; + if (call->caller_call_opline) { + map[call->caller_call_opline - op_array->opcodes] = call; + } for (i = 0; i < call->num_args; i++) { if (call->arg_info[i].opline) { map[call->arg_info[i].opline - op_array->opcodes] = call; diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 7bddb82705..a60ecef888 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -4145,7 +4145,7 @@ void zend_inference_check_recursive_dependencies(zend_op_array *op_array) memset(worklist, 0, sizeof(zend_ulong) * worklist_len); call_info = info->callee_info; while (call_info) { - if (call_info->recursive && + if (call_info->recursive && call_info->caller_call_opline && info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def >= 0) { zend_bitset_incl(worklist, info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def); } -- 2.40.0