Skip to content

Commit 233ffcc

Browse files
committed
Fix GH-10072: PHP crashes when execute_ex is overridden and a __call trampoline is used from internal code
1 parent 5f1311a commit 233ffcc

File tree

3 files changed

+117
-6
lines changed

3 files changed

+117
-6
lines changed

Zend/tests/gh10072.phpt

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
--TEST--
2+
GH-10072 (PHP crashes when execute_ex is overridden and a trampoline is used from internal code)
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.replace_zend_execute_ex=1
7+
--FILE--
8+
<?php
9+
class DummyStreamWrapper
10+
{
11+
/** @var resource|null */
12+
public $context;
13+
14+
/** @var resource|null */
15+
public $handle;
16+
17+
18+
public function stream_cast(int $castAs)
19+
{
20+
return $this->handle;
21+
}
22+
23+
24+
public function stream_close(): void
25+
{
26+
}
27+
28+
public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool
29+
{
30+
return true;
31+
}
32+
33+
34+
public function stream_read(int $count)
35+
{
36+
return 0;
37+
}
38+
39+
40+
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
41+
{
42+
return true;
43+
}
44+
45+
46+
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
47+
{
48+
return false;
49+
}
50+
51+
52+
public function stream_stat()
53+
{
54+
return [];
55+
}
56+
57+
58+
public function stream_tell()
59+
{
60+
return [];
61+
}
62+
63+
64+
public function stream_truncate(int $newSize): bool
65+
{
66+
return true;
67+
}
68+
69+
70+
public function stream_write(string $data)
71+
{
72+
}
73+
74+
75+
public function unlink(string $path): bool
76+
{
77+
return false;
78+
}
79+
}
80+
81+
class TrampolineTest {
82+
/** @var resource|null */
83+
public $context;
84+
85+
/** @var object|null */
86+
private $wrapper;
87+
88+
public function __call(string $name, array $arguments) {
89+
if (!$this->wrapper) {
90+
$this->wrapper = new DummyStreamWrapper();
91+
}
92+
echo 'Trampoline for ', $name, PHP_EOL;
93+
return $this->wrapper->$name(...$arguments);
94+
}
95+
96+
}
97+
98+
stream_wrapper_register('custom', TrampolineTest::class);
99+
100+
101+
$fp = fopen("custom://myvar", "r+");
102+
?>
103+
--EXPECT--
104+
Trampoline for stream_open
105+
Trampoline for stream_close

Zend/zend_vm_def.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -8663,7 +8663,9 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER))
86638663
SAVE_OPLINE_EX();
86648664
ZEND_OBSERVER_FCALL_BEGIN(execute_data);
86658665
execute_data = EX(prev_execute_data);
8666-
LOAD_OPLINE();
8666+
if (execute_data) {
8667+
LOAD_OPLINE();
8668+
}
86678669
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
86688670
zend_execute_ex(call);
86698671
}
@@ -8713,7 +8715,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER))
87138715

87148716
execute_data = EG(current_execute_data);
87158717

8716-
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
8718+
if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
87178719
ZEND_VM_RETURN();
87188720
}
87198721

Zend/zend_vm_execute.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -3319,7 +3319,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
33193319
SAVE_OPLINE_EX();
33203320

33213321
execute_data = EX(prev_execute_data);
3322-
LOAD_OPLINE();
3322+
if (execute_data) {
3323+
LOAD_OPLINE();
3324+
}
33233325
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
33243326
zend_execute_ex(call);
33253327
}
@@ -3369,7 +3371,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
33693371

33703372
execute_data = EG(current_execute_data);
33713373

3372-
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
3374+
if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
33733375
ZEND_VM_RETURN();
33743376
}
33753377

@@ -3456,7 +3458,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
34563458
SAVE_OPLINE_EX();
34573459
zend_observer_fcall_begin(execute_data);
34583460
execute_data = EX(prev_execute_data);
3459-
LOAD_OPLINE();
3461+
if (execute_data) {
3462+
LOAD_OPLINE();
3463+
}
34603464
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
34613465
zend_execute_ex(call);
34623466
}
@@ -3506,7 +3510,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
35063510

35073511
execute_data = EG(current_execute_data);
35083512

3509-
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
3513+
if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
35103514
ZEND_VM_RETURN();
35113515
}
35123516

0 commit comments

Comments
 (0)