-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Delay notice emission until end of opcode #12090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
--TEST-- | ||
Bug #78598: Changing array during undef index RW error segfaults | ||
--INI-- | ||
opcache.jit=0 | ||
--FILE-- | ||
<?php | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--TEST-- | ||
Delayed error 001 | ||
--INI-- | ||
opcache.jit=0 | ||
--FILE-- | ||
<?php | ||
$array[0][1] .= 'foo'; | ||
$array[2][3]++; | ||
$array[3][4]--; | ||
++$array[5][6]; | ||
--$array[7][8]; | ||
$array[9][10] += 42; | ||
?> | ||
--EXPECTF-- | ||
Warning: Undefined variable $array in %s on line %d | ||
|
||
Warning: Undefined array key 0 in %s on line %d | ||
|
||
Warning: Undefined array key 1 in %s on line %d | ||
|
||
Warning: Undefined array key 2 in %s on line %d | ||
|
||
Warning: Undefined array key 3 in %s on line %d | ||
|
||
Warning: Decrement on type null has no effect, this will change in the next major version of PHP in %s on line %d | ||
|
||
Warning: Undefined array key 3 in %s on line %d | ||
|
||
Warning: Undefined array key 4 in %s on line %d | ||
|
||
Warning: Undefined array key 5 in %s on line %d | ||
|
||
Warning: Undefined array key 6 in %s on line %d | ||
|
||
Warning: Decrement on type null has no effect, this will change in the next major version of PHP in %s on line %d | ||
|
||
Warning: Undefined array key 7 in %s on line %d | ||
|
||
Warning: Undefined array key 8 in %s on line %d | ||
|
||
Warning: Undefined array key 9 in %s on line %d | ||
|
||
Warning: Undefined array key 10 in %s on line %d |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,14 +118,21 @@ | |
typedef int (ZEND_FASTCALL *incdec_t)(zval *); | ||
|
||
#define get_zval_ptr(op_type, node, type) _get_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_zval_ptr_delayed(op_type, node, type) _get_zval_ptr_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_zval_ptr_deref(op_type, node, type) _get_zval_ptr_deref(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_zval_ptr_deref_delayed(op_type, node, type) _get_zval_ptr_deref_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_zval_ptr_undef(op_type, node, type) _get_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_zval_ptr_undef_delayed(op_type, node, type) _get_zval_ptr_undef_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_op_data_zval_ptr_r(op_type, node) _get_op_data_zval_ptr_r(op_type, node EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_op_data_zval_ptr_r_delayed(op_type, node) _get_op_data_zval_ptr_r_delayed(op_type, node EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_op_data_zval_ptr_deref_r(op_type, node) _get_op_data_zval_ptr_deref_r(op_type, node EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_op_data_zval_ptr_deref_r_delayed(op_type, node) _get_op_data_zval_ptr_deref_r_delayed(op_type, node EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_zval_ptr_ptr(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC) | ||
#define get_zval_ptr_ptr_undef(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC) | ||
#define get_obj_zval_ptr(op_type, node, type) _get_obj_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_obj_zval_ptr_delayed(op_type, node, type) _get_obj_zval_ptr_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_obj_zval_ptr_undef(op_type, node, type) _get_obj_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_obj_zval_ptr_undef_delayed(op_type, node, type) _get_obj_zval_ptr_undef_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) | ||
#define get_obj_zval_ptr_ptr(op_type, node, type) _get_obj_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC) | ||
|
||
#define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED) | ||
|
@@ -425,6 +432,14 @@ static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, int ty | |
} | ||
} | ||
|
||
static zend_always_inline zval *_get_zval_ptr_delayed(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT_DELAYED(opline, node); | ||
} | ||
return get_zval_ptr(op_type, node, type); | ||
} | ||
|
||
static zend_always_inline zval *_get_op_data_zval_ptr_r(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type & (IS_TMP_VAR|IS_VAR)) { | ||
|
@@ -445,6 +460,14 @@ static zend_always_inline zval *_get_op_data_zval_ptr_r(int op_type, znode_op no | |
} | ||
} | ||
|
||
static zend_always_inline zval *_get_op_data_zval_ptr_r_delayed(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT(RT_CONSTANT_DELAYED_OPLINE(opline)+1, (opline+1)->op1); | ||
} | ||
return get_op_data_zval_ptr_r(op_type, node); | ||
} | ||
|
||
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type & (IS_TMP_VAR|IS_VAR)) { | ||
|
@@ -465,6 +488,14 @@ static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref(int op | |
} | ||
} | ||
|
||
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref_delayed(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT_DELAYED(opline, node); | ||
} | ||
return get_zval_ptr_deref(op_type, node, type); | ||
} | ||
|
||
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_deref_r(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type & (IS_TMP_VAR|IS_VAR)) { | ||
|
@@ -485,6 +516,14 @@ static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_dere | |
} | ||
} | ||
|
||
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_deref_r_delayed(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT(RT_CONSTANT_DELAYED_OPLINE(opline)+1, (opline+1)->op1); | ||
} | ||
return get_op_data_zval_ptr_deref_r(op_type, node); | ||
} | ||
|
||
static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type & (IS_TMP_VAR|IS_VAR)) { | ||
|
@@ -505,6 +544,14 @@ static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, | |
} | ||
} | ||
|
||
static zend_always_inline zval *_get_zval_ptr_undef_delayed(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT_DELAYED(opline, node); | ||
} | ||
return get_zval_ptr_undef(op_type, node, type); | ||
} | ||
|
||
static zend_always_inline zval *_get_zval_ptr_ptr_var(uint32_t var EXECUTE_DATA_DC) | ||
{ | ||
zval *ret = EX_VAR(var); | ||
|
@@ -533,6 +580,14 @@ static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr(int op_type, znode_o | |
return get_zval_ptr(op_type, op, type); | ||
} | ||
|
||
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_delayed(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT(opline, opline->op1); | ||
} | ||
return get_obj_zval_ptr(op_type, op, type); | ||
} | ||
|
||
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_UNUSED) { | ||
|
@@ -541,6 +596,14 @@ static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef(int op_type, z | |
return get_zval_ptr_undef(op_type, op, type); | ||
} | ||
|
||
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef_delayed(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC) | ||
{ | ||
if (op_type == IS_CONST) { | ||
return RT_CONSTANT(opline, opline->op1); | ||
} | ||
return get_obj_zval_ptr_undef(op_type, op, type); | ||
} | ||
|
||
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC) | ||
{ | ||
if (op_type == IS_UNUSED) { | ||
|
@@ -2226,6 +2289,28 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht | |
return zend_hash_index_add_new(ht, lval, &EG(uninitialized_zval)); | ||
} | ||
|
||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_undefined_offset_delayed(zend_long lval) | ||
{ | ||
zend_error_delayed(E_WARNING, "Undefined array key " ZEND_LONG_FMT, lval); | ||
} | ||
|
||
ZEND_API void ZEND_FASTCALL zend_handle_delayed_errors(void) | ||
{ | ||
/* Clear EG(delayed_errors), as more errors may be delayed while we are handling these. */ | ||
HashTable ht; | ||
memcpy(&ht, &EG(delayed_errors), sizeof(HashTable)); | ||
zend_hash_init(&EG(delayed_errors), 0, NULL, NULL, 0); | ||
|
||
zend_error_info *info; | ||
ZEND_HASH_FOREACH_PTR(&ht, info) { | ||
zend_error_zstr_at(info->type, info->filename, info->lineno, info->message); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May the first error throw an exception? Then the second. Which exception is going to be caught? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They could be chained, similar to here: https://3v4l.org/um5TH I'm not sure how this currently behaves. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm too :) |
||
zend_string_release(info->filename); | ||
zend_string_release(info->message); | ||
efree(info); | ||
} ZEND_HASH_FOREACH_END(); | ||
zend_hash_destroy(&ht); | ||
} | ||
|
||
ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht, zend_string *offset) | ||
{ | ||
zval *retval; | ||
|
@@ -2495,7 +2580,8 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht | |
retval = &EG(uninitialized_zval); | ||
break; | ||
case BP_VAR_RW: | ||
retval = zend_undefined_offset_write(ht, hval); | ||
zend_undefined_offset_delayed(hval); | ||
retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval)); | ||
break; | ||
} | ||
} else { | ||
|
@@ -3236,6 +3322,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c | |
|
||
if (prop_op_type == IS_CONST) { | ||
name = Z_STR_P(prop_ptr); | ||
tmp_name = NULL; | ||
} else { | ||
name = zval_get_tmp_string(prop_ptr, &tmp_name); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be dangerous to reuse
EG(opline_before_exception)
for delayed errors. I'm not sure how delayed errors and exceptions may interact. What if we had an exception before the delayed error?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably don't need to change the
EG(opline_before_exception) = EG(current_execute_data)->opline;
ifEG(current_execute_data)->opline == EG(exception_op)
, because all pending errors are handled before exceptions. But there might still be other issues.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here they are not handled but recorded, I can't be sure if some instruction can't throw exception and then emit delayed warning.
At least add
ZEND_ASSERT(!EG(exception))
to catch the possible problem.