-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix bug 71572 #1761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bug 71572 #1761
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--TEST-- | ||
Bug #71572: String offset assignment from an empty string inserts null byte | ||
--FILE-- | ||
<?php | ||
|
||
$str = "abc"; | ||
var_dump($str{0} = ""); | ||
var_dump($str{1} = ""); | ||
var_dump($str{3} = ""); | ||
var_dump($str{10} = ""); | ||
var_dump($str); | ||
?> | ||
==DONE== | ||
--EXPECTF-- | ||
Warning: Cannot assign an empty string to a string offset in %s on line %d | ||
NULL | ||
|
||
Warning: Cannot assign an empty string to a string offset in %s on line %d | ||
NULL | ||
|
||
Warning: Cannot assign an empty string to a string offset in %s on line %d | ||
NULL | ||
|
||
Warning: Cannot assign an empty string to a string offset in %s on line %d | ||
NULL | ||
string(3) "abc" | ||
==DONE== |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1327,8 +1327,10 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval * | |
static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *value, zval *result) | ||
{ | ||
zend_string *old_str; | ||
zend_uchar c; | ||
size_t string_len; | ||
|
||
if (offset < 0) { | ||
if (offset < 0) { /* Error on negative offset */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this meant to be a new line? Using a tab here is strange. |
||
zend_error(E_WARNING, "Illegal string offset: " ZEND_LONG_FMT, offset); | ||
zend_string_release(Z_STR_P(str)); | ||
if (result) { | ||
|
@@ -1337,8 +1339,29 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu | |
return; | ||
} | ||
|
||
if (Z_TYPE_P(value) != IS_STRING) { | ||
/* Convert to string, just the time to pick the 1st byte */ | ||
zend_string *tmp = zval_get_string(value); | ||
|
||
string_len = ZSTR_LEN(tmp); | ||
c = (zend_uchar)ZSTR_VAL(tmp)[0]; | ||
zend_string_release(tmp); | ||
} else { | ||
string_len = Z_STRLEN_P(value); | ||
c = (zend_uchar)Z_STRVAL_P(value)[0]; | ||
} | ||
|
||
if (string_len == 0) { /* Error on empty input string */ | ||
zend_error(E_WARNING, "Cannot assign an empty string to a string offset"); | ||
zend_string_release(Z_STR_P(str)); | ||
if (result) { | ||
ZVAL_NULL(result); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure about this return value, we should cross check it with other assignment failure cases. I think we should be either returning an empty string here (the RHS) or the character at the modified offset of the string (the "new" LHS). |
||
} | ||
return; | ||
} | ||
|
||
old_str = Z_STR_P(str); | ||
if ((size_t)offset >= Z_STRLEN_P(str)) { | ||
if ((size_t)offset >= Z_STRLEN_P(str)) { /* Extend string if needed */ | ||
zend_long old_len = Z_STRLEN_P(str); | ||
Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0); | ||
Z_TYPE_INFO_P(str) = IS_STRING_EX; | ||
|
@@ -1349,27 +1372,19 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu | |
Z_TYPE_INFO_P(str) = IS_STRING_EX; | ||
} | ||
|
||
if (Z_TYPE_P(value) != IS_STRING) { | ||
zend_string *tmp = zval_get_string(value); | ||
Z_STRVAL_P(str)[offset] = c; | ||
|
||
Z_STRVAL_P(str)[offset] = ZSTR_VAL(tmp)[0]; | ||
zend_string_release(tmp); | ||
} else { | ||
Z_STRVAL_P(str)[offset] = Z_STRVAL_P(value)[0]; | ||
} | ||
/* | ||
* the value of an assignment to a string offset is undefined | ||
T(result->u.var).var = &T->str_offset.str; | ||
*/ | ||
|
||
zend_string_release(old_str); | ||
if (result) { | ||
zend_uchar c = (zend_uchar)Z_STRVAL_P(str)[offset]; | ||
|
||
if (result) { /* Return the new character */ | ||
if (CG(one_char_string)[c]) { | ||
ZVAL_INTERNED_STR(result, CG(one_char_string)[c]); | ||
} else { | ||
ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(str) + offset, 1, 0)); | ||
ZVAL_NEW_STR(result, zend_string_init((char *)(&c), 1, 0)); | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a test case for
$str{10} = ""
as well. Currently this will still extend the string, and likely additionally leave one uninitialized byte.