Merge branch 'PHP-7.4'

* PHP-7.4:
  Fix #78438: Corruption when __unserializing deeply nested structures
This commit is contained in:
Christoph M. Becker 2019-08-23 11:41:38 +02:00
commit 5d94ad7944
2 changed files with 138 additions and 9 deletions

View 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!

View File

@ -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);