Skip to content

Fix GH-13817: Segmentation fault for enabled observers after pass 4 #14018

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

Closed
wants to merge 2 commits into from
Closed
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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ PHP NEWS
. Fixed bug GH-14267 (opcache.jit=off does not allow enabling JIT at runtime).
(ilutov)
. Fixed TLS access in JIT on FreeBSD/amd64. (Arnaud)
. Fixed bug GH-13817 (Segmentation fault for enabled observers after pass 4).
(Bob)

- Soap:
. Fixed bug #47925 (PHPClient can't decompress response). (nielsdos)
Expand Down
3 changes: 2 additions & 1 deletion Zend/Optimizer/compact_vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "Optimizer/zend_optimizer_internal.h"
#include "zend_bitset.h"
#include "zend_observer.h"

/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
* This pass does not operate on SSA form anymore. */
Expand Down Expand Up @@ -117,7 +118,7 @@ void zend_optimizer_compact_vars(zend_op_array *op_array) {
op_array->last_var = num_cvs;
}

op_array->T = num_tmps;
op_array->T = num_tmps + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled

free_alloca(vars_map, use_heap2);
}
3 changes: 2 additions & 1 deletion Zend/Optimizer/optimize_temp_vars_5.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_bitset.h"
#include "zend_observer.h"

#define INVALID_VAR ((uint32_t)-1)
#define GET_AVAILABLE_T() \
Expand Down Expand Up @@ -173,5 +174,5 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c
}

zend_arena_release(&ctx->arena, checkpoint);
op_array->T = max + 1;
op_array->T = max + 1 + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled
}
13 changes: 0 additions & 13 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#include "zend_inference.h"
#include "zend_dump.h"
#include "php.h"
#include "zend_observer.h"

#ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES
# define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32
Expand Down Expand Up @@ -1097,8 +1096,6 @@ static void zend_revert_pass_two(zend_op_array *op_array)
}
#endif

op_array->T -= ZEND_OBSERVER_ENABLED;

op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
}

Expand Down Expand Up @@ -1128,8 +1125,6 @@ static void zend_redo_pass_two(zend_op_array *op_array)
}
#endif

op_array->T += ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled

opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
Expand Down Expand Up @@ -1557,12 +1552,6 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
}
}

if (ZEND_OBSERVER_ENABLED) {
for (i = 0; i < call_graph.op_arrays_count; i++) {
++call_graph.op_arrays[i]->T; // ensure accurate temporary count for stack size precalculation
}
}

if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
for (i = 0; i < call_graph.op_arrays_count; i++) {
zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
Expand All @@ -1578,8 +1567,6 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
zend_recalc_live_ranges(op_array, needs_live_range);
}
} else {
op_array->T -= ZEND_OBSERVER_ENABLED; // redo_pass_two will re-increment it

zend_redo_pass_two(op_array);
if (op_array->live_range) {
zend_recalc_live_ranges(op_array, NULL);
Expand Down
51 changes: 51 additions & 0 deletions ext/opcache/tests/gh13817.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
--TEST--
GH-13712 (Segmentation fault for enabled observers after pass 4)
--EXTENSIONS--
opcache
zend_test
--INI--
zend_test.observer.enabled=1
zend_test.observer.show_output=1
zend_test.observer.observe_all=1
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=0x4069
--FILE--
<?php

function inner() {
echo "Ok\n";
}

function foo() {
// If stack size is wrong, inner() will corrupt the previous observed frame
inner();
}

// After foo() def so that we land here, with step_two undone for foo() first
function outer() {
// Pass 15 does constant string propagation, which gives a ZEND_INIT_DYNAMIC_FCALL on a const which Pass 4 will optimize
// Pass 4 must calc the right stack size here
(NAME)();
}

const NAME = "foo";

outer();

?>
--EXPECTF--
<!-- init '%s' -->
<file '%s'>
<!-- init outer() -->
<outer>
<!-- init foo() -->
<foo>
<!-- init inner() -->
<inner>
Ok
</inner>
</foo>
</outer>
</file '%s'>

Loading