Skip to content

Commit 4a08ca1

Browse files
committed
Respect typed references in catch assignment
I decided to null out EG(exception) early here, which means only the exception from the dtor / ref assign is preserved, and the previous exception is not chained in. This is more robust, and I don't think this situation is common enough to be bothered about the precise behavior.
1 parent 314ab47 commit 4a08ca1

File tree

4 files changed

+64
-26
lines changed

4 files changed

+64
-26
lines changed

Zend/tests/bug53511.phpt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ function test() {
2020
test();
2121
echo "bug\n";
2222
--EXPECTF--
23-
Fatal error: Uncaught Exception: ops 2 in %sbug53511.php:11
24-
Stack trace:
25-
#0 %sbug53511.php(17): test()
26-
#1 {main}
27-
28-
Next Exception: ops 1 in %sbug53511.php:4
23+
Fatal error: Uncaught Exception: ops 1 in %sbug53511.php:4
2924
Stack trace:
3025
#0 %sbug53511.php(12): Foo->__destruct()
3126
#1 %sbug53511.php(17): test()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
Variable assignment in catch must respect typed references
3+
--FILE--
4+
5+
6+
class Test {
7+
public int $i = 42;
8+
public string $s = "str";
9+
}
10+
11+
$test = new Test;
12+
13+
$ref =& $test->i;
14+
try {
15+
try {
16+
throw new Exception("ex");
17+
} catch (Exception $ref) {
18+
echo "Unreachable\n";
19+
}
20+
} catch (TypeError $e) {
21+
var_dump($test->i);
22+
echo $e . "\n\n";
23+
}
24+
25+
$ref =& $test->s;
26+
try {
27+
try {
28+
throw new Exception("ex");
29+
} catch (Exception $ref) {
30+
echo "Unreachable\n";
31+
}
32+
} catch (TypeError $e) {
33+
var_dump($test->s);
34+
echo $e . "\n\n";
35+
}
36+
37+
?>
38+
--EXPECTF--
39+
int(42)
40+
TypeError: Cannot assign Exception to reference held by property Test::$i of type int in %s:%d
41+
Stack trace:
42+
#0 {main}
43+
44+
string(3) "str"
45+
TypeError: Cannot assign Exception to reference held by property Test::$s of type string in %s:%d
46+
Stack trace:
47+
#0 {main}

Zend/zend_vm_def.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4632,17 +4632,15 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
46324632

46334633
exception = EG(exception);
46344634
ex = EX_VAR(opline->result.var);
4635-
if (UNEXPECTED(Z_ISREF_P(ex))) {
4636-
ex = Z_REFVAL_P(ex);
4637-
}
4638-
zval_ptr_dtor(ex);
4639-
ZVAL_OBJ(ex, EG(exception));
4640-
if (UNEXPECTED(EG(exception) != exception)) {
4641-
GC_ADDREF(EG(exception));
4642-
HANDLE_EXCEPTION();
4643-
} else {
4635+
{
4636+
/* Always perform a strict assignment. There is a reasonable expectation that if you
4637+
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
4638+
* we should not permit coercion to string here. */
4639+
zval tmp;
4640+
ZVAL_OBJ(&tmp, exception);
46444641
EG(exception) = NULL;
4645-
ZEND_VM_NEXT_OPCODE();
4642+
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
4643+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
46464644
}
46474645
}
46484646

Zend/zend_vm_execute.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3753,17 +3753,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
37533753

37543754
exception = EG(exception);
37553755
ex = EX_VAR(opline->result.var);
3756-
if (UNEXPECTED(Z_ISREF_P(ex))) {
3757-
ex = Z_REFVAL_P(ex);
3758-
}
3759-
zval_ptr_dtor(ex);
3760-
ZVAL_OBJ(ex, EG(exception));
3761-
if (UNEXPECTED(EG(exception) != exception)) {
3762-
GC_ADDREF(EG(exception));
3763-
HANDLE_EXCEPTION();
3764-
} else {
3756+
{
3757+
/* Always perform a strict assignment. There is a reasonable expectation that if you
3758+
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
3759+
* we should not permit coercion to string here. */
3760+
zval tmp;
3761+
ZVAL_OBJ(&tmp, exception);
37653762
EG(exception) = NULL;
3766-
ZEND_VM_NEXT_OPCODE();
3763+
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
3764+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
37673765
}
37683766
}
37693767

0 commit comments

Comments
 (0)