Skip to content
This repository has been archived by the owner on Oct 14, 2023. It is now read-only.

Commit

Permalink
Merge pull request #13 from TysonAndre/fix-php7-serialization-v3
Browse files Browse the repository at this point in the history
Fix php7 igbinary serialization and unserialization igbinary reference id counts in serialize/unserialize.
Make PHP reference unserialization more consistent.
Fix segfault in __wakeup for references
add tests
  • Loading branch information
TysonAndre authored Jul 15, 2016
2 parents 7d5d086 + 1e56913 commit 3f1e5af
Show file tree
Hide file tree
Showing 12 changed files with 826 additions and 133 deletions.
321 changes: 198 additions & 123 deletions igbinary.c

Large diffs are not rendered by default.

33 changes: 23 additions & 10 deletions tests/igbinary_009.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ if(!extension_loaded('igbinary')) {
--FILE--
<?php

function test($type, $variable, $test = true) {
function test($type, $variable, $normalize = false) {
// Canonicalize $variable
if ($normalize) {
$variable = unserialize(serialize($variable));
}
$serialized = igbinary_serialize($variable);
$unserialized = igbinary_unserialize($serialized);


$serialize_act = serialize($unserialized);
$serialize_exp = serialize($variable);

echo $type, "\n";
echo substr(bin2hex($serialized), 8), "\n";
echo !$test || $unserialized == $variable ? 'OK' : 'ERROR', "\n";
echo $serialize_act === $serialize_exp ? 'OK' : 'ERROR', "\n";

ob_start();
var_dump($variable);
Expand All @@ -23,30 +31,35 @@ function test($type, $variable, $test = true) {
var_dump($unserialized);
$dump_act = ob_get_clean();


if ($dump_act !== $dump_exp) {
echo "But var dump differs:\n", $dump_act, "\n", $dump_exp, "\n";
echo "But var dump differs:\nActual:\n", $dump_act, "\nExpected\n", $dump_exp, "\n";
if ($normalize) {
echo "(Was normalized)\n";
}
}

if ($serialize_act !== $serialize_exp) {
echo "But serialize differs:\nActual:\n", $serialize_act, "\nExpected:\n", $serialize_exp, "\n";
}
}

$a = array('foo');

test('array($a, $a)', array($a, $a), true);
test('array(&$a, &$a)', array(&$a, &$a), true);
test('array($a, $a)', [$a, $a]);
test('array(&$a, &$a)', [&$a, &$a]);

$a = array(null);
$b = array(&$a);
$a[0] = &$b;

test('cyclic $a = array(&array(&$a))', $a, false);

test('cyclic $a = array(&array(&$a)) - normalized', $a, true);
--EXPECT--
array($a, $a)
14020600140106001103666f6f06010101
OK
array(&$a, &$a)
1402060025140106001103666f6f0601250101
OK
cyclic $a = array(&array(&$a))
1401060025140106002514010600250101
cyclic $a = array(&array(&$a)) - normalized
14010600251401060014010600250101
OK
63 changes: 63 additions & 0 deletions tests/igbinary_009b.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--TEST--
Check for reference serialization (Original example, not using var_dump)
--SKIPIF--
<?php
if(!extension_loaded('igbinary')) {
echo "skip no igbinary";
}
--FILE--
<?php
error_reporting(E_ALL|E_STRICT);
// Verify that $type[0] is the same zval as $type[0][0][0], but different from $type[0]
function test_cyclic2($type, $variable) {
$serialized = igbinary_serialize($variable);
$unserialized = igbinary_unserialize($serialized);
echo $type, "\n";
echo substr(bin2hex($serialized), 8), "\n";
// Can't use === or == on two recursive arrays in some cases
echo array_keys($unserialized) === array_keys($variable) && array_keys($unserialized[0]) === array_keys($variable[0]) ? 'OK' : 'ERROR', "\n";
ob_start();
var_dump($variable);
$dump_exp = ob_get_clean();
ob_start();
var_dump($unserialized);
$dump_act = ob_get_clean();
if (preg_replace('/&array/', 'array', $dump_act) !== preg_replace('/&array/', 'array', $dump_exp)) {
echo "But var dump differs:\nActual:\n", $dump_act, "\nExpected\n", $dump_exp, "\n";
echo "(Was normalized)\n";
}

if (!isset($unserialized[0]) || count($unserialized) != 1) {
printf("Unexpected keys: %s\n", array_keys($unserialized));
return;
} else if (!is_array($unserialized)) {
printf("\$a[0] is not an array, it is %s", gettype($unserialized));
return;
}
// Set a key, check for the presense of the key 2 levels deeper (Should find it) and 1 level deeper (Should not find it)
$unserialized[0]['test'] = 'foo';
if ($unserialized[0][0][0]['test'] !== 'foo') {
echo "Expected the unserialized array to be cyclic\n";
}
if (isset($unserialized[0][0]['test'])) {
echo "Expected the unserialized array to be cyclic AND of cycle depth 2, but cycle depth is 1\n";
}
}
$a = [null];
$b = [&$a];
$a[0] = &$b;
// 1401060025140106002514010600250101 could also be serialized as 14010600251401060014010600250101 if we normalized the references which only occurred once in the serialization
// (Replace middle &array(&$a) with array(&$array), i.e. second 2514 with 14)
test_cyclic2('cyclic $a = array(&array(&$a)) - testing functionality', $a);
unset($a);
$a = null;
$a = [[&$a]];
test_cyclic2('cyclic $a = array(array(&$a)); $a[0] - testing functionality', $a[0]);
// $a serializes as 140106001401060025140106000101 - This is a bug, probably exists in php5 as well.
--EXPECT--
cyclic $a = array(&array(&$a)) - testing functionality
1401060025140106002514010600250101
OK
cyclic $a = array(array(&$a)); $a[0] - testing functionality
14010600251401060014010600250101
OK
33 changes: 33 additions & 0 deletions tests/igbinary_046b.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Correctly unserialize multiple object refs.
--SKIPIF--
--INI--
igbinary.compact_strings = On
--FILE--
<?php
$a = array(new stdClass());
$a[1] = &$a[0];
$a[2] = &$a[1];
$a[3] = &$a[2];
printf("%s\n", serialize($a));
$ig_ser = igbinary_serialize($a);
printf("%s\n", bin2hex($ig_ser));
$ig = igbinary_unserialize($ig_ser);
printf("%s\n", serialize($ig));
$f = &$ig[3];
$f = 'V';
var_dump($ig);
--EXPECT--
a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;R:2;}
0000000214040600251708737464436c6173731400060125220106022522010603252201
a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;R:2;}
array(4) {
[0]=>
&string(1) "V"
[1]=>
&string(1) "V"
[2]=>
&string(1) "V"
[3]=>
&string(1) "V"
}
33 changes: 33 additions & 0 deletions tests/igbinary_046c.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Correctly unserialize multiple array refs.
--SKIPIF--
--INI--
igbinary.compact_strings = On
--FILE--
<?php
$a = array([]);
$a[1] = &$a[0];
$a[2] = &$a[1];
$a[3] = &$a[2];
printf("%s\n", serialize($a));
$ig_ser = igbinary_serialize($a);
printf("%s\n", bin2hex($ig_ser));
$ig = igbinary_unserialize($ig_ser);
printf("%s\n", serialize($ig));
$f = &$ig[3];
$f = 'V';
var_dump($ig);
--EXPECT--
a:4:{i:0;a:0:{}i:1;R:2;i:2;R:2;i:3;R:2;}
0000000214040600251400060125010106022501010603250101
a:4:{i:0;a:0:{}i:1;R:2;i:2;R:2;i:3;R:2;}
array(4) {
[0]=>
&string(1) "V"
[1]=>
&string(1) "V"
[2]=>
&string(1) "V"
[3]=>
&string(1) "V"
}
67 changes: 67 additions & 0 deletions tests/igbinary_046d.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
--TEST--
Correctly unserialize multiple object refs and non-refs.
--SKIPIF--
--INI--
igbinary.compact_strings = On
--FILE--
<?php
$a = array(new stdClass());
$a[1] = $a[0];
$a[2] = &$a[1];
$a[3] = $a[0];
var_dump($a);
printf("%s\n", serialize($a));
$ig_ser = igbinary_serialize($a);
printf("%s\n", bin2hex($ig_ser));
$ig = igbinary_unserialize($ig_ser);
printf("%s\n", serialize($ig));
var_dump($ig);
$f = &$ig[2];
$f = 'V';
var_dump($ig);
// Note: While the php7 unserializer consistently makes a distinction between refs to an object and non-refs,
// the php5 serializer does not yet.
--EXPECTF--
array(4) {
[0]=>
object(stdClass)#%d (0) {
}
[1]=>
&object(stdClass)#%d (0) {
}
[2]=>
&object(stdClass)#%d (0) {
}
[3]=>
object(stdClass)#%d (0) {
}
}
a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;r:2;}
00000002140406001708737464436c61737314000601252201060225220106032201
a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;r:2;}
array(4) {
[0]=>
object(stdClass)#%d (0) {
}
[1]=>
&object(stdClass)#%d (0) {
}
[2]=>
&object(stdClass)#%d (0) {
}
[3]=>
object(stdClass)#%d (0) {
}
}
array(4) {
[0]=>
object(stdClass)#%d (0) {
}
[1]=>
&string(1) "V"
[2]=>
&string(1) "V"
[3]=>
object(stdClass)#%d (0) {
}
}
120 changes: 120 additions & 0 deletions tests/igbinary_049.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
--TEST--
Correctly unserialize multiple references in arrays
--SKIPIF--
--INI--
igbinary.compact_strings = On
--FILE--
<?php
class Foo{}
$a = array("A");
$a[1] = &$a[0];
$a[2] = &$a[1];
$a[3] = &$a[2];
$a[4] = false;
$a[5] = &$a[4];
$a[6] = new Foo();
$a[7] = &$a[6];
$a[8] = &$a[7];
$a[9] = [33];
$a[10] = new stdClass();
$a[10]->prop = &$a[8];
$a[11] = &$a[10];
$a[12] = $a[9];
$ig_ser = igbinary_serialize($a);
printf("ig_ser=%s\n", bin2hex($ig_ser));
$ig = igbinary_unserialize($ig_ser);
var_dump($ig);
$f = &$ig[3];
$f = 'V';
$g = &$ig[5];
$g = 'H';
$h = $ig[10];
$h->prop = 'S';
var_dump($ig);
--EXPECTF--
ig_ser=00000002140d0600251101410601250101060225010106032501010604250406052501020606251703466f6f1400060725220306082522030609140106000621060a251708737464436c6173731401110470726f70252203060b252205060c0104
array(13) {
[0]=>
&string(1) "A"
[1]=>
&string(1) "A"
[2]=>
&string(1) "A"
[3]=>
&string(1) "A"
[4]=>
&bool(false)
[5]=>
&bool(false)
[6]=>
&object(Foo)#%d (0) {
}
[7]=>
&object(Foo)#%d (0) {
}
[8]=>
&object(Foo)#%d (0) {
}
[9]=>
array(1) {
[0]=>
int(33)
}
[10]=>
&object(stdClass)#%d (1) {
["prop"]=>
&object(Foo)#%d (0) {
}
}
[11]=>
&object(stdClass)#%d (1) {
["prop"]=>
&object(Foo)#%d (0) {
}
}
[12]=>
array(1) {
[0]=>
int(33)
}
}
array(13) {
[0]=>
&string(1) "V"
[1]=>
&string(1) "V"
[2]=>
&string(1) "V"
[3]=>
&string(1) "V"
[4]=>
&string(1) "H"
[5]=>
&string(1) "H"
[6]=>
&string(1) "S"
[7]=>
&string(1) "S"
[8]=>
&string(1) "S"
[9]=>
array(1) {
[0]=>
int(33)
}
[10]=>
&object(stdClass)#%d (1) {
["prop"]=>
&string(1) "S"
}
[11]=>
&object(stdClass)#%d (1) {
["prop"]=>
&string(1) "S"
}
[12]=>
array(1) {
[0]=>
int(33)
}
}
Loading

0 comments on commit 3f1e5af

Please sign in to comment.