php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #62373 serialize() generates wrong reference to the object
Submitted: 2012-06-20 17:13 UTC Modified: 2012-06-25 10:25 UTC
From: miau dot jp at gmail dot com Assigned: moriyoshi
Status: Closed Package: Class/Object related
PHP Version: 5.3.14 OS: any
Private report: No CVE-ID:
 [2012-06-20 17:13 UTC] miau dot jp at gmail dot com
Description:
------------
With many (more than 21760 on 32-bit environment) objects on a PHP environment,
serialize() occasionally generates wrong reference to object.

When an object is passed to serialize() function, a hash value is
caluculated. If the hash value has been seen before, the object is
considered as the same object that is already serialized and serialize()
generates the reference representation such as "r:2".

Hash value is calculated with the logic below.
https://github.com/php/php-src/blob/PHP-5.3.14/ext/standard/var.c#L545

(((size_t)Z_OBJCE_P(var) << 5)
| ((size_t)Z_OBJCE_P(var) >> (sizeof(long) * 8 - 5))) # always 0 on 32-bit 
environment
+ (long) Z_OBJ_HANDLE_P(var)

Z_OBJCE_P(var) is the address of zend_class_entry structure for each class.
Z_OBJ_HANDLE_P(var) is the sequencial number that begins with 1 and
increased by 1 for each object.

sizeof(zend_class_entry) is 680 on 32-bit environment. When you declare
two empty class A and B, and construct object $a and $b, hash values of
two object can be the same. 

|object|Z_OBJ_HANDLE_P(var) |class|Z_OBJCE_P(var)|hash value          |
|------+--------------------+-----+--------------+--------------------+
|$b    |1                   |B    |x + 680       |((x + 680) << 5) + 1|
|(any) |2                   |     |              |                    |
|(any) |3                   |     |              |                    |
|      |:                   |     |              |                    |
|$a    |1 + (680 << 5)      |A    |x             |((x + 680) << 5) + 1|

In such a case, serialize() generates wrong reprensenation.


Test script:
---------------
<?php
class A {}
class B {}

$dummy = array();
$b = new B();
for ($i = 1; $i <= 100000; $i++) {
    $a = new A();
    $s = serialize(array($b, $a));
    if (strpos($s, 'r:') !== false) {
        echo "conflict occures at the {$i}th loop!\n";
        echo "$s\n";
        var_dump(unserialize($s));
        exit;
    }
    $dummy[] = $a;
}
echo "not conflict with each other\n";


Expected result:
----------------
a:2:{i:0;O:1:"B":0:{}i:1;O:1:"A":0:{}}
array(2) {
  [0] =>
  class B#100002 (0) {
  }
  [1] =>
  class A#100003 (0) {
  }
}

Actual result:
--------------
conflict occures at the 21760th loop!
a:2:{i:0;O:1:"B":0:{}i:1;r:2;}
array(2) {
  [0] =>
  class B#21762 (0) {
  }
  [1] =>
  class B#21762 (0) {
  }
}


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-06-20 17:37 UTC] sebastian@php.net
It is worth noting that this occurs in PHP_Depend, see https://github.com/pdepend/pdepend/issues/94 for details. And yes, I have repeatedly experienced the same issue in the past.
 [2012-06-20 18:38 UTC] felipe@php.net
This seems related to the bug #49374
 [2012-06-25 08:26 UTC] moriyoshi@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: moriyoshi
 [2012-06-25 10:19 UTC] moriyoshi@php.net
Automatic comment on behalf of mozo@mozo.jp
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e42718227945202044516c71f0098fe464987410
Log: Fix bug #62373 (serialize() generates wrong reference to the object)
 [2012-06-25 10:19 UTC] moriyoshi@php.net
Automatic comment on behalf of mozo@mozo.jp
Revision: http://git.php.net/?p=php-src.git;a=commit;h=91e1df704eed40325fd963a308e466bbbf96184f
Log: Fix bug #62373 (serialize() generates wrong reference to the object)
 [2012-06-25 10:25 UTC] moriyoshi@php.net
-Status: Assigned +Status: Closed
 [2012-06-25 10:25 UTC] moriyoshi@php.net
This bug has been fixed in SVN.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.


 [2012-06-27 04:54 UTC] stas@php.net
Automatic comment on behalf of mozo@mozo.jp
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e42718227945202044516c71f0098fe464987410
Log: Fix bug #62373 (serialize() generates wrong reference to the object)
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 23 14:02:33 2014 UTC