From 0a5576468412d2f62d045baed781d5bbbfe9ad86 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:53:07 +0200 Subject: [PATCH 1/2] Fix GH-12265: Cloning an object breaks serialization recursion --- ext/standard/tests/serialize/gh12265.phpt | 47 +++++++++++++++++++++++ ext/standard/var.c | 4 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/serialize/gh12265.phpt diff --git a/ext/standard/tests/serialize/gh12265.phpt b/ext/standard/tests/serialize/gh12265.phpt new file mode 100644 index 0000000000000..ab89e4420c39a --- /dev/null +++ b/ext/standard/tests/serialize/gh12265.phpt @@ -0,0 +1,47 @@ +--TEST-- +GH-12265 (Cloning an object breaks serialization recursion) +--FILE-- + new A($this)]; + } +} + +class C { + public B $b; + + public function __construct() { + $this->b = new B; + } +} + +$b = new B(); +$sb = serialize($b); +$stb = serialize(new B); + +printf("serialized original: %s\n", $sb); +printf("serialized temp : %s\n", $stb); + +$c = new C; +$sc = serialize($c); +$stc = serialize(new C); + +printf("serialized original: %s\n", $sc); +printf("serialized temp : %s\n", $stc); + +?> +--EXPECT-- +serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +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;}}} +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;}}} diff --git a/ext/standard/var.c b/ext/standard/var.c index aab00e52d46dd..10d1256ab424d 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -671,7 +671,9 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, b return 0; } else if (!in_rcn_array && Z_REFCOUNT_P(var) == 1 - && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) { + && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1) + /* __serialize may arbitrarily increase the refcount */ + && Z_OBJCE_P(var)->__serialize == NULL) { return 0; } From 60f05cf59837d449309bf91de824af76a14de917 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 25 Sep 2023 20:21:25 +0200 Subject: [PATCH 2/2] __sleep too --- ext/standard/tests/serialize/gh12265.phpt | 2 +- ext/standard/tests/serialize/gh12265b.phpt | 48 ++++++++++++++++++++++ ext/standard/var.c | 5 ++- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 ext/standard/tests/serialize/gh12265b.phpt diff --git a/ext/standard/tests/serialize/gh12265.phpt b/ext/standard/tests/serialize/gh12265.phpt index ab89e4420c39a..5f49a62fee29f 100644 --- a/ext/standard/tests/serialize/gh12265.phpt +++ b/ext/standard/tests/serialize/gh12265.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-12265 (Cloning an object breaks serialization recursion) +GH-12265 (Cloning an object breaks serialization recursion) - __serialize variation --FILE-- a = new A($this); + return ['a']; + } +} + +class C { + public B $b; + + public function __construct() { + $this->b = new B; + } +} + +$b = new B(); +$sb = serialize($b); +$stb = serialize(new B); + +printf("serialized original: %s\n", $sb); +printf("serialized temp : %s\n", $stb); + +$c = new C; +$sc = serialize($c); +$stc = serialize(new C); + +printf("serialized original: %s\n", $sc); +printf("serialized temp : %s\n", $stc); + +?> +--EXPECT-- +serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +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;}}} +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;}}} diff --git a/ext/standard/var.c b/ext/standard/var.c index 10d1256ab424d..860b104260ff1 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -672,8 +672,9 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, b } else if (!in_rcn_array && Z_REFCOUNT_P(var) == 1 && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1) - /* __serialize may arbitrarily increase the refcount */ - && Z_OBJCE_P(var)->__serialize == NULL) { + /* __serialize and __sleep may arbitrarily increase the refcount */ + && Z_OBJCE_P(var)->__serialize == NULL + && zend_hash_find_known_hash(&Z_OBJCE_P(var)->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)) == NULL) { return 0; }