Skip to content

Commit eee5fc0

Browse files
committed
Fix GH-22158: JIT observer dispatch through wrong run_time_cache slot
For a megamorphic call the tracing JIT built the observer handler pointer with the ir_PHI_2 operands reversed relative to the ir_MERGE_WITH predecessor order, so a user function read the internal_function_extension slot instead of op_array_extension. When an extension reserves an op_array handle before the observer registers the two indices differ, that slot holds 0, and the JIT calls a NULL begin handler. The default-off zend_test.observer.reserve_op_array_handle INI forces that index mismatch so the regression test can reproduce the crash. Fixes GH-22158
1 parent d70568e commit eee5fc0

5 files changed

Lines changed: 45 additions & 1 deletion

File tree

NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ PHP NEWS
2525
. Fix stmt->query leak in mysqli_execute_query() validation errors.
2626
(David Carlier)
2727

28+
- Opcache:
29+
. Fixed bug GH-22158 (Tracing JIT dispatches the observer begin handler
30+
through the wrong run_time_cache slot on megamorphic calls). (ptondereau,
31+
iliaal)
32+
2833
- Phar:
2934
. Fixed a bypass of the magic ".phar" directory protection in
3035
Phar::addEmptyDir() for paths starting with "/.phar", while allowing

ext/opcache/jit/zend_jit_ir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4815,7 +4815,7 @@ static struct jit_observer_fcall_is_unobserved_data jit_observer_fcall_is_unobse
48154815
ir_ref observer_handler_user = ir_ADD_OFFSET(run_time_cache, zend_observer_fcall_op_array_extension * sizeof(void *));
48164816

48174817
ir_MERGE_WITH(if_internal_func_end);
4818-
*observer_handler = ir_PHI_2(IR_ADDR, observer_handler_internal, observer_handler_user);
4818+
*observer_handler = ir_PHI_2(IR_ADDR, observer_handler_user, observer_handler_internal);
48194819
}
48204820

48214821
// JIT: if (*observer_handler == ZEND_OBSERVER_NONE_OBSERVED) {

ext/opcache/tests/jit/gh22158.phpt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-22158 (Tracing JIT dispatches observer begin handler through the wrong run_time_cache slot on megamorphic calls)
3+
--EXTENSIONS--
4+
opcache
5+
zend_test
6+
--INI--
7+
opcache.enable=1
8+
opcache.enable_cli=1
9+
opcache.jit=tracing
10+
opcache.jit_buffer_size=32M
11+
opcache.jit_max_polymorphic_calls=0
12+
zend_test.observer.enabled=1
13+
zend_test.observer.observe_all=1
14+
zend_test.observer.show_output=0
15+
zend_test.observer.reserve_op_array_handle=1
16+
--FILE--
17+
<?php
18+
interface S { public function f(): int; }
19+
final class A implements S { public function f(): int { return 1; } }
20+
final class B implements S { public function f(): int { return 2; } }
21+
final class C implements S { public function f(): int { return 3; } }
22+
final class D implements S { public function f(): int { return 4; } }
23+
final class E implements S { public function f(): int { return 5; } }
24+
25+
$o = [new A, new B, new C, new D, new E];
26+
$t = 0;
27+
for ($i = 0; $i < 200000; $i++) {
28+
$t += $o[$i % 5]->f();
29+
}
30+
echo $t, "\n";
31+
?>
32+
--EXPECT--
33+
600000

ext/zend_test/observer.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "php_test.h"
1919
#include "observer.h"
2020
#include "zend_observer.h"
21+
#include "zend_extensions.h"
2122
#include "zend_smart_str.h"
2223
#include "ext/standard/php_var.h"
2324
#include "zend_generators.h"
@@ -378,6 +379,7 @@ PHP_INI_BEGIN()
378379
STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_switch", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_switch, zend_zend_test_globals, zend_test_globals)
379380
STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_destroy", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_destroy, zend_zend_test_globals, zend_test_globals)
380381
STD_PHP_INI_BOOLEAN("zend_test.observer.execute_internal", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_execute_internal, zend_zend_test_globals, zend_test_globals)
382+
STD_PHP_INI_BOOLEAN("zend_test.observer.reserve_op_array_handle", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_reserve_op_array_handle, zend_zend_test_globals, zend_test_globals)
381383
PHP_INI_END()
382384

383385
void zend_test_observer_init(INIT_FUNC_ARGS)
@@ -386,6 +388,9 @@ void zend_test_observer_init(INIT_FUNC_ARGS)
386388
if (type != MODULE_TEMPORARY) {
387389
REGISTER_INI_ENTRIES();
388390
if (ZT_G(observer_enabled)) {
391+
if (ZT_G(observer_reserve_op_array_handle)) {
392+
zend_get_op_array_extension_handle("zend_test");
393+
}
389394
zend_observer_fcall_register(observer_fcall_init);
390395
}
391396
} else {

ext/zend_test/php_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
5151
int observer_fiber_switch;
5252
int observer_fiber_destroy;
5353
int observer_execute_internal;
54+
int observer_reserve_op_array_handle;
5455
HashTable *global_weakmap;
5556
int replace_zend_execute_ex;
5657
int register_passes;

0 commit comments

Comments
 (0)