Skip to content

Commit 142a70b

Browse files
committed
Zend/zend_execute.c: use zend_check_string_offset() for all string offsets
1 parent 90bab1e commit 142a70b

File tree

1 file changed

+69
-96
lines changed

1 file changed

+69
-96
lines changed

Zend/zend_execute.c

Lines changed: 69 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,9 +1635,14 @@ static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_inf
16351635
}
16361636
}
16371637

1638-
static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type EXECUTE_DATA_DC)
1638+
/* Compared to the behaviour of array offsets, isset()/empty() did not throw
1639+
* TypeErrors for invalid offsets, or warn on type coercions.
1640+
* The coalesce operator did throw on invalid offset types but not for type coercions so we need to be aware of it. */
1641+
static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type, bool *is_type_valid, bool is_coalesce EXECUTE_DATA_DC)
16391642
{
16401643
zend_long offset;
1644+
/* isset()/empty() are more strict in what they allow/warn */
1645+
bool allow_errors = (type != BP_VAR_IS) || is_coalesce;
16411646

16421647
try_again:
16431648
switch(Z_TYPE_P(dim)) {
@@ -1646,15 +1651,22 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
16461651
case IS_STRING:
16471652
{
16481653
bool trailing_data = false;
1649-
/* For BC reasons we allow errors so that we can warn on leading numeric string */
1654+
/* For BC reasons we allow errors so that we can warn on leading numeric string,
1655+
* however, empty() and isset() never allowed errors */
16501656
if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL,
1651-
/* allow errors */ true, NULL, &trailing_data)) {
1657+
allow_errors, NULL, &trailing_data)) {
16521658
if (UNEXPECTED(trailing_data) && type != BP_VAR_UNSET) {
16531659
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
16541660
}
16551661
return offset;
16561662
}
1657-
zend_illegal_string_offset(dim, type);
1663+
if (is_type_valid) {
1664+
*is_type_valid = false;
1665+
}
1666+
if (type != BP_VAR_IS) {
1667+
zend_illegal_string_offset(dim, type);
1668+
}
1669+
/* BP_VAR_IS emits no warning and throws no exception */
16581670
return 0;
16591671
}
16601672
case IS_UNDEF:
@@ -1664,17 +1676,29 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
16641676
case IS_NULL:
16651677
case IS_FALSE:
16661678
case IS_TRUE:
1667-
zend_error(E_WARNING, "String offset cast occurred");
1679+
if (type != BP_VAR_IS) {
1680+
zend_error(E_WARNING, "String offset cast occurred");
1681+
}
16681682
break;
16691683
case IS_REFERENCE:
16701684
dim = Z_REFVAL_P(dim);
16711685
goto try_again;
16721686
default:
1673-
zend_illegal_string_offset(dim, type);
1687+
if (is_type_valid) {
1688+
*is_type_valid = false;
1689+
}
1690+
if (type != BP_VAR_IS) {
1691+
zend_illegal_string_offset(dim, type);
1692+
} else if (is_coalesce) {
1693+
/* is_coalesce used to have the same logic as BP_VAR_R */
1694+
zend_illegal_string_offset(dim, BP_VAR_R);
1695+
}
1696+
/* isset()/empty() emits no warning and throws no exception */
16741697
return 0;
16751698
}
16761699

1677-
return zval_get_long_func(dim, /* is_strict */ false);
1700+
/* Being strict here means that incompatible floats emit a deprecation notice */
1701+
return zval_get_long_func(dim, /* is_strict */ !allow_errors);
16781702
}
16791703

16801704
ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void)
@@ -1759,19 +1783,22 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
17591783
if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
17601784
offset = Z_LVAL_P(dim);
17611785
} else {
1786+
bool is_type_valid = true;
17621787
/* The string may be destroyed while throwing the notice.
17631788
* Temporarily increase the refcount to detect this situation. */
1764-
GC_ADDREF(s);
1765-
offset = zend_check_string_offset(dim, BP_VAR_W EXECUTE_DATA_CC);
1766-
if (UNEXPECTED(GC_DELREF(s) == 0)) {
1789+
if (!(GC_FLAGS(s) & IS_STR_INTERNED)) {
1790+
GC_ADDREF(s);
1791+
}
1792+
offset = zend_check_string_offset(dim, BP_VAR_W, &is_type_valid, /* is_coalesce */ false EXECUTE_DATA_CC);
1793+
if (!(GC_FLAGS(s) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(s) == 0)) {
17671794
zend_string_efree(s);
17681795
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
17691796
ZVAL_NULL(EX_VAR(opline->result.var));
17701797
}
17711798
return;
17721799
}
17731800
/* Illegal offset assignment */
1774-
if (UNEXPECTED(EG(exception) != NULL)) {
1801+
if (UNEXPECTED(!is_type_valid || EG(exception) != NULL)) {
17751802
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
17761803
ZVAL_UNDEF(EX_VAR(opline->result.var));
17771804
}
@@ -2322,7 +2349,7 @@ static ZEND_COLD void zend_binary_assign_op_dim_slow(zval *container, zval *dim
23222349
if (opline->op2_type == IS_UNUSED) {
23232350
zend_use_new_element_for_string();
23242351
} else {
2325-
zend_check_string_offset(dim, BP_VAR_RW EXECUTE_DATA_CC);
2352+
zend_check_string_offset(dim, BP_VAR_RW, /* is_type_valid */ NULL, /* is_coalesce */ false EXECUTE_DATA_CC);
23262353
zend_wrong_string_offset_error();
23272354
}
23282355
} else {
@@ -2624,7 +2651,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
26242651
if (dim == NULL) {
26252652
zend_use_new_element_for_string();
26262653
} else {
2627-
zend_check_string_offset(dim, type EXECUTE_DATA_CC);
2654+
zend_check_string_offset(dim, type, /* is_type_valid */ NULL, /* is_coalesce */ false EXECUTE_DATA_CC);
26282655
zend_wrong_string_offset_error();
26292656
}
26302657
ZVAL_UNDEF(result);
@@ -2747,73 +2774,27 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
27472774
zend_string *str = Z_STR_P(container);
27482775
zend_long offset;
27492776

2750-
try_string_offset:
2751-
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
2752-
switch (Z_TYPE_P(dim)) {
2753-
case IS_STRING:
2754-
{
2755-
bool trailing_data = false;
2756-
/* For BC reasons we allow errors so that we can warn on leading numeric string */
2757-
if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset,
2758-
NULL, /* allow errors */ true, NULL, &trailing_data)) {
2759-
if (UNEXPECTED(trailing_data)) {
2760-
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
2761-
}
2762-
goto out;
2763-
}
2764-
if (type == BP_VAR_IS) {
2765-
ZVAL_NULL(result);
2766-
return;
2767-
}
2768-
zend_illegal_string_offset(dim, BP_VAR_R);
2769-
ZVAL_NULL(result);
2770-
return;
2771-
}
2772-
case IS_UNDEF:
2773-
/* The string may be destroyed while throwing the notice.
2774-
* Temporarily increase the refcount to detect this situation. */
2775-
if (!(GC_FLAGS(str) & IS_STR_INTERNED)) {
2776-
GC_ADDREF(str);
2777-
}
2778-
ZVAL_UNDEFINED_OP2();
2779-
if (!(GC_FLAGS(str) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(str) == 0)) {
2780-
zend_string_efree(str);
2781-
ZVAL_NULL(result);
2782-
return;
2783-
}
2784-
ZEND_FALLTHROUGH;
2785-
case IS_DOUBLE:
2786-
case IS_NULL:
2787-
case IS_FALSE:
2788-
case IS_TRUE:
2789-
if (type != BP_VAR_IS) {
2790-
/* The string may be destroyed while throwing the notice.
2791-
* Temporarily increase the refcount to detect this situation. */
2792-
if (!(GC_FLAGS(str) & IS_STR_INTERNED)) {
2793-
GC_ADDREF(str);
2794-
}
2795-
zend_error(E_WARNING, "String offset cast occurred");
2796-
if (!(GC_FLAGS(str) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(str) == 0)) {
2797-
zend_string_efree(str);
2798-
ZVAL_NULL(result);
2799-
return;
2800-
}
2801-
}
2802-
break;
2803-
case IS_REFERENCE:
2804-
dim = Z_REFVAL_P(dim);
2805-
goto try_string_offset;
2806-
default:
2807-
zend_illegal_string_offset(dim, BP_VAR_R);
2808-
ZVAL_NULL(result);
2809-
return;
2810-
}
2811-
2812-
offset = zval_get_long_func(dim, /* is_strict */ false);
2813-
} else {
2777+
if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
28142778
offset = Z_LVAL_P(dim);
2779+
} else {
2780+
bool is_type_valid = true;
2781+
/* The string may be destroyed while throwing the notice.
2782+
* Temporarily increase the refcount to detect this situation. */
2783+
if (!(GC_FLAGS(str) & IS_STR_INTERNED)) {
2784+
GC_ADDREF(str);
2785+
}
2786+
offset = zend_check_string_offset(dim, type, &is_type_valid, /* is_coalesce */ true EXECUTE_DATA_CC);
2787+
if (!(GC_FLAGS(str) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(str) == 0)) {
2788+
zend_string_efree(str);
2789+
ZVAL_NULL(result);
2790+
return;
2791+
}
2792+
/* Illegal offset assignment */
2793+
if (UNEXPECTED(!is_type_valid || EG(exception) != NULL)) {
2794+
ZVAL_NULL(result);
2795+
return;
2796+
}
28152797
}
2816-
out:
28172798

28182799
if (UNEXPECTED(ZSTR_LEN(str) < ((offset < 0) ? -(size_t)offset : ((size_t)offset + 1)))) {
28192800
if (type != BP_VAR_IS) {
@@ -2954,16 +2935,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isset_dim_slow(zval *container,
29542935
return 0;
29552936
}
29562937
} else {
2957-
/*if (OP2_TYPE & (IS_CV|IS_VAR)) {*/
2958-
ZVAL_DEREF(offset);
2959-
/*}*/
2960-
if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */
2961-
|| (Z_TYPE_P(offset) == IS_STRING /* or numeric string */
2962-
&& IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) {
2963-
lval = zval_get_long_ex(offset, /* is_strict */ true);
2964-
goto str_offset;
2938+
bool is_type_valid = true;
2939+
lval = zend_check_string_offset(offset, BP_VAR_IS, &is_type_valid, /* is_coalesce */ false EXECUTE_DATA_CC);
2940+
if (!is_type_valid || UNEXPECTED(EG(exception) != NULL)) {
2941+
return false;
29652942
}
2966-
return 0;
2943+
goto str_offset;
29672944
}
29682945
} else {
29692946
return 0;
@@ -2993,16 +2970,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isempty_dim_slow(zval *containe
29932970
return 1;
29942971
}
29952972
} else {
2996-
/*if (OP2_TYPE & (IS_CV|IS_VAR)) {*/
2997-
ZVAL_DEREF(offset);
2998-
/*}*/
2999-
if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */
3000-
|| (Z_TYPE_P(offset) == IS_STRING /* or numeric string */
3001-
&& IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) {
3002-
lval = zval_get_long_ex(offset, /* is_strict */ true);
3003-
goto str_offset;
2973+
bool is_type_valid = true;
2974+
lval = zend_check_string_offset(offset, BP_VAR_IS, &is_type_valid, /* is_coalesce */ false EXECUTE_DATA_CC);
2975+
if (!is_type_valid || UNEXPECTED(EG(exception) != NULL)) {
2976+
return true;
30042977
}
3005-
return 1;
2978+
goto str_offset;
30062979
}
30072980
} else {
30082981
return 1;

0 commit comments

Comments
 (0)