Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ PHP NEWS
segfaults). (iliaal)

- Standard:
. Fixed use-after-free in serialize() when a nested magic callback releases
the current object. (PuH4ck3rX)
. Fixed bug GH-22360 (convert.base64-encode corruption on
incremental flush). (David Carlier)
. Fixed bug GH-22395 (base_convert() outputs at most 64 characters).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
--TEST--
serialize() retains objects across nested magic callbacks
--FILE--
<?php

class A {
public $b;
}

class B {
public $c;
public $pad = 123;
}

class Spray {
public $x;
public $y = 'CONTROLLED';
}

class C {
public $a;

public function __serialize(): array {
unset($this->a->b);
$GLOBALS['spray'] = new Spray();
return [];
}
}

$a = new A();
$b = new B();
$c = new C();

$a->b = $b;
$b->c = $c;
$c->a = $a;

unset($b, $c);
echo serialize($a), "\n";

?>
--EXPECT--
O:1:"A":1:{s:1:"b";O:1:"B":2:{s:1:"c";O:1:"C":0:{}s:3:"pad";i:123;}}
16 changes: 3 additions & 13 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -726,10 +726,7 @@ PHP_FUNCTION(var_export)

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

/**
* @param bool in_rcn_array Whether the element appears in a potentially nested array with RC > 1.
*/
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, bool in_rcn_array) /* {{{ */
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /* {{{ */
{
zval *zv;
zend_ulong key;
Expand All @@ -741,13 +738,6 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, b
/* pass */
} else if (Z_TYPE_P(var) != IS_OBJECT) {
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)
/* __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;
}

/* References to objects are treated as if the reference didn't exist */
Expand Down Expand Up @@ -1008,7 +998,7 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
* since we already wrote the length of the array before */
if (Z_TYPE_P(data) == IS_ARRAY) {
if (UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) {
php_add_var_hash(var_hash, struc, in_rcn_array);
php_add_var_hash(var_hash, struc);
smart_str_appendl(buf, "N;", 2);
} else {
php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
Expand Down Expand Up @@ -1059,7 +1049,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
return;
}

if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
if (var_hash && (var_already = php_add_var_hash(var_hash, struc))) {
if (var_already == -1) {
/* Reference to an object that failed to serialize, replace with null. */
smart_str_appendl(buf, "N;", 2);
Expand Down
Loading