php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77302 Unserialize decode issue
Submitted: 2018-12-15 08:50 UTC Modified: 2019-02-21 11:08 UTC
Votes:31
Avg. Score:4.6 ± 0.9
Reproduced:29 of 30 (96.7%)
Same Version:24 (82.8%)
Same OS:13 (44.8%)
From: sh at analogic dot cz Assigned: dmitry (profile)
Status: Assigned Package: Variables related
PHP Version: 7.3.0 OS:
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: sh at analogic dot cz
New email:
PHP Version: OS:

 

 [2018-12-15 08:50 UTC] sh at analogic dot cz
Description:
------------
See test script
5.6.38 - 7.2.13 will work OK
7.3.0 fails with "Notice: unserialize(): Error at offset 938 of 2517 bytes in /in/gTumn on line 3"
(https://3v4l.org/gTumn)

Object is serialized at PHP 7.3.0

Test script:
---------------
<?php

var_dump(unserialize('a:4:{i:0;O:20:"AppBundle\\Entity\\Box":18:{s:10:"' . "\0" . '*' . "\0" . 'address";s:13:"test@test.com";s:7:"' . "\0" . '*' . "\0" . 'user";s:4:"test";s:9:"' . "\0" . '*' . "\0" . 'domain";O:38:"Proxies\\__CG__\\AppBundle\\Entity\\Domain":10:{s:17:"__isInitialized__";b:1;s:7:"' . "\0" . '*' . "\0" . 'name";s:8:"test.com";s:10:"' . "\0" . '*' . "\0" . 'created";O:8:"DateTime":3:{s:4:"date";s:26:"2018-12-14 21:38:14.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:10:"' . "\0" . '*' . "\0" . 'updated";O:8:"DateTime":3:{s:4:"date";s:26:"2018-12-14 21:38:14.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:12:"' . "\0" . '*' . "\0" . 'domainBin";b:0;s:19:"' . "\0" . '*' . "\0" . 'domainBinAddress";N;s:10:"' . "\0" . '*' . "\0" . 'forward";b:0;s:16:"' . "\0" . '*' . "\0" . 'forwardDomain";N;s:14:"' . "\0" . '*' . "\0" . 'referenceId";N;s:8:"' . "\0" . '*' . "\0" . 'boxes";O:33:"Doctrine\\ORM\\PersistentCollection":2:{s:13:"' . "\0" . '*' . "\0" . 'collection";O:43:"Doctrine\\Common\\Collections\\ArrayCollection":1:{s:53:"' . "\0" . 'Doctrine\\Common\\Collections\\ArrayCollection' . "\0" . 'elements";a:1:{i:0;O:20:"AppBundle\\Entity\\Box":18:{s:10:"' . "\0" . '*' . "\0" . 'address";s:13:"test@test.com";s:7:"' . "\0" . '*' . "\0" . 'user";s:4:"test";s:9:"' . "\0" . '*' . "\0" . 'domain";r:6;s:11:"' . "\0" . '*' . "\0" . 'password";s:120:"{SHA512-CRYPT}$6$acvyLX2N9lsg1F.K$Bop/wS7UxWIH/VzR0uGhopOBSVvwlAdsdxzqXgwluo4VmylnzgGQ5/UhdHZ/1U0UyID8POk2/vGy2h4Q4pGSQ1";s:20:"' . "\0" . '*' . "\0" . 'passwordPlaintext";s:0:"";s:7:"' . "\0" . '*' . "\0" . 'home";s:27:"/data/domains/test.com/test";s:6:"' . "\0" . '*' . "\0" . 'uid";i:8;s:6:"' . "\0" . '*' . "\0" . 'gid";i:8;s:7:"' . "\0" . '*' . "\0" . 'name";s:0:"";s:11:"' . "\0" . '*' . "\0" . 'disabled";b:0;s:14:"' . "\0" . '*' . "\0" . 'domainAdmin";b:0;s:13:"' . "\0" . '*' . "\0" . 'superAdmin";b:1;s:21:"' . "\0" . '*' . "\0" . 'strictFromDisabled";b:0;s:14:"' . "\0" . '*' . "\0" . 'referenceId";N;s:10:"' . "\0" . '*' . "\0" . 'created";O:8:"DateTime":3:{s:4:"date";s:26:"2018-12-14 21:38:33.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:10:"' . "\0" . '*' . "\0" . 'updated";O:8:"DateTime":3:{s:4:"date";s:26:"2018-12-14 21:39:41.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:15:"' . "\0" . '*' . "\0" . 'redirectOnly";b:0;s:13:"' . "\0" . '*' . "\0" . 'redirectTo";a:0:{}}}}s:14:"' . "\0" . '*' . "\0" . 'initialized";b:1;}}s:11:"' . "\0" . '*' . "\0" . 'password";s:120:"{SHA512-CRYPT}$6$acvyLX2N9lsg1F.K$Bop/wS7UxWIH/VzR0uGhopOBSVvwlAdsdxzqXgwluo4VmylnzgGQ5/UhdHZ/1U0UyID8POk2/vGy2h4Q4pGSQ1";s:20:"' . "\0" . '*' . "\0" . 'passwordPlaintext";s:0:"";s:7:"' . "\0" . '*' . "\0" . 'home";s:27:"/data/domains/test.com/test";s:6:"' . "\0" . '*' . "\0" . 'uid";i:8;s:6:"' . "\0" . '*' . "\0" . 'gid";i:8;s:7:"' . "\0" . '*' . "\0" . 'name";s:0:"";s:11:"' . "\0" . '*' . "\0" . 'disabled";b:0;s:14:"' . "\0" . '*' . "\0" . 'domainAdmin";b:0;s:13:"' . "\0" . '*' . "\0" . 'superAdmin";b:1;s:21:"' . "\0" . '*' . "\0" . 'strictFromDisabled";b:0;s:14:"' . "\0" . '*' . "\0" . 'referenceId";N;s:10:"' . "\0" . '*' . "\0" . 'created";r:40;s:10:"' . "\0" . '*' . "\0" . 'updated";r:44;s:15:"' . "\0" . '*' . "\0" . 'redirectOnly";b:0;s:13:"' . "\0" . '*' . "\0" . 'redirectTo";a:0:{}}i:1;b:1;i:2;a:2:{i:0;O:41:"Symfony\\Component\\Security\\Core\\Role\\Role":1:{s:47:"' . "\0" . 'Symfony\\Component\\Security\\Core\\Role\\Role' . "\0" . 'role";s:9:"ROLE_USER";}i:1;O:41:"Symfony\\Component\\Security\\Core\\Role\\Role":1:{s:47:"' . "\0" . 'Symfony\\Component\\Security\\Core\\Role\\Role' . "\0" . 'role";s:16:"ROLE_SUPER_ADMIN";}}i:3;a:0:{}}'));

Expected result:
----------------
Object is decoded and dumped

Actual result:
--------------
Notice: unserialize(): Error at offset 938 of 2517 bytes in /in/gTumn on line 3

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-12-15 13:08 UTC] cmb@php.net
-Package: *General Issues +Package: Variables related
 [2018-12-15 13:08 UTC] cmb@php.net
This behavioral change has been introduced by commit f26fc52[1].
It doesn't appear that the former behavior was correct, though,
since
    s:9:" * domain";r:6
has been unserialized as
    ["domain":protected] => bool(true)

[1] <http://git.php.net/?p=php-src.git;a=commit;h=f26fc527da442943892f265ea48d94a22c29b2bc>
 [2018-12-15 20:02 UTC] kalle@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: dmitry
 [2018-12-15 20:02 UTC] kalle@php.net
Dmitry, can you please clarify?
 [2018-12-19 23:57 UTC] nikic@php.net
Has the serialized string been produced by a vanilla `serialize()` call that is not nested within a Serializable interface or similar? The r:6 reference looks off-by-one to me.
 [2018-12-23 10:36 UTC] me at famoser dot ch
The bug has an issue in the symfony repository on github: https://github.com/symfony/symfony/issues/29459
This PR shows how the serialize/unserialize calls were used: https://github.com/symfony/symfony/pull/29621/files
 [2018-12-25 13:43 UTC] php at famoser dot ch
I have managed to create an example to reliably reproduce the behaviour: https://github.com/mangelio/app/blob/php-bug-%2377302/README.BUG77302.md 

The example is unfortunately not separated cleanly from the symfony framework and my specific project; it is therefore still hard to tell how and why the bug is hit. I will try to isolate the root cause further, but the example may already help others.
 [2019-01-03 10:26 UTC] nikic@php.net
@Sjon provided the following test case with a similar issue: https://3v4l.org/BEobu That one is an instance of bug #66052 though, where the stricter validation in 7.3 makes the unserialization fail rather than return bogus data.

The case in this report looks different though, as you don't have any "C:" payloads in the serialized data.
 [2019-01-14 13:23 UTC] dmitry@php.net
-Status: Assigned +Status: Feedback
 [2019-01-14 13:23 UTC] dmitry@php.net
The problem is caused by different order during serialisation and unserialization and therefore different reference numbering. This only happens when Serializable objects call serialize/unserialize functions few times.

function serialize() { 
    return serialize(array($this->data, parent::serialize())); 
}

function unserialize($data) { 
    list($this->$data, $parent_data) = unserialize($data); 
    parent::unserialize($parent_data); 
}

The problem can't be fixed without format change (e.g. we may use string position instead of object number).

As a workaround, I propose avoiding nested serialization like parent::serialize() calls.
 [2019-01-22 12:07 UTC] o dot ilyushyn at superbody dot com
No fix so far? Really? Can you rollback serialization functions like it was in 7.2?
 [2019-01-22 18:32 UTC] jusiacms at gmail dot com
A year of work on the next version and release with such an error. Eh.
 [2019-01-22 19:57 UTC] dmitry@php.net
7.2 and below are affected by the same problem.
The old versions don't fail, but silently produce incorrect result (not the same as was serialized).
 [2019-01-22 20:23 UTC] nikic@php.net
I plan to propose and implement a new custom object serialization mechanism for PHP 7.4, to replace the Serializable interface and all the problems that come with it.

For now, all I can suggest is to rewrite your code in a way that does not use parent::serialize(). I don't think there is anything we can do to fix Serializable itself, unfortunately.
 [2019-01-22 22:59 UTC] cmb@php.net
-Status: Feedback +Status: Open
 [2019-02-21 09:30 UTC] felix at moches dot de
I think this is the same bug, but quite simple to reproduce: https://3v4l.org/cTPe9

As soon as at least two serializable objects are pushed to a collection, it cannot be serialized correctly anymore.

This is really bad news for any caching solution.
 [2019-02-21 10:50 UTC] nikic@php.net
@felix: That's not quite the same bug, and something we can actually fix...

I wasn't aware that returning null from Serializable::serialize() is a thing. This was allowed in https://github.com/php/php-src/commit/d77945ef78aead05a3adb6307ccedc12d0ca49ee, but I'm wondering why... Do you happen to know?
 [2019-02-26 18:00 UTC] felix at moches dot de
Thank you very much.
 [2019-02-26 18:06 UTC] felix at moches dot de
Oh, and concerning your question: The docs at http://php.net/manual/en/serializable.serialize.php say:
"Return Values: Returns the string representation of the object or NULL"

Although the return type declaration explicitly states "string" and not "?string".
But this is probably not PHP7.1+ syntax anyways.
 [2019-06-27 15:06 UTC] aco at itn dot me
So, I am on PHP Version 7.2.19-0ubuntu0.18.04.1
But I have same problem, can I get the function directly, or I need to update to which PHP version ?
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Aug 18 09:01:27 2019 UTC