Skip to content

Commit 2384112

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

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
@@ -1610,11 +1610,9 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
16101610
} else {
16111611
/* The string may be destroyed while throwing the notice.
16121612
* Temporarily increase the refcount to detect this situation. */
1613-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) {
1614-
GC_ADDREF(s);
1615-
}
1613+
GC_ADDREF(s);
16161614
offset = zend_check_string_offset(dim, BP_VAR_W EXECUTE_DATA_CC);
1617-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) {
1615+
if (GC_DELREF(s) == 0) {
16181616
zend_string_efree(s);
16191617
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
16201618
ZVAL_NULL(EX_VAR(opline->result.var));
@@ -1644,17 +1642,17 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
16441642
}
16451643

16461644
if (UNEXPECTED(Z_TYPE_P(value) != IS_STRING)) {
1645+
zend_string *tmp;
1646+
16471647
/* The string may be destroyed while throwing the notice.
16481648
* Temporarily increase the refcount to detect this situation. */
1649-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) {
1650-
GC_ADDREF(s);
1651-
}
1649+
GC_ADDREF(s);
16521650
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
16531651
zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC);
16541652
}
16551653
/* Convert to string, just the time to pick the 1st byte */
1656-
zend_string *tmp = zval_try_get_string_func(value);
1657-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) {
1654+
tmp = zval_try_get_string_func(value);
1655+
if (GC_DELREF(s) == 0) {
16581656
zend_string_efree(s);
16591657
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
16601658
ZVAL_NULL(EX_VAR(opline->result.var));
@@ -1688,11 +1686,9 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
16881686

16891687
/* The string may be destroyed while throwing the notice.
16901688
* Temporarily increase the refcount to detect this situation. */
1691-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) {
1692-
GC_ADDREF(s);
1693-
}
1689+
GC_ADDREF(s);
16941690
zend_error(E_WARNING, "Only the first byte will be assigned to the string offset");
1695-
if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) {
1691+
if (GC_DELREF(s) == 0) {
16961692
zend_string_efree(s);
16971693
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
16981694
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)