php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79031 Session unserialization problem
Submitted: 2019-12-25 09:52 UTC Modified: 2019-12-30 11:29 UTC
Votes:2
Avg. Score:5.0 ± 0.0
Reproduced:2 of 2 (100.0%)
Same Version:2 (100.0%)
Same OS:0 (0.0%)
From: syberon at gmail dot com Assigned: nikic (profile)
Status: Closed Package: Session related
PHP Version: 7.4.1 OS: Windows 10 1909
Private report: No CVE-ID: None
 [2019-12-25 09:52 UTC] syberon at gmail dot com
Description:
------------
Since version 7.4 of PHP the unserialize function can't process object references from serialized session strings.

For example, i have a few object stored in session with 3 namespaces (i exploded it to readability:

__ZF|a:2:{s:20:"_REQUEST_ACCESS_TIME";d:1577256665.394478;s:6:"_VALID";a:1:{s:25:"Zend\Session\Validator\Id";s:26:"uk2ngahnpc35k4rfsqd97kf9e6";}}
FlashMessenger|C:23:"Zend\Stdlib\ArrayObject":205:{a:4:{s:7:"storage";a:0:{}s:4:"flag";i:2;s:13:"iteratorClass";s:13:"ArrayIterator";s:19:"protectedProperties";a:4:{i:0;s:7:"storage";i:1;s:4:"flag";i:2;s:13:"iteratorClass";i:3;s:19:"protectedProperties";}}}

MtCms\User\Authentication\Adapter\DatabaseAdapter|C:23:"Zend\Stdlib\ArrayObject":644:{a:4:{s:7:"storage";a:1:{s:7:"storage";a:2:{s:12:"is_satisfied";b:1;s:8:"identity";O:8:"stdClass":13:{s:2:"id";s:1:"1";s:8:"username";s:5:"admin";s:5:"email";s:17:"syberon@gmail.com";s:12:"display_name";s:6:"Andrey";s:7:"picture";s:0:"";s:8:"delivery";s:1:"1";s:4:"city";s:22:"Новосибирск";s:9:"city_code";s:3:"270";s:8:"zip_code";s:6:"630000";s:7:"address";s:16:"ываывавы";s:5:"phone";s:4:"4234";s:5:"state";s:1:"1";s:10:"persistent";s:1:"1";}}}s:4:"flag";i:2;s:13:"iteratorClass";s:13:"ArrayIterator";s:19:"protectedProperties";a:4:{i:0;s:7:"storage";i:1;s:4:"flag";i:2;s:13:"iteratorClass";i:3;s:19:"protectedProperties";}}}

Zend_Auth|C:23:"Zend\Stdlib\ArrayObject":224:{a:4:{s:7:"storage";a:1:{s:7:"storage";r:20;}s:4:"flag";i:2;s:13:"iteratorClass";s:13:"ArrayIterator";s:19:"protectedProperties";a:4:{i:0;s:7:"storage";i:1;s:4:"flag";i:2;s:13:"iteratorClass";i:3;s:19:"protectedProperties";}}}

Second namespace 'MtCms\User\Authentication\Adapter\DatabaseAdapter' has object of class 'Zend\Stdlib\ArrayObject' that has a property with name 'storage' that contains anothe object.

Third namespace 'Zend_Auth' has the object of class 'Zend\Stdlib\ArrayObject' too. And this object has the same property 'storage' but it has value r:20 (reference to object of second namespace)

PHP 7.3 parses this serialized session data without any problems and at result we have a objects from 2nd and 3rd namespaces with property 'storage' what have identically object contain in.

But then i upgraded to PHP 7.4 the session_start() function give the error when trying to parse this session data. It can not recognize 'r:20' reference in object of 3rd namespace.


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-12-27 04:12 UTC] syberon at gmail dot com
-Package: Class/Object related +Package: Session related
 [2019-12-27 04:12 UTC] syberon at gmail dot com
I found that this problem is related to PHP 7.4 session unserializing. I made some tests and found that if some class (Zend\Stdlib\ArrayObject in my case) implements Serializable interface and has some property (object of stdClass for example) it failed to decode if stored in session.

I made a minimal reproducible example:

<?php
class SerializableClass implements Serializable {
    public $sharedProp;
    public function __construct($prop)
    {
        $this->sharedProp = $prop;
    }
    public function __set($key, $value)
    {
        $this->$key = $value;
    }
    public function serialize()
    {
        return serialize(get_object_vars($this));
    }
    public function unserialize($data)
    {
        $ar = unserialize($data);
        foreach ($ar as $k => $v) {
            $this->__set($k, $v);
        }
    }
}

// Shared object that acts as property of two another objects stored in session
$testPropertyObj = new stdClass();
$testPropertyObj->name = 'test';

// Two instances of \SerializableClass that shares property
$sessionObject = [
    'obj1' => new SerializableClass($testPropertyObj),
    'obj2' => new SerializableClass($testPropertyObj),
];
session_start();
$_SESSION = $sessionObject;

On first run it creates array with two instances of object with one shared property and store it in session.
On second run it tries to start session and fail to parse the second object stored in session because the it loses reference to property 'name'.

This script runs without any problems on PHP 7.3, and rises the parse errors on PHP 7.4.
 [2019-12-27 08:20 UTC] syberon at gmail dot com
From observable behavior it appears that unserialize implementation when called by session extension C code does not set context for unserialize, resulting in relative numbered references being wrong when used in nested unserialize calls.
 [2019-12-27 09:24 UTC] aleksey at xerkus dot pro
https://3v4l.org/NPt2R This slightly modified example from comment above showcases the issue
https://3v4l.org/HeLT4 This example uses `php_serialize` session serializer and exhibits exactly the same behavior.
 [2019-12-27 16:53 UTC] cmb@php.net
-Status: Open +Status: Verified -Assigned To: +Assigned To: nikic
 [2019-12-27 16:53 UTC] cmb@php.net
This regression has apparently been introduced with commit
b8ef7c3[1].  Nikita, could you have a look please?

[1] <http://git.php.net/?p=php-src.git;a=commit;h=b8ef7c35abd31666d9fb317db4b09a9eef0ede6c>
 [2019-12-30 10:21 UTC] nikic@php.net
Looks like this is a general bug in how serialization locking is handled. Here's a case without using sessions: https://3v4l.org/cZqVf The inner objects should be the same, but they're different.
 [2019-12-30 11:29 UTC] nikic@php.net
-Status: Verified +Status: Closed
 [2019-12-30 11:29 UTC] nikic@php.net
The commit is reverted for 7.4, so I'm closing this issue as fixed.

I've also prepared a fix for the underlying problem targeting PHP 8 here: https://github.com/php/php-src/pull/5039
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 09:01:32 2024 UTC