Skip to content

Commit f6f1748

Browse files
committed
Fix GH-12265: Cloning an object breaks serialization recursion
Closes GH-12287.
1 parent c9cc68b commit f6f1748

File tree

4 files changed

+103
-1
lines changed

4 files changed

+103
-1
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ PHP NEWS
66
. Fixed bug GH-19765 (object_properties_load() bypasses readonly property
77
checks). (timwolla)
88

9+
- Standard:
10+
. Fixed bug GH-12265 (Cloning an object breaks serialization recursion).
11+
(nielsdos)
12+
913
25 Sep 2025, PHP 8.3.26
1014

1115
- Core:
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
GH-12265 (Cloning an object breaks serialization recursion) - __serialize variation
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(public B $x) {
8+
}
9+
}
10+
11+
class B {
12+
public A $a;
13+
14+
public function __serialize()
15+
{
16+
return ['a' => new A($this)];
17+
}
18+
}
19+
20+
class C {
21+
public B $b;
22+
23+
public function __construct() {
24+
$this->b = new B;
25+
}
26+
}
27+
28+
$b = new B();
29+
$sb = serialize($b);
30+
$stb = serialize(new B);
31+
32+
printf("serialized original: %s\n", $sb);
33+
printf("serialized temp : %s\n", $stb);
34+
35+
$c = new C;
36+
$sc = serialize($c);
37+
$stc = serialize(new C);
38+
39+
printf("serialized original: %s\n", $sc);
40+
printf("serialized temp : %s\n", $stc);
41+
42+
?>
43+
--EXPECT--
44+
serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}}
45+
serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}}
46+
serialized original: O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}}
47+
serialized temp : O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
GH-12265 (Cloning an object breaks serialization recursion) - __sleep variation
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(public B $x) {
8+
}
9+
}
10+
11+
class B {
12+
public A $a;
13+
14+
public function __sleep()
15+
{
16+
$this->a = new A($this);
17+
return ['a'];
18+
}
19+
}
20+
21+
class C {
22+
public B $b;
23+
24+
public function __construct() {
25+
$this->b = new B;
26+
}
27+
}
28+
29+
$b = new B();
30+
$sb = serialize($b);
31+
$stb = serialize(new B);
32+
33+
printf("serialized original: %s\n", $sb);
34+
printf("serialized temp : %s\n", $stb);
35+
36+
$c = new C;
37+
$sc = serialize($c);
38+
$stc = serialize(new C);
39+
40+
printf("serialized original: %s\n", $sc);
41+
printf("serialized temp : %s\n", $stc);
42+
43+
?>
44+
--EXPECT--
45+
serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}}
46+
serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}}
47+
serialized original: O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}}
48+
serialized temp : O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}}

ext/standard/var.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,10 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, b
681681
return 0;
682682
} else if (!in_rcn_array
683683
&& Z_REFCOUNT_P(var) == 1
684-
&& (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) {
684+
&& (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)
685+
/* __serialize and __sleep may arbitrarily increase the refcount */
686+
&& Z_OBJCE_P(var)->__serialize == NULL
687+
&& zend_hash_find_known_hash(&Z_OBJCE_P(var)->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)) == NULL) {
685688
return 0;
686689
}
687690

0 commit comments

Comments
 (0)