Skip to content

Commit 855242b

Browse files
committed
New implementation (without leaks)
1 parent 2009d9a commit 855242b

File tree

2 files changed

+67
-55
lines changed

2 files changed

+67
-55
lines changed

Zend/zend_execute.c

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,7 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
15711571
{
15721572
char *string_value;
15731573
size_t string_len;
1574+
zend_long old_len = Z_STRLEN_P(str);
15741575
zend_long offset;
15751576

15761577
offset = zend_check_string_offset(dim, BP_VAR_W EXECUTE_DATA_CC);
@@ -1611,7 +1612,6 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
16111612

16121613
if ((size_t)offset >= Z_STRLEN_P(str)) {
16131614
/* Extend string if needed */
1614-
zend_long old_len = Z_STRLEN_P(str);
16151615
ZVAL_NEW_STR(str, zend_string_extend(Z_STR_P(str), offset + 1, 0));
16161616
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
16171617
Z_STRVAL_P(str)[offset+1] = 0;
@@ -1633,61 +1633,39 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
16331633
return;
16341634
}
16351635

1636+
/* -1 for the byte we are replacing */
1637+
zend_long new_len = old_len + string_len - 1;
16361638
if ((size_t)offset >= Z_STRLEN_P(str)) {
1637-
/* Extend string if needed */
1638-
zend_long old_len = Z_STRLEN_P(str);
1639+
/* Extend string */
16391640
ZVAL_NEW_STR(str, zend_string_extend(Z_STR_P(str), offset + string_len, 0));
16401641
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
1641-
ZVAL_NEW_STR(str, zend_string_init(strcat(Z_STRVAL_P(str), string_value), offset + string_len, 0));
1642+
memcpy(Z_STRVAL_P(str) + offset, string_value, string_len);
16421643
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
1643-
ZVAL_INTERNED_STR(EX_VAR(opline->result.var), zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
1644+
ZVAL_STR(EX_VAR(opline->result.var), zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
16441645
}
16451646
return;
16461647
} else if (!Z_REFCOUNTED_P(str)) {
1647-
ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
1648+
ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), new_len, 0));
16481649
} else if (Z_REFCOUNT_P(str) > 1) {
16491650
Z_DELREF_P(str);
1650-
ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
1651+
ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), new_len, 0));
16511652
} else {
16521653
zend_string_forget_hash_val(Z_STR_P(str));
16531654
}
16541655

1655-
// Buffer offset
1656-
int k = 0;
1657-
// Source offset
1658-
int i = 0;
1659-
char *buffer = emalloc(Z_STRLEN_P(str) + string_len - 1); // -1 as we replace a byte
1660-
char *source = Z_STRVAL_P(str);
1661-
// Append bytes from the source string to the buffer until the offset is reached
1662-
while (i < offset) {
1663-
buffer[k] = source[i];
1664-
i++;
1665-
k++;
1666-
}
1667-
i++; // Skip byte being replaced
1668-
// If not an empty string then append all the bytes from the value to the buffer
1656+
zend_string *tmp = zend_string_init(Z_STRVAL_P(str), new_len, 0);
16691657
if (string_len > 0) {
1670-
int j = 0;
1671-
while (string_value[j] != '\0') {
1672-
buffer[k] = string_value[j];
1673-
j++;
1674-
k++;
1675-
}
1676-
}
1677-
// Add remaining bytes from the source string.
1678-
while (source[i] != '\0') {
1679-
buffer[k] = source[i];
1680-
i++;
1681-
k++;
1658+
memcpy(ZSTR_VAL(tmp) + offset, string_value, string_len);
16821659
}
1683-
// Append NUL byte to make a valid C string.
1684-
buffer[k] = '\0';
1685-
ZVAL_NEW_STR(str, zend_string_init(buffer, Z_STRLEN_P(str) + string_len - 1, 0));
1660+
/* Copy after the replacement string, from the position of the initial string after the offset,
1661+
* for the remainder of the initial string (old length - offset) */
1662+
memcpy(ZSTR_VAL(tmp) + offset + string_len, Z_STRVAL_P(str) + offset + 1, old_len - offset);
1663+
memcpy(Z_STRVAL_P(str), ZSTR_VAL(tmp), ZSTR_LEN(tmp));
1664+
zend_string_release_ex(tmp, 0);
1665+
16861666
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
1687-
ZVAL_INTERNED_STR(EX_VAR(opline->result.var), zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
1667+
ZVAL_STR(EX_VAR(opline->result.var), zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
16881668
}
1689-
1690-
efree(buffer);
16911669
}
16921670

16931671
static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *ref)

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
862862
static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result)
863863
{
864864
zend_string *old_str;
865+
zend_long old_len = Z_STRLEN_P(str);
865866
zend_uchar c;
866867
size_t string_len;
867868
zend_long offset;
@@ -895,26 +896,51 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
895896
c = (zend_uchar)Z_STRVAL_P(value)[0];
896897
}
897898

898-
if (string_len == 0) {
899-
/* Error on empty input string */
900-
zend_error(E_WARNING, "Cannot assign an empty string to a string offset");
899+
if (offset < 0) { /* Handle negative offset */
900+
offset += (zend_long)Z_STRLEN_P(str);
901+
}
902+
903+
/* If it's a byte char replace byte directly */
904+
if (string_len == 1) {
905+
if ((size_t) offset >= Z_STRLEN_P(str)) {
906+
/* Extend string if needed */
907+
Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0);
908+
Z_TYPE_INFO_P(str) = IS_STRING_EX;
909+
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
910+
Z_STRVAL_P(str)[offset + 1] = 0;
911+
} else if (!Z_REFCOUNTED_P(str)) {
912+
old_str = Z_STR_P(str);
913+
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
914+
Z_TYPE_INFO_P(str) = IS_STRING_EX;
915+
zend_string_release(old_str);
916+
} else {
917+
SEPARATE_STRING(str);
918+
zend_string_forget_hash_val(Z_STR_P(str));
919+
}
920+
921+
Z_STRVAL_P(str)[offset] = c;
922+
901923
if (result) {
902-
ZVAL_NULL(result);
924+
/* Return the new character */
925+
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
903926
}
904927
return;
905928
}
906929

907-
if (offset < 0) { /* Handle negative offset */
908-
offset += (zend_long)Z_STRLEN_P(str);
909-
}
910-
930+
/* -1 for the byte we are replacing */
931+
zend_long new_len = old_len + string_len - 1;
911932
if ((size_t)offset >= Z_STRLEN_P(str)) {
912-
/* Extend string if needed */
913-
zend_long old_len = Z_STRLEN_P(str);
914-
Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0);
915-
Z_TYPE_INFO_P(str) = IS_STRING_EX;
933+
old_str = Z_STR_P(str);
934+
/* Extend string */
935+
ZVAL_NEW_STR(str, zend_string_extend(Z_STR_P(str), offset + string_len, 0));
916936
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
917-
Z_STRVAL_P(str)[offset+1] = 0;
937+
memcpy(Z_STRVAL_P(str) + offset, ZSTR_VAL(old_str), string_len);
938+
939+
zend_string_release(old_str);
940+
if (result) {
941+
ZVAL_STR(result, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
942+
}
943+
return;
918944
} else if (!Z_REFCOUNTED_P(str)) {
919945
old_str = Z_STR_P(str);
920946
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
@@ -925,11 +951,19 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
925951
zend_string_forget_hash_val(Z_STR_P(str));
926952
}
927953

928-
Z_STRVAL_P(str)[offset] = c;
954+
old_str = Z_STR_P(str);
955+
zend_string *tmp = zend_string_init(Z_STRVAL_P(str), new_len, 0);
956+
if (string_len > 0) {
957+
memcpy(ZSTR_VAL(tmp) + offset, ZSTR_VAL(old_str), string_len);
958+
}
959+
/* Copy after the replacement string, from the position of the initial string after the offset,
960+
* for the remainder of the initial string (old length - offset) */
961+
memcpy(ZSTR_VAL(tmp) + offset + string_len, Z_STRVAL_P(str) + offset + 1, old_len - offset);
962+
memcpy(Z_STRVAL_P(str), ZSTR_VAL(tmp), ZSTR_LEN(tmp));
963+
zend_string_release_ex(tmp, 0);
929964

930965
if (result) {
931-
/* Return the new character */
932-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
966+
ZVAL_STR(result, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0));
933967
}
934968
}
935969

0 commit comments

Comments
 (0)