Skip to content

Commit 6925337

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

File tree

1 file changed

+61
-95
lines changed

1 file changed

+61
-95
lines changed

Zend/zend_execute.c

Lines changed: 61 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,10 @@ 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;
16411644

@@ -1646,15 +1649,17 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
16461649
case IS_STRING:
16471650
{
16481651
bool trailing_data = false;
1649-
/* For BC reasons we allow errors so that we can warn on leading numeric string */
1652+
/* For BC reasons we allow errors so that we can warn on leading numeric string,
1653+
* however, empty() and isset() never allowed errors */
1654+
bool allow_errors = (type != BP_VAR_IS) || is_coalesce;
16501655
if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL,
1651-
/* allow errors */ true, NULL, &trailing_data)) {
1656+
allow_errors, NULL, &trailing_data)) {
16521657
if (UNEXPECTED(trailing_data) && type != BP_VAR_UNSET) {
16531658
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
16541659
}
16551660
return offset;
16561661
}
1657-
zend_illegal_string_offset(dim, type);
1662+
goto invalid_type;
16581663
return 0;
16591664
}
16601665
case IS_UNDEF:
@@ -1664,13 +1669,25 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
16641669
case IS_NULL:
16651670
case IS_FALSE:
16661671
case IS_TRUE:
1667-
zend_error(E_WARNING, "String offset cast occurred");
1672+
if (type != BP_VAR_IS) {
1673+
zend_error(E_WARNING, "String offset cast occurred");
1674+
}
16681675
break;
16691676
case IS_REFERENCE:
16701677
dim = Z_REFVAL_P(dim);
16711678
goto try_again;
16721679
default:
1673-
zend_illegal_string_offset(dim, type);
1680+
invalid_type:
1681+
if (is_type_valid) {
1682+
*is_type_valid = false;
1683+
}
1684+
if (type != BP_VAR_IS) {
1685+
zend_illegal_string_offset(dim, type);
1686+
} else if (is_coalesce) {
1687+
/* is_coalesce used to have the same logic as BP_VAR_R */
1688+
zend_illegal_string_offset(dim, BP_VAR_R);
1689+
}
1690+
/* BP_VAR_IS emits no warning and throws noe exception */
16741691
return 0;
16751692
}
16761693

@@ -1759,19 +1776,22 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
17591776
if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
17601777
offset = Z_LVAL_P(dim);
17611778
} else {
1779+
bool is_type_valid = true;
17621780
/* The string may be destroyed while throwing the notice.
17631781
* 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)) {
1782+
if (!(GC_FLAGS(s) & IS_STR_INTERNED)) {
1783+
GC_ADDREF(s);
1784+
}
1785+
offset = zend_check_string_offset(dim, BP_VAR_W, &is_type_valid, /* is_coalesce */ false EXECUTE_DATA_CC);
1786+
if (!(GC_FLAGS(s) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(s) == 0)) {
17671787
zend_string_efree(s);
17681788
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
17691789
ZVAL_NULL(EX_VAR(opline->result.var));
17701790
}
17711791
return;
17721792
}
17731793
/* Illegal offset assignment */
1774-
if (UNEXPECTED(EG(exception) != NULL)) {
1794+
if (UNEXPECTED(!is_type_valid || EG(exception) != NULL)) {
17751795
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
17761796
ZVAL_UNDEF(EX_VAR(opline->result.var));
17771797
}
@@ -2322,7 +2342,7 @@ static ZEND_COLD void zend_binary_assign_op_dim_slow(zval *container, zval *dim
23222342
if (opline->op2_type == IS_UNUSED) {
23232343
zend_use_new_element_for_string();
23242344
} else {
2325-
zend_check_string_offset(dim, BP_VAR_RW EXECUTE_DATA_CC);
2345+
zend_check_string_offset(dim, BP_VAR_RW, /* is_type_valid */ NULL, /* is_coalesce */ false EXECUTE_DATA_CC);
23262346
zend_wrong_string_offset_error();
23272347
}
23282348
} else {
@@ -2624,7 +2644,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
26242644
if (dim == NULL) {
26252645
zend_use_new_element_for_string();
26262646
} else {
2627-
zend_check_string_offset(dim, type EXECUTE_DATA_CC);
2647+
zend_check_string_offset(dim, type, /* is_type_valid */ NULL, /* is_coalesce */ false EXECUTE_DATA_CC);
26282648
zend_wrong_string_offset_error();
26292649
}
26302650
ZVAL_UNDEF(result);
@@ -2747,73 +2767,27 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
27472767
zend_string *str = Z_STR_P(container);
27482768
zend_long offset;
27492769

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 {
2770+
if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
28142771
offset = Z_LVAL_P(dim);
2772+
} else {
2773+
bool is_type_valid = true;
2774+
/* The string may be destroyed while throwing the notice.
2775+
* Temporarily increase the refcount to detect this situation. */
2776+
if (!(GC_FLAGS(str) & IS_STR_INTERNED)) {
2777+
GC_ADDREF(str);
2778+
}
2779+
offset = zend_check_string_offset(dim, type, &is_type_valid, /* is_coalesce */ true EXECUTE_DATA_CC);
2780+
if (!(GC_FLAGS(str) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(str) == 0)) {
2781+
zend_string_efree(str);
2782+
ZVAL_NULL(result);
2783+
return;
2784+
}
2785+
/* Illegal offset assignment */
2786+
if (UNEXPECTED(!is_type_valid || EG(exception) != NULL)) {
2787+
ZVAL_NULL(result);
2788+
return;
2789+
}
28152790
}
2816-
out:
28172791

28182792
if (UNEXPECTED(ZSTR_LEN(str) < ((offset < 0) ? -(size_t)offset : ((size_t)offset + 1)))) {
28192793
if (type != BP_VAR_IS) {
@@ -2954,16 +2928,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isset_dim_slow(zval *container,
29542928
return 0;
29552929
}
29562930
} 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;
2931+
bool is_type_valid = true;
2932+
lval = zend_check_string_offset(offset, BP_VAR_IS, &is_type_valid, /* is_coalesce */ false EXECUTE_DATA_CC);
2933+
if (!is_type_valid || UNEXPECTED(EG(exception) != NULL)) {
2934+
return false;
29652935
}
2966-
return 0;
2936+
goto str_offset;
29672937
}
29682938
} else {
29692939
return 0;
@@ -2993,16 +2963,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isempty_dim_slow(zval *containe
29932963
return 1;
29942964
}
29952965
} 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;
2966+
bool is_type_valid = true;
2967+
lval = zend_check_string_offset(offset, BP_VAR_IS, &is_type_valid, /* is_coalesce */ false EXECUTE_DATA_CC);
2968+
if (!is_type_valid || UNEXPECTED(EG(exception) != NULL)) {
2969+
return true;
30042970
}
3005-
return 1;
2971+
goto str_offset;
30062972
}
30072973
} else {
30082974
return 1;

0 commit comments

Comments
 (0)