Skip to content

Commit e833e5c

Browse files
committed
Merge branch 'PHP-8.0' into PHP-8.1
* PHP-8.0: Fix clobering of operand by error handler in assignment to string offset (optimization and JIT support)
2 parents 9786eac + 4595a57 commit e833e5c

File tree

2 files changed

+87
-34
lines changed

2 files changed

+87
-34
lines changed

Zend/zend_execute.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,11 +1684,9 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
16841684
} else {
16851685
/* The string may be destroyed while throwing the notice.
16861686
* Temporarily increase the refcount to detect this situation. */
1687-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) {
1688-
GC_ADDREF(s);
1689-
}
1687+
GC_ADDREF(s);
16901688
offset = zend_check_string_offset(dim, BP_VAR_W EXECUTE_DATA_CC);
1691-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) {
1689+
if (GC_DELREF(s) == 0) {
16921690
zend_string_efree(s);
16931691
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
16941692
ZVAL_NULL(EX_VAR(opline->result.var));
@@ -1718,17 +1716,17 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
17181716
}
17191717

17201718
if (UNEXPECTED(Z_TYPE_P(value) != IS_STRING)) {
1719+
zend_string *tmp;
1720+
17211721
/* The string may be destroyed while throwing the notice.
17221722
* Temporarily increase the refcount to detect this situation. */
1723-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) {
1724-
GC_ADDREF(s);
1725-
}
1723+
GC_ADDREF(s);
17261724
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
17271725
zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC);
17281726
}
17291727
/* Convert to string, just the time to pick the 1st byte */
1730-
zend_string *tmp = zval_try_get_string_func(value);
1731-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) {
1728+
tmp = zval_try_get_string_func(value);
1729+
if (GC_DELREF(s) == 0) {
17321730
zend_string_efree(s);
17331731
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
17341732
ZVAL_NULL(EX_VAR(opline->result.var));
@@ -1762,11 +1760,9 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
17621760

17631761
/* The string may be destroyed while throwing the notice.
17641762
* Temporarily increase the refcount to detect this situation. */
1765-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) {
1766-
GC_ADDREF(s);
1767-
}
1763+
GC_ADDREF(s);
17681764
zend_error(E_WARNING, "Only the first byte will be assigned to the string offset");
1769-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) {
1765+
if (GC_DELREF(s) == 0) {
17701766
zend_string_efree(s);
17711767
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
17721768
ZVAL_NULL(EX_VAR(opline->result.var));

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -936,13 +936,32 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_obj_is_helper(zval *container, zval
936936

937937
static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result)
938938
{
939-
zend_string *old_str;
940939
zend_uchar c;
941940
size_t string_len;
942941
zend_long offset;
942+
zend_string *s;
943+
944+
/* separate string */
945+
if (Z_REFCOUNTED_P(str) && Z_REFCOUNT_P(str) == 1) {
946+
s = Z_STR_P(str);
947+
} else {
948+
s = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
949+
ZSTR_H(s) = ZSTR_H(Z_STR_P(str));
950+
ZVAL_NEW_STR(str, s);
951+
}
943952

944953
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
954+
/* The string may be destroyed while throwing the notice.
955+
* Temporarily increase the refcount to detect this situation. */
956+
GC_ADDREF(s);
945957
offset = zend_check_string_offset(dim/*, BP_VAR_W*/);
958+
if (GC_DELREF(s) == 0) {
959+
zend_string_efree(s);
960+
if (result) {
961+
ZVAL_NULL(result);
962+
}
963+
return;
964+
}
946965
if (UNEXPECTED(EG(exception) != NULL)) {
947966
if (UNEXPECTED(result)) {
948967
ZVAL_UNDEF(result);
@@ -952,7 +971,7 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
952971
} else {
953972
offset = Z_LVAL_P(dim);
954973
}
955-
if (offset < -(zend_long)Z_STRLEN_P(str)) {
974+
if (offset < -(zend_long)ZSTR_LEN(s)) {
956975
/* Error on negative offset */
957976
zend_error(E_WARNING, "Illegal string offset " ZEND_LONG_FMT, offset);
958977
if (result) {
@@ -962,8 +981,35 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
962981
}
963982

964983
if (Z_TYPE_P(value) != IS_STRING) {
984+
zend_string *tmp;
985+
986+
/* The string may be destroyed while throwing the notice.
987+
* Temporarily increase the refcount to detect this situation. */
988+
GC_ADDREF(s);
989+
990+
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
991+
const zend_op *op_data = EG(current_execute_data)->opline + 1;
992+
ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
993+
zend_jit_undefined_op_helper(op_data->op1.var);
994+
value = &EG(uninitialized_zval);
995+
}
996+
965997
/* Convert to string, just the time to pick the 1st byte */
966-
zend_string *tmp = zval_try_get_string_func(value);
998+
tmp = zval_try_get_string_func(value);
999+
1000+
if (GC_DELREF(s) == 0) {
1001+
zend_string_efree(s);
1002+
if (result) {
1003+
ZVAL_NULL(result);
1004+
}
1005+
return;
1006+
}
1007+
if (UNEXPECTED(!tmp)) {
1008+
if (result) {
1009+
ZVAL_UNDEF(result);
1010+
}
1011+
return;
1012+
}
9671013

9681014
if (UNEXPECTED(!tmp)) {
9691015
if (result) {
@@ -991,27 +1037,37 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
9911037
return;
9921038
}
9931039

1040+
/* The string may be destroyed while throwing the notice.
1041+
* Temporarily increase the refcount to detect this situation. */
1042+
GC_ADDREF(s);
9941043
zend_error(E_WARNING, "Only the first byte will be assigned to the string offset");
1044+
if (GC_DELREF(s) == 0) {
1045+
zend_string_efree(s);
1046+
if (result) {
1047+
ZVAL_NULL(result);
1048+
}
1049+
return;
1050+
}
1051+
/* Illegal offset assignment */
1052+
if (UNEXPECTED(EG(exception) != NULL)) {
1053+
if (result) {
1054+
ZVAL_UNDEF(result);
1055+
}
1056+
return;
1057+
}
9951058
}
9961059

9971060
if (offset < 0) { /* Handle negative offset */
998-
offset += (zend_long)Z_STRLEN_P(str);
1061+
offset += (zend_long)ZSTR_LEN(s);
9991062
}
10001063

1001-
if ((size_t)offset >= Z_STRLEN_P(str)) {
1064+
if ((size_t)offset >= ZSTR_LEN(s)) {
10021065
/* Extend string if needed */
1003-
zend_long old_len = Z_STRLEN_P(str);
1004-
Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0);
1005-
Z_TYPE_INFO_P(str) = IS_STRING_EX;
1066+
zend_long old_len = ZSTR_LEN(s);
1067+
ZVAL_NEW_STR(str, zend_string_extend(s, (size_t)offset + 1, 0));
10061068
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
10071069
Z_STRVAL_P(str)[offset+1] = 0;
1008-
} else if (!Z_REFCOUNTED_P(str)) {
1009-
old_str = Z_STR_P(str);
1010-
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
1011-
Z_TYPE_INFO_P(str) = IS_STRING_EX;
1012-
zend_string_release(old_str);
10131070
} else {
1014-
SEPARATE_STRING(str);
10151071
zend_string_forget_hash_val(Z_STR_P(str));
10161072
}
10171073

@@ -1112,6 +1168,11 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_obj_rw_helper(zval *object_ptr, zva
11121168

11131169
static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim, zval *value, zval *result)
11141170
{
1171+
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) && EXPECTED(dim != NULL)) {
1172+
zend_assign_to_string_offset(object_ptr, dim, value, result);
1173+
return;
1174+
}
1175+
11151176
if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
11161177
const zend_op *opline = EG(current_execute_data)->opline;
11171178
zend_jit_undefined_op_helper(opline->op2.var);
@@ -1136,13 +1197,9 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim
11361197
}
11371198
}
11381199
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
1139-
if (!dim) {
1140-
zend_throw_error(NULL, "[] operator not supported for strings");
1141-
if (result) {
1142-
ZVAL_UNDEF(result);
1143-
}
1144-
} else {
1145-
zend_assign_to_string_offset(object_ptr, dim, value, result);
1200+
zend_throw_error(NULL, "[] operator not supported for strings");
1201+
if (result) {
1202+
ZVAL_UNDEF(result);
11461203
}
11471204
} else if (Z_TYPE_P(object_ptr) == IS_FALSE) {
11481205
zend_false_to_array_deprecated();

0 commit comments

Comments
 (0)