Skip to content

Commit 6b9d295

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: Fix serialization of RC1 objects appearing in object graph twice
2 parents 54dfa86 + d7d3669 commit 6b9d295

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
@@ -29,6 +29,9 @@ PHP NEWS
2929
- Session:
3030
. Removed broken url support for transferring session ID. (ilutov)
3131

32+
- Standard:
33+
. Fix serialization of RC1 objects appearing in object graph twice. (ilutov)
34+
3235
06 Jul 2023, PHP 8.2.8
3336

3437
- CLI:
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

@@ -926,7 +931,7 @@ static int php_var_serialize_get_sleep_props(
926931
}
927932
/* }}} */
928933

929-
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) /* {{{ */
934+
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) /* {{{ */
930935
{
931936
smart_str_append_unsigned(buf, count);
932937
smart_str_appendl(buf, ":{", 2);
@@ -956,19 +961,19 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
956961
if (Z_TYPE_P(data) == IS_ARRAY) {
957962
if (UNEXPECTED(Z_IS_RECURSIVE_P(data))
958963
|| UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) {
959-
php_add_var_hash(var_hash, struc);
964+
php_add_var_hash(var_hash, struc, in_rcn_array);
960965
smart_str_appendl(buf, "N;", 2);
961966
} else {
962967
if (Z_REFCOUNTED_P(data)) {
963968
Z_PROTECT_RECURSION_P(data);
964969
}
965-
php_var_serialize_intern(buf, data, var_hash);
970+
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
966971
if (Z_REFCOUNTED_P(data)) {
967972
Z_UNPROTECT_RECURSION_P(data);
968973
}
969974
}
970975
} else {
971-
php_var_serialize_intern(buf, data, var_hash);
976+
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
972977
}
973978
} ZEND_HASH_FOREACH_END();
974979
}
@@ -983,13 +988,13 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht,
983988
if (php_var_serialize_get_sleep_props(&props, struc, ht) == SUCCESS) {
984989
php_var_serialize_class_name(buf, struc);
985990
php_var_serialize_nested_data(
986-
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash);
991+
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash, GC_REFCOUNT(&props) > 1);
987992
}
988993
zend_hash_destroy(&props);
989994
}
990995
/* }}} */
991996

992-
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */
997+
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root) /* {{{ */
993998
{
994999
zend_long var_already;
9951000
HashTable *myht;
@@ -998,7 +1003,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
9981003
return;
9991004
}
10001005

1001-
if (var_hash && (var_already = php_add_var_hash(var_hash, struc))) {
1006+
if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
10021007
if (var_already == -1) {
10031008
/* Reference to an object that failed to serialize, replace with null. */
10041009
smart_str_appendl(buf, "N;", 2);
@@ -1107,7 +1112,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
11071112
if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
11081113
data = Z_REFVAL_P(data);
11091114
}
1110-
php_var_serialize_intern(buf, data, var_hash);
1115+
php_var_serialize_intern(buf, data, var_hash, Z_REFCOUNT(retval) > 1, false);
11111116
} ZEND_HASH_FOREACH_END();
11121117
smart_str_appendc(buf, '}');
11131118

@@ -1229,7 +1234,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12291234
prop = Z_REFVAL_P(prop);
12301235
}
12311236

1232-
php_var_serialize_intern(buf, prop, var_hash);
1237+
php_var_serialize_intern(buf, prop, var_hash, false, false);
12331238
}
12341239
smart_str_appendc(buf, '}');
12351240
} else {
@@ -1244,15 +1249,16 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12441249
if (count > 0 && incomplete_class) {
12451250
--count;
12461251
}
1247-
php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash);
1252+
php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash, GC_REFCOUNT(myht) > 1);
12481253
zend_release_properties(myht);
12491254
return;
12501255
}
12511256
case IS_ARRAY:
12521257
smart_str_appendl(buf, "a:", 2);
12531258
myht = Z_ARRVAL_P(struc);
12541259
php_var_serialize_nested_data(
1255-
buf, struc, myht, zend_array_count(myht), /* incomplete_class */ 0, var_hash);
1260+
buf, struc, myht, zend_array_count(myht), /* incomplete_class */ 0, var_hash,
1261+
!is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1));
12561262
return;
12571263
case IS_REFERENCE:
12581264
struc = Z_REFVAL_P(struc);
@@ -1266,7 +1272,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
12661272

12671273
PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data) /* {{{ */
12681274
{
1269-
php_var_serialize_intern(buf, struc, *data);
1275+
php_var_serialize_intern(buf, struc, *data, false, true);
12701276
smart_str_0(buf);
12711277
}
12721278
/* }}} */

0 commit comments

Comments
 (0)