php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #67363 Unserialize corrupts data
Submitted: 2014-05-30 15:26 UTC Modified: 2017-01-07 22:15 UTC
Votes:11
Avg. Score:4.9 ± 0.3
Reproduced:10 of 10 (100.0%)
Same Version:5 (50.0%)
Same OS:6 (60.0%)
From: mg at artigo dot pl Assigned:
Status: Open Package: Variables related
PHP Version: Irrelevant OS: Irrelavant
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2014-05-30 15:26 UTC] mg at artigo dot pl
Description:
------------
Unserialize method of PHP corrupts random values inside serialized objects. Corrupted value may be a string (in this case it is clearly visible on the website).

We have encountered this issue on every PHP version from 5.3 to 5.5
and multiple environments (linux, windows). PHP 5.2 was free from this bug (version switching proved it).

The corruption occurs only on some value combinations, for example after we change one of the string values the corruption does not appear, when we changed it back: the corruption occurs again.
The type of object does not matter, it often happens for stdClass.

Please check an example object, which was used in Test script:
https://dl.dropboxusercontent.com/u/11435743/php/load.txt
Please compare it with the object which is an output of unserialize function.
https://dl.dropboxusercontent.com/u/11435743/php/load_corrupt.txt

Clearly one of the values has changed:
Instead of "Make-up & Styling", value is now: "Make-up ' Sty 
Please note that pasting of the corrupt string works only until letter "y", please check the load_corrupt.txt file to see the full string.

The corruption may have a various form, it can be an additional letter, changed letter, for example: "Stylid" instead of "Styling".

Test script:
---------------
file_put_contents('load.txt', $data); // correct object
file_put_contents('load_corrupt.txt', serialize(unserialize($data))); // one of the value was changed by unserialize, we serialize it back to save it to file

Expected result:
----------------
"make-up-styling";s:4:"name";s:17:"Make-up & Styling"

Actual result:
--------------
s:15:"make-up-styling";s:4:"name";s:17:"Make-up ' Sty

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-05-30 16:55 UTC] mg at artigo dot pl
It seems that serialize function has something to do with it, it gives different outputs:

Case1, output of:
serialize(unserialize(serialize($data)));
https://dl.dropboxusercontent.com/u/11435743/php/serialize_unserialize_serialize.txt

Case 2, output of:
serialize($data)
https://dl.dropboxusercontent.com/u/11435743/php/serialize.txt

I attach this example, because data corruption does seem to occur in Case 1. It seems only to occur in Case 2.
 [2014-05-30 17:00 UTC] mg at artigo dot pl
Please excuse the error in the sentence:

I attach this example, because data corruption does NOT seem to occur in Case 1. It seems only to occur in Case 2.
 [2014-05-30 17:18 UTC] mg at artigo dot pl
It turns out I simplified Case 1 too much, I attach the whole information again:
------------

It seems that serialize function has something to do with it. It returns different outputs in the following cases:

Case 1:
unserialize(serialize($data));
echo serialize($data); // output:
https://dl.dropboxusercontent.com/u/11435743/php/serialize_unserialize_serialize.txt

Case 2:
echo serialize($data); // output:
https://dl.dropboxusercontent.com/u/11435743/php/serialize.txt

Data corruption DOES NOT occur when I run unserialize on the output of Case 1.
Data corruption DOES occur when I run unserialize on the output of Case 2.

Example of the currupted outputs are attached in the original post.

To summarize: putting additional unserialize(serialize($data)); before serialize($data); seems to workaround the corruption by resulting in a different output of the second call of serialize function. This output can then be safely passed to unserialize function and the corruption does not occur.
 [2014-06-02 06:52 UTC] mg at artigo dot pl
The information in comments is irrelevant. The corruption does not occur, because the object itself changes, but it is the same with any change of it - only some combinations of values cause corruption. The difference in serialize output is related to classes serialize method which removes the "parent" field.
 [2014-06-02 07:57 UTC] mg at artigo dot pl
Regarding the original problem with unserialize corruption issue:

In this single case "Make-up & Styling" became "Make-up ' Sty ing" 
& = hex 26 became ' = hex 27 
l = hex 6c became (nul) = hex 00

The changes in characters are always different and seem random.

It is worth mentioning that the issue appears out of nowhere and can be fixed by restarting the server. It seems memory related. For this reason it is not possible to provide a bug testing snippet, because it simply won't create the same issue on another machine, although it may get it's own unique corruption on a random day.

The issue presents itself always on Apache server (various versions 2-2.4), mod_php or FCGI.
 [2014-06-05 15:33 UTC] mg at artigo dot pl
I just experienced the same issue on a totally different setup
apache + mod_php 5.5.11 + zend opcache

String "Opinion Leader des Tages"
became "Opinion Leader dfs Taes"

after unserialize.
 [2014-06-27 11:18 UTC] mg at artigo dot pl
We just had the same issue on a different server (FCGI 5.4.27)
String currupted by unserialize:
"ticketshop-promo-summer-ticket" => "ticketshop-promo.summ(NUL)r-ticket"
 [2014-06-27 11:25 UTC] mg at artigo dot pl
3 more:

"ticketshop-kilometer-bank" => "ticketshop-kilomfter-(NUL)ank"
"ticketshop-promo-spar-holiday" => "ticketshop-promo.spar(NUL)holiday"
"ticketshop-promo-weekend" => "ticketshop-promo.week(NUL)nd"
 [2017-01-01 12:44 UTC] nikic@php.net
-Status: Open +Status: Feedback
 [2017-01-01 12:44 UTC] nikic@php.net
Is someone still encountering this problem in PHP >= 5.6? If so, could you please specify whether you use opcache? If you do, can you check whether you get a reproducible crash if you set opcache.protect_memory=1?
 [2017-01-01 13:31 UTC] mg at artigo dot pl
-Status: Feedback +Status: Open
 [2017-01-01 13:31 UTC] mg at artigo dot pl
Dear Nikita,

I wish you a Happy New Year with lots of happiness and joy. 

It has been so long since the issue was reported, that we managed to implement some workarounds in the meantime and we do not encounter it anymore.
That said, if I encounter it again, I will open another issue, ok?

Thanks,
Marcin
 [2017-01-01 13:36 UTC] mg at artigo dot pl
Maybe I should add that the issue was present for sure without opcache and it was not the case of shared memory, because we could repeat it any time, on any day with the same result and same corruption.
 [2017-01-07 17:45 UTC] php at laszlokorte dot de
Not sure if this is the correct issue but I came across some problem with serialize/unserialize. (php-5.6.14)

I have created a gist to reproduce it:

https://gist.github.com/laszlokorte/3948f40873346cc1fd9b8c11ab06ae04

The problem is that if an object contains a another object as child in multiple places that child does not get (un)serialized correctly. Not just that the identity of the multiple occurrences is not preserved but the object is not unserialized correctly at all. It's easier to understand by looking at gist.
 [2017-01-07 17:58 UTC] nikic@php.net
@laszlokorte: That is most likely bug #66085, which is fixed in PHP 7.0 (but not 5.6).
 [2017-01-07 18:36 UTC] php at laszlokorte dot de
@nikic Thanks, indeed the problem of the gist I posted is solved by php7. But in my real application I now get many "Notice: unserialize(): Error at offset 1026 of 1348 bytes" errors.
 [2017-01-07 19:57 UTC] jh1711 at xmail dot net
@laszlokorte, there's a mistake in your gist. Definition::unserialize should pass $data to unserialize, or use $value as a parameter. Unserializing an uninitialized variable causes the false values, and not unserializing the parameter messes up the references. See https://3v4l.org/vioIT .

@nikic, imho the cause of bug #66085 is serializing additional data during serialization. Of course this can result in similar behaviour. See https://3v4l.org/9hVS2 .
 [2017-01-07 22:08 UTC] php at laszlokorte dot de
@jh1711 You re right. I fixed the bug in the gist. But mainDef is not correctly unserialized in php<7 anyway and in my real application I still get the "unserialize(): Error at offset 1026 of 1348 byte error" in a similar but more complex situation (deeper nestings, more properties, but no cycles)
 [2017-01-07 22:15 UTC] nikic@php.net
@laszlokorte: In that case you're probably hit by bug #66052 (see also my comment on bug #73253).
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Nov 17 00:01:34 2019 UTC