Skip to content

Commit 95a0709

Browse files
committed
Fix handling of ERROR zval in op1 of ASSIGN_REF
If op1 is ERROR the behavior is to not perform any assignment and return NULL. However, if the RHS was a by-value returning function, we'd instead emit a notice and return the RHS as the return value (even though the value was not assigned to anything -- the temporary is immediately destroyed). This normalized the behavior to always check for an ERROR in op1 first.
1 parent 6d23909 commit 95a0709

File tree

3 files changed

+95
-78
lines changed

3 files changed

+95
-78
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
If the LHS of ref-assign ERRORs, that takes precendence over the "only variables" notice
3+
--FILE--
4+
<?php
5+
6+
function val() {
7+
return 42;
8+
}
9+
10+
$str = "foo";
11+
$var = 24;
12+
var_dump($str->foo =& $var);
13+
var_dump($str);
14+
var_dump($str->foo =& val());
15+
var_dump($str);
16+
17+
?>
18+
--EXPECTF--
19+
Warning: Attempt to modify property 'foo' of non-object in %s on line %d
20+
NULL
21+
string(3) "foo"
22+
23+
Warning: Attempt to modify property 'foo' of non-object in %s on line %d
24+
NULL
25+
string(3) "foo"

Zend/zend_vm_def.h

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,16 +2323,18 @@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV, SRC)
23232323
value_ptr = GET_OP2_ZVAL_PTR_PTR(BP_VAR_W);
23242324
variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
23252325

2326-
if (OP1_TYPE == IS_VAR &&
2327-
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
2328-
UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
2326+
if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
2327+
variable_ptr = &EG(uninitialized_zval);
2328+
} else if (OP1_TYPE == IS_VAR &&
2329+
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT)) {
23292330

23302331
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
23312332
FREE_OP1_VAR_PTR();
23322333
FREE_OP2_VAR_PTR();
23332334
UNDEF_RESULT();
23342335
HANDLE_EXCEPTION();
2335-
2336+
} else if (OP2_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr))) {
2337+
variable_ptr = &EG(uninitialized_zval);
23362338
} else if (OP2_TYPE == IS_VAR &&
23372339
opline->extended_value == ZEND_RETURNS_FUNCTION &&
23382340
UNEXPECTED(!Z_ISREF_P(value_ptr))) {
@@ -2343,22 +2345,18 @@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV, SRC)
23432345
HANDLE_EXCEPTION();
23442346
}
23452347

2348+
/* op2 freed by assign_to_variable */
2349+
FREE_OP1_VAR_PTR();
2350+
ZEND_VM_NEXT_OPCODE();
23462351
} else {
2352+
zend_assign_to_variable_reference(variable_ptr, value_ptr);
2353+
}
23472354

2348-
if ((OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
2349-
(OP2_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
2350-
variable_ptr = &EG(uninitialized_zval);
2351-
} else {
2352-
zend_assign_to_variable_reference(variable_ptr, value_ptr);
2353-
}
2354-
2355-
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2356-
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
2357-
}
2358-
2359-
FREE_OP2_VAR_PTR();
2355+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2356+
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
23602357
}
23612358

2359+
FREE_OP2_VAR_PTR();
23622360
FREE_OP1_VAR_PTR();
23632361
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
23642362
}

Zend/zend_vm_execute.h

Lines changed: 56 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -26372,16 +26372,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_VAR_HANDLE
2637226372
value_ptr = _get_zval_ptr_ptr_var(opline->op2.var, &free_op2 EXECUTE_DATA_CC);
2637326373
variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
2637426374

26375-
if (IS_VAR == IS_VAR &&
26376-
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
26377-
UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
26375+
if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
26376+
variable_ptr = &EG(uninitialized_zval);
26377+
} else if (IS_VAR == IS_VAR &&
26378+
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT)) {
2637826379

2637926380
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
2638026381
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
2638126382
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
2638226383
UNDEF_RESULT();
2638326384
HANDLE_EXCEPTION();
26384-
26385+
} else if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr))) {
26386+
variable_ptr = &EG(uninitialized_zval);
2638526387
} else if (IS_VAR == IS_VAR &&
2638626388
opline->extended_value == ZEND_RETURNS_FUNCTION &&
2638726389
UNEXPECTED(!Z_ISREF_P(value_ptr))) {
@@ -26392,22 +26394,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_VAR_HANDLE
2639226394
HANDLE_EXCEPTION();
2639326395
}
2639426396

26397+
/* op2 freed by assign_to_variable */
26398+
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
26399+
ZEND_VM_NEXT_OPCODE();
2639526400
} else {
26401+
zend_assign_to_variable_reference(variable_ptr, value_ptr);
26402+
}
2639626403

26397-
if ((IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
26398-
(IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
26399-
variable_ptr = &EG(uninitialized_zval);
26400-
} else {
26401-
zend_assign_to_variable_reference(variable_ptr, value_ptr);
26402-
}
26403-
26404-
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
26405-
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
26406-
}
26407-
26408-
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
26404+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
26405+
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
2640926406
}
2641026407

26408+
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
2641126409
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
2641226410
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
2641326411
}
@@ -29425,16 +29423,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER
2942529423
value_ptr = _get_zval_ptr_cv_BP_VAR_W(opline->op2.var EXECUTE_DATA_CC);
2942629424
variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
2942729425

29428-
if (IS_VAR == IS_VAR &&
29429-
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
29430-
UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
29426+
if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
29427+
variable_ptr = &EG(uninitialized_zval);
29428+
} else if (IS_VAR == IS_VAR &&
29429+
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT)) {
2943129430

2943229431
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
2943329432
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
2943429433

2943529434
UNDEF_RESULT();
2943629435
HANDLE_EXCEPTION();
29437-
29436+
} else if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr))) {
29437+
variable_ptr = &EG(uninitialized_zval);
2943829438
} else if (IS_CV == IS_VAR &&
2943929439
opline->extended_value == ZEND_RETURNS_FUNCTION &&
2944029440
UNEXPECTED(!Z_ISREF_P(value_ptr))) {
@@ -29445,19 +29445,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER
2944529445
HANDLE_EXCEPTION();
2944629446
}
2944729447

29448+
/* op2 freed by assign_to_variable */
29449+
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
29450+
ZEND_VM_NEXT_OPCODE();
2944829451
} else {
29452+
zend_assign_to_variable_reference(variable_ptr, value_ptr);
29453+
}
2944929454

29450-
if ((IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
29451-
(IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
29452-
variable_ptr = &EG(uninitialized_zval);
29453-
} else {
29454-
zend_assign_to_variable_reference(variable_ptr, value_ptr);
29455-
}
29456-
29457-
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
29458-
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
29459-
}
29460-
29455+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
29456+
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
2946129457
}
2946229458

2946329459
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -45055,16 +45051,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER
4505545051
value_ptr = _get_zval_ptr_ptr_var(opline->op2.var, &free_op2 EXECUTE_DATA_CC);
4505645052
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(opline->op1.var EXECUTE_DATA_CC);
4505745053

45058-
if (IS_CV == IS_VAR &&
45059-
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
45060-
UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
45054+
if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
45055+
variable_ptr = &EG(uninitialized_zval);
45056+
} else if (IS_CV == IS_VAR &&
45057+
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT)) {
4506145058

4506245059
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
4506345060

4506445061
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
4506545062
UNDEF_RESULT();
4506645063
HANDLE_EXCEPTION();
45067-
45064+
} else if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr))) {
45065+
variable_ptr = &EG(uninitialized_zval);
4506845066
} else if (IS_VAR == IS_VAR &&
4506945067
opline->extended_value == ZEND_RETURNS_FUNCTION &&
4507045068
UNEXPECTED(!Z_ISREF_P(value_ptr))) {
@@ -45075,22 +45073,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER
4507545073
HANDLE_EXCEPTION();
4507645074
}
4507745075

45078-
} else {
45076+
/* op2 freed by assign_to_variable */
4507945077

45080-
if ((IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
45081-
(IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
45082-
variable_ptr = &EG(uninitialized_zval);
45083-
} else {
45084-
zend_assign_to_variable_reference(variable_ptr, value_ptr);
45085-
}
45086-
45087-
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
45088-
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
45089-
}
45078+
ZEND_VM_NEXT_OPCODE();
45079+
} else {
45080+
zend_assign_to_variable_reference(variable_ptr, value_ptr);
45081+
}
4509045082

45091-
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
45083+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
45084+
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
4509245085
}
4509345086

45087+
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
45088+
4509445089
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4509545090
}
4509645091

@@ -49567,16 +49562,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER(
4956749562
value_ptr = _get_zval_ptr_cv_BP_VAR_W(opline->op2.var EXECUTE_DATA_CC);
4956849563
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(opline->op1.var EXECUTE_DATA_CC);
4956949564

49570-
if (IS_CV == IS_VAR &&
49571-
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
49572-
UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
49565+
if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
49566+
variable_ptr = &EG(uninitialized_zval);
49567+
} else if (IS_CV == IS_VAR &&
49568+
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT)) {
4957349569

4957449570
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
4957549571

4957649572

4957749573
UNDEF_RESULT();
4957849574
HANDLE_EXCEPTION();
49579-
49575+
} else if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr))) {
49576+
variable_ptr = &EG(uninitialized_zval);
4958049577
} else if (IS_CV == IS_VAR &&
4958149578
opline->extended_value == ZEND_RETURNS_FUNCTION &&
4958249579
UNEXPECTED(!Z_ISREF_P(value_ptr))) {
@@ -49587,21 +49584,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER(
4958749584
HANDLE_EXCEPTION();
4958849585
}
4958949586

49590-
} else {
49591-
49592-
if ((IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
49593-
(IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
49594-
variable_ptr = &EG(uninitialized_zval);
49595-
} else {
49596-
zend_assign_to_variable_reference(variable_ptr, value_ptr);
49597-
}
49587+
/* op2 freed by assign_to_variable */
4959849588

49599-
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
49600-
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
49601-
}
49589+
ZEND_VM_NEXT_OPCODE();
49590+
} else {
49591+
zend_assign_to_variable_reference(variable_ptr, value_ptr);
49592+
}
4960249593

49594+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
49595+
ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
4960349596
}
4960449597

49598+
4960549599
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4960649600
}
4960749601

0 commit comments

Comments
 (0)