Skip to content

Commit c00badd

Browse files
committed
Defer user error handlers raised from user-code opcodes
A user error handler raised mid-opcode can reenter the VM and free or mutate state the opcode still holds a raw pointer into, a long-standing source of use-after-free and memory-corruption bugs. Defer the handler to the next vm_interrupt safepoint, after the opcode completes. The interpreter and both JITs reach that safepoint through the existing vm_interrupt mechanism. A diagnostic silenced by @ keeps its error_reporting across the deferral; diagnostics raised from internal functions still run synchronously, since the function may gate on EG(exception) or throw right after emitting. Function JIT flushes at the interpreter's safepoints: post-call with the callee frame still on top, each jump-target block, and function entry, spilling the live SSA values before it resumes. Exception handling flushes any buffered diagnostic with the pending exception saved and restored around the handler, so nothing is dropped when a later opcode throws. Under the CALL VM the flush is deferred past a call being assembled, so a throwing handler cannot unwind into a half-built frame and fault in cleanup_unfinished_calls. Fixes GH-20018 Fixes GH-16792 Fixes GH-17416 Fixes GH-18274 Fixes GH-20042 Fixes GH-21245 Fixes GH-22419
1 parent 81f09e8 commit c00badd

94 files changed

Lines changed: 1085 additions & 399 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Zend/tests/ArrayAccess/bug41209.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ echo "Done\n";
4242
Fatal error: Uncaught ErrorException: Undefined variable $id in %s:%d
4343
Stack trace:
4444
#0 %s(%d): env::errorHandler(2, 'Undefined varia...', '%s', %d)
45-
#1 {main}
45+
#1 %s(%d): cache->offsetExists(NULL)
46+
#2 {main}
4647
thrown in %s on line %d

Zend/tests/bitwise_not_precision_exception.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ try {
1212
}
1313
?>
1414
--EXPECT--
15+
int(-1)
1516
The float INF is not representable as an int, cast occurred

Zend/tests/bug63206.phpt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ set_error_handler(function() {
2222
$triggerNotice1++;
2323
$triggerNotice2++;
2424
?>
25-
--EXPECT--
25+
--EXPECTF--
2626
Second handler
27-
Internal handler
27+
28+
Warning: Undefined variable $triggerInternalNotice in %s on line %d
2829
Second handler
29-
Internal handler
30+
31+
Warning: Undefined variable $triggerInternalNotice in %s on line %d

Zend/tests/bug70662.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ var_dump($a);
1414
--EXPECT--
1515
array(1) {
1616
["b"]=>
17-
int(2)
17+
int(1)
1818
}

Zend/tests/bug70785.phpt

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,32 @@ set_error_handler(function($no, $msg) {
77
throw new Exception($msg);
88
});
99

10-
try {
10+
function smart_branch() {
1111
if ($a === null) { // ZEND_VM_SMART_BRANCH
12-
undefined_function('Null');
12+
return 'branch';
1313
}
14-
} catch (Exception $e) {
14+
return 'no branch';
1515
}
1616

17-
try {
17+
function next_opcode() {
1818
$c === 3; // ZEND_VM_NEXT_OPCODE
19-
undefined_function();
19+
return 'done';
20+
}
21+
22+
try {
23+
smart_branch();
2024
} catch (Exception $e) {
25+
echo $e->getMessage(), PHP_EOL;
2126
}
27+
28+
try {
29+
next_opcode();
30+
} catch (Exception $e) {
31+
echo $e->getMessage(), PHP_EOL;
32+
}
33+
echo "okey\n";
2234
?>
23-
okey
2435
--EXPECT--
36+
Undefined variable $a
37+
Undefined variable $c
2538
okey

Zend/tests/bug72101.phpt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ $foo->bar($a, $b, $c);
7979
Fatal error: Uncaught Error: Class "DoesNotExists" not found in %s:%d
8080
Stack trace:
8181
#0 %s(%d): {closure:%s:%d}(2, 'MethodCallbackB...', '%s', 8)
82-
#1 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
83-
#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static))
84-
#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
85-
#4 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0)
86-
#5 {main}
82+
#1 %sbug72101.php(%d): MethodCallbackByReference->callback(0, 0, 0)
83+
#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
84+
#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static))
85+
#4 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static))
86+
#5 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0)
87+
#6 {main}
8788
thrown in %sbug72101.php on line %d

Zend/tests/bug76534.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ $x = "foo";
1010
$y = &$x["2bar"];
1111
?>
1212
--EXPECTF--
13-
Fatal error: Uncaught Exception: Illegal string offset "2bar" in %s:%d
13+
Fatal error: Uncaught Error: Cannot create references to/from string offsets in %s:%d
14+
Stack trace:
15+
#0 {main}
16+
17+
Next Exception: Illegal string offset "2bar" in %s:%d
1418
Stack trace:
1519
#0 %s(%d): {closure:%s:%d}(2, 'Illegal string ...', '%s', 7)
1620
#1 {main}
17-
thrown in %sbug76534.php on line %d
21+
thrown in %s on line %d

Zend/tests/bug78598.phpt

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,31 @@ var_dump($my_var);
2525

2626
?>
2727
--EXPECT--
28-
int(0)
29-
int(0)
30-
int(0)
31-
int(0)
28+
array(1) {
29+
[0]=>
30+
string(3) "xyz"
31+
}
32+
array(1) {
33+
[0]=>
34+
array(1) {
35+
[0]=>
36+
array(1) {
37+
[0]=>
38+
string(3) "xyz"
39+
}
40+
}
41+
}
42+
array(1) {
43+
["foo"]=>
44+
string(3) "xyz"
45+
}
46+
array(1) {
47+
["foo"]=>
48+
array(1) {
49+
["bar"]=>
50+
array(1) {
51+
["baz"]=>
52+
string(3) "xyz"
53+
}
54+
}
55+
}

Zend/tests/bug79784.phpt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ var_dump($a);
1515

1616
?>
1717
--EXPECT--
18-
NULL
19-
NULL
20-
NULL
18+
array(1) {
19+
[""]=>
20+
string(1) "x"
21+
}
22+
array(1) {
23+
[""]=>
24+
string(1) "x"
25+
}
26+
array(1) {
27+
[""]=>
28+
array(1) {
29+
[""]=>
30+
string(1) "x"
31+
}
32+
}

Zend/tests/bug79793.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ var_dump($ary);
1818

1919
?>
2020
--EXPECT--
21-
Undefined array key "foobar"
2221
array(1) {
2322
["foobar"]=>
2423
int(1)
2524
}
26-
Undefined array key "foobarbaz"
25+
Undefined array key "foobar"
2726
array(2) {
2827
["foobar"]=>
2928
int(1)
3029
["foobarbaz"]=>
3130
int(1)
3231
}
32+
Undefined array key "foobarbaz"

0 commit comments

Comments
 (0)