Skip to content

Commit ff8c290

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Fix serialization of RC1 objects appearing in object graph twice
2 parents 68ef393 + 6b9d295 commit ff8c290

File tree

3 files changed

+66
-15
lines changed

3 files changed

+66
-15
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ PHP NEWS
2828
- Session:
2929
. Fixed bug GH-11529 (Crash after dealing with an Apache request). (nielsdos)
3030

31+
- Standard:
32+
. Fix serialization of RC1 objects appearing in object graph twice. (ilutov)
33+
3134
- zip:
3235
. zip extension version 1.22.0 for libzip 1.10.0. (Remi)
3336
. add new error macros (ER_DATA_LENGTH and ER_NOT_ALLOWED). (Remi)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Serialization of RC1 objects appearing in object graph twice
3+
--FILE--
4+
<?php
5+
6+
function rcn() {
7+
$root = new stdClass;
8+
$end = new stdClass;
9+
$root->a = [$end];
10+
$root->b = $root->a;
11+
unset($end);
12+
echo serialize($root), "\n";
13+
}
14+
15+
function rcn_rc1() {
16+
$root = new stdClass;
17+
$end = new stdClass;
18+
$root->a = [[$end]];
19+
$root->b = $root->a;
20+
unset($end);
21+
echo serialize($root), "\n";
22+
}
23+
24+
function rcn_properties_ht() {
25+
$object = new stdClass;
26+
$object->object = new stdClass;
27+
$array = (array) $object;
28+
$root = [$object, $array];
29+
unset($object);
30+
unset($array);
31+
echo serialize($root), "\n";
32+
}
33+
34+
rcn();
35+
rcn_rc1();
36+
rcn_properties_ht();
37+
38+
?>
39+
--EXPECT--
40+
O:8:"stdClass":2:{s:1:"a";a:1:{i:0;O:8:"stdClass":0:{}}s:1:"b";a:1:{i:0;r:3;}}
41+
O:8:"stdClass":2:{s:1:"a";a:1:{i:0;a:1:{i:0;O:8:"stdClass":0:{}}}s:1:"b";a:1:{i:0;a:1:{i:0;r:4;}}}
42+
a:2:{i:0;O:8:"stdClass":1:{s:6:"object";O:8:"stdClass":0:{}}i:1;a:1:{s:6:"object";r:3;}}

ext/standard/var.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -655,9 +655,12 @@ PHP_FUNCTION(var_export)
655655
}
656656
/* }}} */
657657

658-
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash);
658+
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root);
659659

660-
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /* {{{ */
660+
/**
661+
* @param bool in_rcn_array Whether the element appears in a potentially nested array with RC > 1.
662+
*/
663+
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, bool in_rcn_array) /* {{{ */
661664
{
662665
zval *zv;
663666
zend_ulong key;
@@ -669,7 +672,9 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /
669672
/* pass */
670673
} else if (Z_TYPE_P(var) != IS_OBJECT) {
671674
return 0;
672-
} else if (Z_REFCOUNT_P(var) == 1 && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) {
675+
} else if (!in_rcn_array
676+
&& Z_REFCOUNT_P(var) == 1
677+
&& (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) {
673678
return 0;
674679
}
675680

@@ -911,7 +916,7 @@ static int php_var_serialize_get_sleep_props(
911916
}
912917
/* }}} */
913918

914-
static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, bool incomplete_class, php_serialize_data_t var_hash) /* {{{ */
919+
static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, bool incomplete_class, php_serialize_data_t var_hash, bool in_rcn_array) /* {{{ */
915920
{
916921
smart_str_append_unsigned(buf, count);
917922
smart_str_appendl(buf, ":{", 2);
@@ -941,19 +946,19 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
941946
if (Z_TYPE_P(data) == IS_ARRAY) {
942947
if (UNEXPECTED(Z_IS_RECURSIVE_P(data))
943948
|| UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) {
944-
php_add_var_hash(var_hash, struc);
949+
php_add_var_hash(var_hash, struc, in_rcn_array);
945950
smart_str_appendl(buf, "N;", 2);
946951
} else {
947952
if (Z_REFCOUNTED_P(data)) {
948953
Z_PROTECT_RECURSION_P(data);
949954
}
950-
php_var_serialize_intern(buf, data, var_hash);
955+
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
951956
if (Z_REFCOUNTED_P(data)) {
952957
Z_UNPROTECT_RECURSION_P(data);
953958
}
954959
}
955960
} else {
956-
php_var_serialize_intern(buf, data, var_hash);
961+
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
957962
}
958963
} ZEND_HASH_FOREACH_END();
959964
}
@@ -968,13 +973,13 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht,
968973
if (php_var_serialize_get_sleep_props(&props, struc, ht) == SUCCESS) {
969974
php_var_serialize_class_name(buf, struc);
970975
php_var_serialize_nested_data(
971-
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash);
976+
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash, GC_REFCOUNT(&props) > 1);
972977
}
973978
zend_hash_destroy(&props);
974979
}
975980
/* }}} */
976981

977-
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */
982+
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root) /* {{{ */
978983
{
979984
zend_long var_already;
980985
HashTable *myht;
@@ -983,7 +988,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
983988
return;
984989
}
985990

986-
if (var_hash && (var_already = php_add_var_hash(var_hash, struc))) {
991+
if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
987992
if (var_already == -1) {
988993
/* Reference to an object that failed to serialize, replace with null. */
989994
smart_str_appendl(buf, "N;", 2);
@@ -1092,7 +1097,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
10921097
if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
10931098
data = Z_REFVAL_P(data);
10941099
}
1095-
php_var_serialize_intern(buf, data, var_hash);
1100+
php_var_serialize_intern(buf, data, var_hash, Z_REFCOUNT(retval) > 1, false);
10961101
} ZEND_HASH_FOREACH_END();
10971102
smart_str_appendc(buf, '}');
10981103

@@ -1214,7 +1219,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12141219
prop = Z_REFVAL_P(prop);
12151220
}
12161221

1217-
php_var_serialize_intern(buf, prop, var_hash);
1222+
php_var_serialize_intern(buf, prop, var_hash, false, false);
12181223
}
12191224
smart_str_appendc(buf, '}');
12201225
} else {
@@ -1229,15 +1234,16 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12291234
if (count > 0 && incomplete_class) {
12301235
--count;
12311236
}
1232-
php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash);
1237+
php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash, GC_REFCOUNT(myht) > 1);
12331238
zend_release_properties(myht);
12341239
return;
12351240
}
12361241
case IS_ARRAY:
12371242
smart_str_appendl(buf, "a:", 2);
12381243
myht = Z_ARRVAL_P(struc);
12391244
php_var_serialize_nested_data(
1240-
buf, struc, myht, zend_array_count(myht), /* incomplete_class */ 0, var_hash);
1245+
buf, struc, myht, zend_array_count(myht), /* incomplete_class */ 0, var_hash,
1246+
!is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1));
12411247
return;
12421248
case IS_REFERENCE:
12431249
struc = Z_REFVAL_P(struc);
@@ -1251,7 +1257,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12511257

12521258
PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data) /* {{{ */
12531259
{
1254-
php_var_serialize_intern(buf, struc, *data);
1260+
php_var_serialize_intern(buf, struc, *data, false, true);
12551261
smart_str_0(buf);
12561262
}
12571263
/* }}} */

0 commit comments

Comments
 (0)