Skip to content

Commit 2cf7ad0

Browse files
committed
Fix GH-24081: User opcode DISPATCH runs on a stale frame in the TAILCALL VM
ZEND_VM_DISPATCH() ran the CALL variant of the handler and returned its next opline to execute_ex(), whose execute_data still pointed at the entry frame. It now tail calls the TAILCALL variant of the handler. Close GH-24109
1 parent ac1a9c1 commit 2cf7ad0

4 files changed

Lines changed: 34 additions & 4 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ PHP NEWS
1010
. Fixed memory manager keeping a block reallocated to exactly the size of
1111
the next smaller bin in its larger bin, which efree_size() then freed
1212
into the wrong one. (Marc Bennewitz)
13+
. Fixed bug GH-24081 (User opcode handlers resume execution against a stale
14+
frame under ZEND_VM_KIND_TAILCALL). (devnexen)
1315

1416
- Opcache:
1517
. Fixed bug GH-17626 (JIT corrupts an opline handler when blacklisting a

‎Zend/zend_vm_execute.h‎

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Zend/zend_vm_gen.php‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
19221922
out($f,"static const zend_op call_interrupt_op;\n");
19231923
out($f,"#endif\n\n");
19241924
}
1925-
out($f,"#if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL) || !ZEND_VM_SPEC\n");
1925+
out($f,"#if ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !ZEND_VM_SPEC\n");
19261926
out($f,"static zend_vm_opcode_handler_t zend_vm_get_opcode_handler(uint8_t opcode, const zend_op* op);\n");
19271927
out($f,"#endif\n\n");
19281928
if ($kind == ZEND_VM_KIND_HYBRID) {
@@ -2149,6 +2149,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
21492149
out($f,"# undef ZEND_VM_RETURN\n");
21502150
out($f,"# undef ZEND_VM_DISPATCH_TO_HELPER\n");
21512151
out($f,"# undef ZEND_VM_INTERRUPT\n");
2152+
out($f,"# undef ZEND_VM_DISPATCH\n");
21522153
out($f,"\n");
21532154
out($f,"# define ZEND_VM_TAIL_CALL(call) ZEND_MUSTTAIL return call\n");
21542155
out($f,"# define ZEND_VM_CONTINUE() ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n");
@@ -2160,6 +2161,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
21602161
out($f," } while (0)\n");
21612162
out($f,"# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()\n");
21622163
out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n");
2164+
out($f,"# define ZEND_VM_DISPATCH(opcode, opline) ZEND_VM_TAIL_CALL(zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n");
21632165
out($f,"\n");
21642166
out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n");
21652167
out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS);\n");
@@ -3043,7 +3045,7 @@ function gen_vm($def, $skel) {
30433045
out($f, "\treturn (spec & SPEC_START_MASK) + offset;\n");
30443046
}
30453047
out($f, "}\n\n");
3046-
out($f, "#if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL) || !ZEND_VM_SPEC\n");
3048+
out($f, "#if ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !ZEND_VM_SPEC\n");
30473049
out($f, "static zend_vm_opcode_handler_t zend_vm_get_opcode_handler(uint8_t opcode, const zend_op* op)\n");
30483050
out($f, "{\n");
30493051
if (!ZEND_VM_SPEC) {

‎ext/zend_test/tests/gh24081.phpt‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-24081 (User opcode handlers resume execution against a stale frame under ZEND_VM_KIND_TAILCALL)
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
opcache.jit=disable
7+
zend_test.observer.enabled=1
8+
zend_test.observer.show_opcode_in_user_handler=ZEND_ADD
9+
--FILE--
10+
<?php
11+
function f($a, $b) {
12+
$r = $a + $b;
13+
return strlen("x") + $r;
14+
}
15+
16+
var_dump(f(1, 2));
17+
?>
18+
--EXPECTF--
19+
<!-- init '%s' -->
20+
<!-- init f() -->
21+
<!-- opcode: 'ZEND_ADD' in user handler -->
22+
<!-- opcode: 'ZEND_ADD' in user handler -->
23+
<!-- init var_dump() -->
24+
int(4)

0 commit comments

Comments
 (0)