diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 966297a39636d..57d5cf887aca9 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3330,8 +3330,6 @@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM) zend_string **rope; zval *var, *ret; uint32_t i; - size_t len = 0; - char *target; rope = (zend_string**)EX_VAR(opline->op1.var); if (OP2_TYPE == IS_CONST) { @@ -3364,12 +3362,18 @@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM) } } } + + size_t len = 0; + uint32_t flags = ZSTR_COPYABLE_CONCAT_PROPERTIES; for (i = 0; i <= opline->extended_value; i++) { + flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(rope[i]); len += ZSTR_LEN(rope[i]); } ret = EX_VAR(opline->result.var); ZVAL_STR(ret, zend_string_alloc(len, 0)); - target = Z_STRVAL_P(ret); + GC_ADD_FLAGS(Z_STR_P(ret), flags); + + char *target = Z_STRVAL_P(ret); for (i = 0; i <= opline->extended_value; i++) { memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i])); target += ZSTR_LEN(rope[i]); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ef1219ab7d896..95e66b0cac3a7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -19867,8 +19867,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CONST_HANDLE zend_string **rope; zval *var, *ret; uint32_t i; - size_t len = 0; - char *target; rope = (zend_string**)EX_VAR(opline->op1.var); if (IS_CONST == IS_CONST) { @@ -19901,12 +19899,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CONST_HANDLE } } } + + size_t len = 0; + uint32_t flags = ZSTR_COPYABLE_CONCAT_PROPERTIES; for (i = 0; i <= opline->extended_value; i++) { + flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(rope[i]); len += ZSTR_LEN(rope[i]); } ret = EX_VAR(opline->result.var); ZVAL_STR(ret, zend_string_alloc(len, 0)); - target = Z_STRVAL_P(ret); + GC_ADD_FLAGS(Z_STR_P(ret), flags); + + char *target = Z_STRVAL_P(ret); for (i = 0; i <= opline->extended_value; i++) { memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i])); target += ZSTR_LEN(rope[i]); @@ -20344,8 +20348,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDL zend_string **rope; zval *var, *ret; uint32_t i; - size_t len = 0; - char *target; rope = (zend_string**)EX_VAR(opline->op1.var); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { @@ -20378,12 +20380,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDL } } } + + size_t len = 0; + uint32_t flags = ZSTR_COPYABLE_CONCAT_PROPERTIES; for (i = 0; i <= opline->extended_value; i++) { + flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(rope[i]); len += ZSTR_LEN(rope[i]); } ret = EX_VAR(opline->result.var); ZVAL_STR(ret, zend_string_alloc(len, 0)); - target = Z_STRVAL_P(ret); + GC_ADD_FLAGS(Z_STR_P(ret), flags); + + char *target = Z_STRVAL_P(ret); for (i = 0; i <= opline->extended_value; i++) { memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i])); target += ZSTR_LEN(rope[i]); @@ -21205,8 +21213,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CV_HANDLER(Z zend_string **rope; zval *var, *ret; uint32_t i; - size_t len = 0; - char *target; rope = (zend_string**)EX_VAR(opline->op1.var); if (IS_CV == IS_CONST) { @@ -21239,12 +21245,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CV_HANDLER(Z } } } + + size_t len = 0; + uint32_t flags = ZSTR_COPYABLE_CONCAT_PROPERTIES; for (i = 0; i <= opline->extended_value; i++) { + flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(rope[i]); len += ZSTR_LEN(rope[i]); } ret = EX_VAR(opline->result.var); ZVAL_STR(ret, zend_string_alloc(len, 0)); - target = Z_STRVAL_P(ret); + GC_ADD_FLAGS(Z_STR_P(ret), flags); + + char *target = Z_STRVAL_P(ret); for (i = 0; i <= opline->extended_value; i++) { memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i])); target += ZSTR_LEN(rope[i]); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 7636c7810531c..7c889a2ade8df 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3104,13 +3104,16 @@ static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t zend_string *ret; uint32_t i; size_t len = 0; - char *target; + uint32_t flags = ZSTR_COPYABLE_CONCAT_PROPERTIES; for (i = 0; i <= count; i++) { + flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(rope[i]); len += ZSTR_LEN(rope[i]); } ret = zend_string_alloc(len, 0); - target = ZSTR_VAL(ret); + GC_ADD_FLAGS(ret, flags); + + char *target = ZSTR_VAL(ret); for (i = 0; i <= count; i++) { memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i])); target += ZSTR_LEN(rope[i]); diff --git a/ext/zend_test/tests/strings_marked_as_utf8.phpt b/ext/zend_test/tests/strings_marked_as_utf8.phpt index 9eaa43e63902c..6a32a27a491d1 100644 --- a/ext/zend_test/tests/strings_marked_as_utf8.phpt +++ b/ext/zend_test/tests/strings_marked_as_utf8.phpt @@ -107,6 +107,13 @@ $s = $o . $o; var_dump($s); var_dump(zend_test_is_string_marked_as_valid_utf8($s)); +echo "Rope concat:\n"; +$foo = 'f'; +$bar = 'b'; +$baz = 'a'; +$rope = "$foo$bar$baz"; +var_dump(zend_test_is_string_marked_as_valid_utf8($rope)); + echo "str_repeat:\n"; $string = "a"; $string_concat = str_repeat($string, 100); @@ -183,6 +190,8 @@ bool(true) Concatenation of objects: string(2) "zz" bool(true) +Rope concat: +bool(true) str_repeat: bool(true) bool(false) diff --git a/ext/zend_test/tests/strings_not_marked_as_utf8.phpt b/ext/zend_test/tests/strings_not_marked_as_utf8.phpt index 1a5210d39313f..d9115e8bd987e 100644 --- a/ext/zend_test/tests/strings_not_marked_as_utf8.phpt +++ b/ext/zend_test/tests/strings_not_marked_as_utf8.phpt @@ -102,6 +102,12 @@ $s = $o . $o; $s = $s . $non_utf8; var_dump(zend_test_is_string_marked_as_valid_utf8($s)); +echo "Rope concat:\n"; +$foo = 'f'; +$bar = "\xc3"; +$baz = 'a'; +$rope = "$foo$bar$baz"; +var_dump(zend_test_is_string_marked_as_valid_utf8($rope)); ?> --EXPECT-- Integer cast to string concatenated to invalid UTF-8: @@ -129,3 +135,5 @@ Concatenation in loop (compound assignment): bool(false) Concatenation of objects: bool(false) +Rope concat: +bool(false)