diff --git a/NEWS b/NEWS index 5f4b19f6fa865..5d3c1e46ce304 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,10 @@ PHP NEWS . Fix GH-16136 (Memory leak in php_ldap_do_modify() when entry is not a proper dictionary). (Girgias) +- MBString: + . Fixed bug GH-16261 (Reference invariant broken in mb_convert_variables()). + (nielsdos) + - OpenSSL: . Fixed stub for openssl_csr_new. (Jakub Zelenka) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 0b362309ca438..70125dc909748 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3289,7 +3289,7 @@ static int mb_recursive_convert_variable(mbfl_buffer_converter *convd, zval *var if (ret != NULL) { zval_ptr_dtor(orig_var); // TODO: avoid reallocation ??? - ZVAL_STRINGL(orig_var, (char *)ret->val, ret->len); + ZVAL_STRINGL(orig_var, (const char *) ret->val, ret->len); efree(ret->val); } } else if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) { @@ -3305,7 +3305,22 @@ static int mb_recursive_convert_variable(mbfl_buffer_converter *convd, zval *var ht = HASH_OF(var); if (ht != NULL) { - ZEND_HASH_FOREACH_VAL_IND(ht, entry) { + ZEND_HASH_FOREACH_VAL(ht, entry) { + /* Can be a typed property declaration, in which case we need to remove the reference from the source list. + * Just using ZEND_TRY_ASSIGN_STRINGL is not sufficient because that would not unwrap the reference + * and change values through references (see bug #26639). */ + if (Z_TYPE_P(entry) == IS_INDIRECT) { + ZEND_ASSERT(Z_TYPE_P(var) == IS_OBJECT); + + entry = Z_INDIRECT_P(entry); + if (Z_ISREF_P(entry) && Z_TYPE_P(Z_REFVAL_P(entry)) == IS_STRING) { + zend_property_info *info = zend_get_typed_property_info_for_slot(Z_OBJ_P(var), entry); + if (info) { + ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(entry), info); + } + } + } + if (mb_recursive_convert_variable(convd, entry)) { if (Z_REFCOUNTED_P(var)) { Z_UNPROTECT_RECURSION_P(var); diff --git a/ext/mbstring/tests/gh16261.phpt b/ext/mbstring/tests/gh16261.phpt new file mode 100644 index 0000000000000..3573bd191c63d --- /dev/null +++ b/ext/mbstring/tests/gh16261.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-16261 (Reference invariant broken in mb_convert_variables()) +--EXTENSIONS-- +mbstring +--FILE-- +x =& $ref; +$test->z =& $ref3; +mb_convert_variables("EUC-JP", "Shift_JIS", $test); + +class Test2 { + public function __construct(public string $x) {} +} +$test2 = new Test2("foo"); + +mb_convert_variables("EUC-JP", "Shift_JIS", $test->x); + +var_dump($test, $test2); +?> +--EXPECT-- +object(Test)#1 (2) { + ["x"]=> + string(5) "hello" + ["y"]=> + uninitialized(string) + ["z"]=> + &array(1) { + [0]=> + string(5) "world" + } +} +object(Test2)#2 (1) { + ["x"]=> + string(3) "foo" +}