mirror of
https://github.com/php/php-src.git
synced 2024-09-22 02:17:32 +00:00
Merge branch 'PHP-7.4'
* PHP-7.4: Fix #78438: Corruption when __unserializing deeply nested structures
This commit is contained in:
commit
5d94ad7944
121
ext/standard/tests/serialize/bug78438.phpt
Normal file
121
ext/standard/tests/serialize/bug78438.phpt
Normal file
@ -0,0 +1,121 @@
|
||||
--TEST--
|
||||
Bug #78438 (Corruption when __unserializing deeply nested structures)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Node
|
||||
{
|
||||
public $childs = [];
|
||||
|
||||
public function __serialize()
|
||||
{
|
||||
return [$this->childs];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data)
|
||||
{
|
||||
list($this->childs) = $data;
|
||||
}
|
||||
}
|
||||
|
||||
function createTree ($width, $depth) {
|
||||
$root = new Node();
|
||||
|
||||
$nextLevel = [$root];
|
||||
|
||||
for ($level=1; $level<$depth; $level++) {
|
||||
$levelRoots = $nextLevel;
|
||||
$nextLevel = [];
|
||||
|
||||
while (count($levelRoots) > 0) {
|
||||
$levelRoot = array_shift($levelRoots);
|
||||
|
||||
for ($w = 0; $w < $width; $w++) {
|
||||
$tester = new Node();
|
||||
|
||||
$levelRoot->childs[] = $tester;
|
||||
|
||||
$nextLevel[] = $tester;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
|
||||
$width = 3;
|
||||
ob_implicit_flush();
|
||||
|
||||
foreach (range(1, 8) as $depth) {
|
||||
$tree = createTree($width, $depth);
|
||||
|
||||
echo "Testcase tree $width x $depth".PHP_EOL;
|
||||
|
||||
echo "> Serializing now".PHP_EOL;
|
||||
$serialized = serialize($tree);
|
||||
|
||||
echo "> Unserializing now".PHP_EOL;
|
||||
$tree = unserialize($serialized);
|
||||
|
||||
// Lets test whether all is ok!
|
||||
$expectedSize = ($width**$depth - 1)/($width-1);
|
||||
|
||||
$nodes = [$tree];
|
||||
$count = 0;
|
||||
|
||||
while (count($nodes) > 0) {
|
||||
$count++;
|
||||
|
||||
$node = array_shift($nodes);
|
||||
foreach ($node->childs as $node) {
|
||||
$nodes[] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
echo "> Unserialized total node count was $count, expected $expectedSize: ".($expectedSize === $count ? 'CORRECT!' : 'INCORRECT');
|
||||
|
||||
echo PHP_EOL;
|
||||
echo PHP_EOL;
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
Testcase tree 3 x 1
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 1, expected 1: CORRECT!
|
||||
|
||||
Testcase tree 3 x 2
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 4, expected 4: CORRECT!
|
||||
|
||||
Testcase tree 3 x 3
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 13, expected 13: CORRECT!
|
||||
|
||||
Testcase tree 3 x 4
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 40, expected 40: CORRECT!
|
||||
|
||||
Testcase tree 3 x 5
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 121, expected 121: CORRECT!
|
||||
|
||||
Testcase tree 3 x 6
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 364, expected 364: CORRECT!
|
||||
|
||||
Testcase tree 3 x 7
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 1093, expected 1093: CORRECT!
|
||||
|
||||
Testcase tree 3 x 8
|
||||
> Serializing now
|
||||
> Unserializing now
|
||||
> Unserialized total node count was 3280, expected 3280: CORRECT!
|
@ -40,7 +40,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
zend_long used_slots;
|
||||
void *next;
|
||||
zval data[VAR_ENTRIES_MAX];
|
||||
zval data[VAR_DTOR_ENTRIES_MAX];
|
||||
} var_dtor_entries;
|
||||
|
||||
struct php_unserialize_data {
|
||||
@ -122,16 +122,17 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
|
||||
}
|
||||
}
|
||||
|
||||
PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
|
||||
static zend_always_inline zval *tmp_var(php_unserialize_data_t *var_hashx, zend_long num)
|
||||
{
|
||||
var_dtor_entries *var_hash;
|
||||
zend_long used_slots;
|
||||
|
||||
if (!var_hashx || !*var_hashx) {
|
||||
if (!var_hashx || !*var_hashx || num < 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
var_hash = (*var_hashx)->last_dtor;
|
||||
if (!var_hash || var_hash->used_slots == VAR_DTOR_ENTRIES_MAX) {
|
||||
if (!var_hash || var_hash->used_slots + num > VAR_DTOR_ENTRIES_MAX) {
|
||||
var_hash = emalloc(sizeof(var_dtor_entries));
|
||||
var_hash->used_slots = 0;
|
||||
var_hash->next = 0;
|
||||
@ -144,9 +145,16 @@ PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
|
||||
|
||||
(*var_hashx)->last_dtor = var_hash;
|
||||
}
|
||||
ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
|
||||
Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
|
||||
return &var_hash->data[var_hash->used_slots++];
|
||||
for (used_slots = var_hash->used_slots; var_hash->used_slots < used_slots + num; var_hash->used_slots++) {
|
||||
ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
|
||||
Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
|
||||
}
|
||||
return &var_hash->data[used_slots];
|
||||
}
|
||||
|
||||
PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
|
||||
{
|
||||
return tmp_var(var_hashx, 1);
|
||||
}
|
||||
|
||||
PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval)
|
||||
@ -653,10 +661,10 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, zend_
|
||||
/* Delay __unserialize() call until end of serialization. We use two slots here to
|
||||
* store both the object and the unserialized data array. */
|
||||
ZVAL_DEREF(rval);
|
||||
tmp = var_tmp_var(var_hash);
|
||||
tmp = tmp_var(var_hash, 2);
|
||||
ZVAL_COPY(tmp, rval);
|
||||
Z_EXTRA_P(tmp) = VAR_UNSERIALIZE_FLAG;
|
||||
tmp = var_tmp_var(var_hash);
|
||||
tmp++;
|
||||
ZVAL_COPY_VALUE(tmp, &ary);
|
||||
|
||||
return finish_nested_data(UNSERIALIZE_PASSTHRU);
|
||||
|
Loading…
Reference in New Issue
Block a user