Skip to content

Commit cfd7da7

Browse files
committed
wip
1 parent 09a12d4 commit cfd7da7

File tree

6 files changed

+198
-57
lines changed

6 files changed

+198
-57
lines changed

Zend/tests/bug78598.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ $my_var[0] .= "xyz";
1212
var_dump($my_var);
1313

1414
$my_var = null;
15-
$my_var[0][0][0] .= "xyz";
15+
$my_var[0][0] .= "xyz";
1616
var_dump($my_var);
1717

1818
$my_var = null;

Zend/zend.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1718,7 +1718,8 @@ ZEND_API void zend_free_recorded_errors(void)
17181718
EG(num_errors) = 0;
17191719
}
17201720

1721-
ZEND_API ZEND_COLD void zend_error_delayed(int type, const char *format, ...) {
1721+
ZEND_API ZEND_COLD void zend_error_delayed(int type, const char *format, ...)
1722+
{
17221723
ZEND_ASSERT(!(type & E_FATAL_ERRORS) && "Cannot delay fatal error");
17231724
zend_error_info *info = emalloc(sizeof(zend_error_info));
17241725
info->type = type;

Zend/zend_execute.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2500,6 +2500,7 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
25002500
retval = &EG(uninitialized_zval);
25012501
break;
25022502
case BP_VAR_RW:
2503+
// retval = zend_undefined_offset_write(ht, hval);
25032504
zend_undefined_offset_delayed(hval);
25042505
retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
25052506
break;

Zend/zend_globals.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ struct _zend_executor_globals {
246246
zend_object *exception, *prev_exception;
247247
const zend_op *opline_before_exception;
248248
zend_op exception_op[3];
249+
/* FIXME: Is this safe for VM reentry? We may have to save/restore this value. */
250+
/* Index 1-2 are always set to HANDLE_DELAYED_ERROR. When emitting a delayed error, we set
251+
* EG(current_execute_data)->opline to index 0. Index 0 is used for delaying notices of
252+
* FETCH/ASSIGN sequences. If HANDLE_DELAYED_ERROR detects that the last opcode produced an
253+
* INDIRECT value it sets EG(delayed_error_op)[0] to the next opcode. After the opcode has
254+
* executed it advances once again to the HANDLE_DELAYED_ERROR opcode which will repeat this
255+
* check. If the last opcode did not produce an INDIRECT value, it can safely emit the warning. */
249256
zend_op delayed_error_op[3];
250257

251258
struct _zend_module_entry *current_module;

Zend/zend_vm_def.h

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,9 @@ ZEND_VM_C_LABEL(assign_dim_op_array):
11631163
SEPARATE_ARRAY(container);
11641164
ht = Z_ARRVAL_P(container);
11651165
ZEND_VM_C_LABEL(assign_dim_op_new_array):
1166-
dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
1166+
dim = OP2_TYPE == IS_CONST && opline == &EG(delayed_error_op)[0]
1167+
? RT_CONSTANT(EG(opline_before_exception), opline->op2)
1168+
: GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
11671169
if (OP2_TYPE == IS_UNUSED) {
11681170
var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval));
11691171
if (UNEXPECTED(!var_ptr)) {
@@ -1210,7 +1212,9 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array):
12101212
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
12111213
zend_object *obj = Z_OBJ_P(container);
12121214

1213-
dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
1215+
dim = OP2_TYPE == IS_CONST && opline == &EG(delayed_error_op)[0]
1216+
? RT_CONSTANT(EG(opline_before_exception), opline->op2)
1217+
: GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
12141218
if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
12151219
dim++;
12161220
}
@@ -1234,7 +1238,9 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array):
12341238
}
12351239
ZEND_VM_C_GOTO(assign_dim_op_new_array);
12361240
} else {
1237-
dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
1241+
dim = OP2_TYPE == IS_CONST && opline == &EG(delayed_error_op)[0]
1242+
? RT_CONSTANT(EG(opline_before_exception), opline->op2)
1243+
: GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
12381244
zend_binary_assign_op_dim_slow(container, dim OPLINE_CC EXECUTE_DATA_CC);
12391245
ZEND_VM_C_LABEL(assign_dim_op_ret_null):
12401246
FREE_OP_DATA();
@@ -8092,26 +8098,65 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
80928098

80938099
ZEND_VM_HANDLER(204, ZEND_HANDLE_DELAYED_ERROR, ANY, ANY)
80948100
{
8095-
const zend_op *next_op = EG(opline_before_exception) + 1;
8101+
const zend_op *prev_op = EG(opline_before_exception);
8102+
bool delay = false;
8103+
switch (prev_op->opcode) {
8104+
case ZEND_FETCH_W:
8105+
case ZEND_FETCH_RW:
8106+
case ZEND_FETCH_FUNC_ARG:
8107+
case ZEND_FETCH_UNSET:
8108+
case ZEND_FETCH_DIM_W:
8109+
case ZEND_FETCH_DIM_RW:
8110+
case ZEND_FETCH_DIM_UNSET:
8111+
case ZEND_FETCH_LIST_W:
8112+
case ZEND_FETCH_OBJ_W:
8113+
case ZEND_FETCH_OBJ_RW:
8114+
case ZEND_FETCH_OBJ_UNSET:
8115+
delay = true;
8116+
break;
8117+
}
8118+
8119+
// FIXME: Is this guaranteed to be there?
8120+
const zend_op *next_op = prev_op + 1;
80968121
if (next_op->opcode == ZEND_OP_DATA) {
80978122
next_op++;
80988123
}
80998124

8100-
/* Clear EG(delayed_errors), as more errors may be delayed while we are handling these. */
8101-
HashTable ht;
8102-
memcpy(&ht, &EG(delayed_errors), sizeof(HashTable));
8103-
zend_hash_init(&EG(delayed_errors), 0, NULL, NULL, 0);
8125+
if (delay) {
8126+
EG(delayed_error_op)[0] = *next_op;
8127+
// FIXME: Is this guaranteed to be there?
8128+
if (next_op[1].opcode == ZEND_OP_DATA) {
8129+
EG(delayed_error_op)[1] = next_op[1];
8130+
} else {
8131+
/* Reset to ZEND_HANDLE_DELAYED_ERROR */
8132+
EG(delayed_error_op)[1] = EG(delayed_error_op)[2];
8133+
}
8134+
EG(opline_before_exception) = next_op;
8135+
EG(current_execute_data)->opline = EG(delayed_error_op);
81048136

8105-
zend_error_info *info;
8106-
ZEND_HASH_FOREACH_PTR(&ht, info) {
8107-
zend_error_zstr_at(info->type, info->filename, info->lineno, info->message);
8108-
zend_string_release(info->filename);
8109-
zend_string_release(info->message);
8110-
efree(info);
8111-
} ZEND_HASH_FOREACH_END();
8112-
zend_hash_destroy(&ht);
8137+
ZEND_VM_SET_NEXT_OPCODE(EG(delayed_error_op));
8138+
} else {
8139+
/* Clear EG(delayed_errors), as more errors may be delayed while we are handling these. */
8140+
HashTable ht;
8141+
memcpy(&ht, &EG(delayed_errors), sizeof(HashTable));
8142+
zend_hash_init(&EG(delayed_errors), 0, NULL, NULL, 0);
8143+
8144+
zend_error_info *info;
8145+
ZEND_HASH_FOREACH_PTR(&ht, info) {
8146+
zend_error_zstr_at(info->type, info->filename, info->lineno, info->message);
8147+
zend_string_release(info->filename);
8148+
zend_string_release(info->message);
8149+
efree(info);
8150+
} ZEND_HASH_FOREACH_END();
8151+
zend_hash_destroy(&ht);
8152+
8153+
if (EG(exception)) {
8154+
HANDLE_EXCEPTION();
8155+
}
8156+
8157+
ZEND_VM_SET_NEXT_OPCODE(next_op);
8158+
}
81138159

8114-
ZEND_VM_SET_NEXT_OPCODE(next_op);
81158160
ZEND_VM_CONTINUE();
81168161
}
81178162

0 commit comments

Comments
 (0)