php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #65967 SplObjectStorage contains corrupt member variables after garbage collection
Submitted: 2013-10-25 16:25 UTC Modified: 2016-03-18 19:39 UTC
Votes:15
Avg. Score:4.2 ± 0.8
Reproduced:14 of 14 (100.0%)
Same Version:9 (64.3%)
Same OS:10 (71.4%)
From: crog at gustavus dot edu Assigned: nikic (profile)
Status: Closed Package: SPL related
PHP Version: 5.2+ OS: Any
Private report: No CVE-ID: None
 [2013-10-25 16:25 UTC] crog at gustavus dot edu
Description:
------------
If garbage collection is run after creating an SplObjectStorage instance, the object will contain a property which cannot be processed by several other PHP functions (namely, serialization and variable exporting [var_dump, var_export, etc]).

This is caused by the "\x00gcdata" property that is created in the spl_object_storage_get_gc function in spl_observer.c:365 (in php 5.4). This property gets included in the object's property collection and prevents the object from being used by anything that scans the property collection.

Test script:
---------------
$objstore = new SplObjectStorage();
gc_collect_cycles();

var_dump($objstore);

print serialize($objstore);

Expected result:
----------------
object(SplObjectStorage)[6]
C:16:"SplObjectStorage":14:{x:i:0;m:a:0:{}}

Actual result:
--------------
( ! ) Notice: Corrupt member variable name in /cis/www/rog/tester.php on line 97
Call Stack
#	Time	Memory	Function	Location
1	0.0005	308976	{main}( )	../tester.php:0
2	0.0027	768160	var_dump ( ... )	../tester.php:97
Dump $_POST

object(SplObjectStorage)[6]
  public '' => 
    array (size=0)
      empty
C:16:"SplObjectStorage":34:{x:i:0;m:a:1:{s:7:"gcdata";a:0:{}}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-10-29 14:25 UTC] crog at gustavus dot edu
We've discovered that this problem is related to a segfault/zend_mm_heap corruption that occurs when unserializing a large (?) object consisting of several SplObjectStorage instances.

We've temporarily worked around the issue with a simple string replacement on serialization:

$serialized = str_replace("\x00gcdata", '_gcdata', serialize($this->data));

Of course, this leaves the gcdata floating around with no real purpose and we're not sure of the long-term effects of the eventual doubling of that _gcdata variable, so we're hoping for a fix sooner than later.


Backtrace:
(Note: This was taken with the garbage collection disabled. When GC is enabled, the heap is simply corrupted [zend_mm_heap corrupted] and doesn't fail with a core dump/backtrace)

(gdb) bt full
#0  zend_mm_remove_from_free_list (heap=0xbe42e0, mm_block=0x1379478)
    at /usr/src/debug/php-5.4.21/Zend/zend_alloc.c:833
        prev = 0x68cc2cb934f70384
        next = 0x29
#1  0x00000000005bcdd3 in _zend_mm_free_int (heap=0xbe42e0, p=0x1379488)
    at /usr/src/debug/php-5.4.21/Zend/zend_alloc.c:2101
        mm_block = 0x1379478
        next_block = 0x1379478
        size = 0
#2  0x00000000005ef4d1 in zend_hash_destroy (ht=0x12f2250)
    at /usr/src/debug/php-5.4.21/Zend/zend_hash.c:565
        p = 0x1379510
        q = 0x1379488
#3  0x000000000060417c in zend_object_std_dtor (object=0x1312780)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:44
No locals.
#4  0x0000000000604209 in zend_objects_free_object_storage (object=0x1312780)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:137
No locals.
#5  0x000000000060a14c in zend_objects_store_del_ref_by_handle_ex (handle=416,
    handlers=<value optimized out>) at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:226
        __orig_bailout = 0x7fffffffc780
        __bailout = {{__jmpbuf = {19999776, 3201897196840651886, 20502008, 140737488349162,
              0, 0, -3201899731257029522, 3201898902524759150}, __mask_was_saved = 0,
            __saved_mask = {__val = {140732768597912, 1376, 6016370, 88, 6016370, 136,
                6016370, 104, 6016370, 20503224, 19865104, 176, 6016370, 272, 6016370,
                1200}}}}
        obj = 0x7ffee6694838
        failure = <value optimized out>
#6  0x000000000060a173 in zend_objects_store_del_ref (zobject=0x1312c20)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:178
        handle = <value optimized out>
#7  0x00000000005d478a in _zval_dtor (zval_ptr=0x138d5a8)
    at /usr/src/debug/php-5.4.21/Zend/zend_variables.h:35
No locals.
#8  _zval_ptr_dtor (zval_ptr=0x138d5a8)
    at /usr/src/debug/php-5.4.21/Zend/zend_execute_API.c:436
No locals.
#9  0x00000000005ef4ab in zend_hash_destroy (ht=0x12f3130)
    at /usr/src/debug/php-5.4.21/Zend/zend_hash.c:560
        p = 0x138d5f8
        q = 0x138d590
#10 0x000000000060417c in zend_object_std_dtor (object=0x1313f38)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:44
No locals.
#11 0x0000000000604209 in zend_objects_free_object_storage (object=0x1313f38)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:137
No locals.
#12 0x000000000060a14c in zend_objects_store_del_ref_by_handle_ex (handle=423,
    handlers=<value optimized out>) at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:226
        __orig_bailout = 0x7fffffffc900
        __bailout = {{__jmpbuf = {140732764080624, 3201897323198254190, 20509584,
              140737488349162, 0, 0, -3201899731158463378, 3201898902524759150},
            __mask_was_saved = 0, __saved_mask = {__val = {3201897415757106286, 0,
                140737488349162, 0, 0, 88, 6016370, 19858328, 19854112, 80, 6016370, 136,
                6016370, 96, 6016370, 20509848}}}}
        obj = 0x7ffee66949f8
        failure = <value optimized out>
#13 0x000000000060a173 in zend_objects_store_del_ref (zobject=0x1314f70)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:178
        handle = <value optimized out>
#14 0x00000000005d478a in _zval_dtor (zval_ptr=0x1379f20)
    at /usr/src/debug/php-5.4.21/Zend/zend_variables.h:35
No locals.
#15 _zval_ptr_dtor (zval_ptr=0x1379f20)
    at /usr/src/debug/php-5.4.21/Zend/zend_execute_API.c:436
No locals.
#16 0x0000000000505d89 in spl_object_storage_dtor (element=0x1379f20)
    at /usr/src/debug/php-5.4.21/ext/spl/spl_observer.c:182
No locals.
#17 0x00000000005ef4ab in zend_hash_destroy (ht=0x138c7f0)
    at /usr/src/debug/php-5.4.21/Zend/zend_hash.c:560
        p = 0x138f390
        q = 0x138e180
#18 0x0000000000507512 in spl_SplOjectStorage_free_storage (object=0x138c7d0)
    at /usr/src/debug/php-5.4.21/ext/spl/spl_observer.c:103
        intern = 0x138c7d0
#19 0x000000000060a14c in zend_objects_store_del_ref_by_handle_ex (handle=422,
    handlers=<value optimized out>) at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:226
        __orig_bailout = 0x7fffffffca90
        __bailout = {{__jmpbuf = {19918568, 3201897197490769006, 20420720, 140737488349162,
              0, 0, -3201899731074577298, 3201898902524759150}, __mask_was_saved = 0,
            __saved_mask = {__val = {140737488341712, 140732764073968, 3201897277952199790,
                20436200, 140737488349162, 32, 6016370, 240, 6016370, 20421464, 19874576, 80,
                6016370, 408, 6016370, 936}}}}
        obj = 0x7ffee66949b8
        failure = <value optimized out>
#20 0x000000000060a173 in zend_objects_store_del_ref (zobject=0x12feee8)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:178
        handle = <value optimized out>
#21 0x00000000005d478a in _zval_dtor (zval_ptr=0x1379820)
    at /usr/src/debug/php-5.4.21/Zend/zend_variables.h:35
No locals.
#22 _zval_ptr_dtor (zval_ptr=0x1379820)
    at /usr/src/debug/php-5.4.21/Zend/zend_execute_API.c:436
No locals.
#23 0x00000000005ef4ab in zend_hash_destroy (ht=0x12f2250)
    at /usr/src/debug/php-5.4.21/Zend/zend_hash.c:560
        p = 0x1379870
        q = 0x1379808
#24 0x000000000060417c in zend_object_std_dtor (object=0x1312780)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:44
No locals.
#25 0x0000000000604209 in zend_objects_free_object_storage (object=0x1312780)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:137
No locals.
#26 0x000000000060a14c in zend_objects_store_del_ref_by_handle_ex (handle=416,
    handlers=<value optimized out>) at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:226
        __orig_bailout = 0x7fffffffcc50
        __bailout = {{__jmpbuf = {140732764080176, 3201897196626742382, 0, 140737488349162,
              0, 0, -3201899730992788370, 3201898902524759150}, __mask_was_saved = 0,
            __saved_mask = {__val = {3201897420299537518, 19981896, 140737488349162, 0, 0,
                15244844342691597422, 3201898902524759150, 0, 0, 15244844342687403118,
                3201898902524759150, 0, 6016370, 64, 6016370, 88}}}}
        obj = 0x7ffee6694838
        failure = <value optimized out>
#27 0x000000000060a173 in zend_objects_store_del_ref (zobject=0x1312c20)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:178
        handle = <value optimized out>
#28 0x00000000005d478a in _zval_dtor (zval_ptr=0x12efa20)
    at /usr/src/debug/php-5.4.21/Zend/zend_variables.h:35
No locals.
#29 _zval_ptr_dtor (zval_ptr=0x12efa20)
    at /usr/src/debug/php-5.4.21/Zend/zend_execute_API.c:436
No locals.
#30 0x00000000005ef4ab in zend_hash_destroy (ht=0x12f0b90)
    at /usr/src/debug/php-5.4.21/Zend/zend_hash.c:560
        p = 0x0
        q = 0x12efa08
#31 0x00000000005e148b in _zval_dtor_func (zvalue=0x135b070)
    at /usr/src/debug/php-5.4.21/Zend/zend_variables.c:45
No locals.
#32 0x00000000005d478a in _zval_dtor (zval_ptr=0x130e660)
    at /usr/src/debug/php-5.4.21/Zend/zend_variables.h:35
No locals.
#33 _zval_ptr_dtor (zval_ptr=0x130e660)
    at /usr/src/debug/php-5.4.21/Zend/zend_execute_API.c:436
No locals.
#34 0x00000000005ef4ab in zend_hash_destroy (ht=0x13535a8)
    at /usr/src/debug/php-5.4.21/Zend/zend_hash.c:560
        p = 0x130eb30
        q = 0x130e648
#35 0x000000000060417c in zend_object_std_dtor (object=0x1353ce8)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:44
No locals.
#36 0x0000000000604209 in zend_objects_free_object_storage (object=0x1353ce8)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects.c:137
No locals.
#37 0x0000000000609c4c in zend_objects_store_free_object_storage (objects=0x9f3740)
    at /usr/src/debug/php-5.4.21/Zend/zend_objects_API.c:97
        obj = <value optimized out>
        i = <value optimized out>
#38 0x00000000005d5953 in shutdown_executor ()
    at /usr/src/debug/php-5.4.21/Zend/zend_execute_API.c:295
        __orig_bailout = 0x7fffffffe360
        __bailout = {{__jmpbuf = {10433408, 3201899266092141678, 140737488348528,
              140737488349162, 0, 0, -3201899730923582354, 3201899276952112238},
            __mask_was_saved = 0, __saved_mask = {__val = {3201899133432204398, 0, 6016370,
                32, 6016370, 184, 6016370, 140737353949184, 15537104, 104, 6016370,
                140732767562360, 140732767788136, 88, 15432352, 10434720}}}}
#39 0x00000000005e2802 in zend_deactivate () at /usr/src/debug/php-5.4.21/Zend/zend.c:938
No locals.
#40 0x0000000000584f7c in php_request_shutdown (dummy=<value optimized out>)
    at /usr/src/debug/php-5.4.21/main/main.c:1808
        report_memleaks = 1 '\001'
#41 0x000000000068c55f in do_cli (argc=4, argv=0x7fffffffe558)
    at /usr/src/debug/php-5.4.21/sapi/cli/php_cli.c:1172
        c = <value optimized out>
        file_handle = {type = ZEND_HANDLE_MAPPED,
          filename = 0x7fffffffe7ea "/usr/bin/phpunit", opened_path = 0x0, handle = {
            fd = -134399096, fp = 0x7ffff7fd3b88, stream = {handle = 0x7ffff7fd3b88,
              isatty = 0, mmap = {len = 2019, pos = 0, map = 0x7ffee6f94000,
                buf = 0x7ffee6f9400f "", old_handle = 0xeb7c50,
                old_closer = 0x5f8a70 <zend_stream_stdio_closer>},
              reader = 0x5f9090 <zend_stream_stdio_reader>,
              fsizer = 0x5f8b20 <zend_stream_stdio_fsizer>,
              closer = 0x5f9010 <zend_stream_mmap_closer>}}, free_filename = 0 '\000'}
        behavior = <value optimized out>
        reflection_what = <value optimized out>
        request_started = 1
        exit_status = 0
        php_optarg = 0x7fffffffe7de "gc_enable=0"
        php_optind = 4
        exec_direct = <value optimized out>
        exec_run = <value optimized out>
        exec_begin = <value optimized out>
        exec_end = <value optimized out>
        arg_free = <value optimized out>
        arg_excp = <value optimized out>
        script_file = <value optimized out>
        translated_path = 0xeb7e90 "/usr/bin/phpunit"
        interactive = <value optimized out>
        lineno = 2
        param_error = 0x6f9ff0 "Either execute direct code, process stdin or use a file.\n"
        hide_argv = <value optimized out>
#42 0x000000000068d928 in main (argc=4, argv=0x7fffffffe558)
    at /usr/src/debug/php-5.4.21/sapi/cli/php_cli.c:1365
        __orig_bailout = 0x0
        __bailout = {{__jmpbuf = {7596570286683205949, 4039469594811778698,
              7883960575912274292, 8458716092903159905, 7857145540187679090,
              7306640099802838133, -3201899730208453522, 3201898834450287726},
            __mask_was_saved = 0, __saved_mask = {__val = {140737488348544, 25, 270252091729,
                270278976984, 140737488348144, 270249628040, 270276808342, 194,
                140737488348206, 18446744073600302988, 4096, 0, 0, 0, 6903184, 0}}}}
        c = <value optimized out>
        exit_status = 0
        module_started = 1
        sapi_started = 1
        php_optarg = 0x7fffffffe7de "gc_enable=0"
        php_optind = 3
        use_extended_info = 0
        ini_path_override = 0x0
        ini_entries = 0xbe4250 "html_errors=0\nregister_argc_argv=1\nimplicit_flush=1\noutput_buffering=0\nmax_execution_time=0\nmax_input_time=-1\ngc_enable=0\n"
        ini_entries_len = <value optimized out>
        ini_ignore = 0
        sapi_module = 0x9d8360
 [2013-11-20 14:29 UTC] sjon at hortensius dot net
I am unable to reproduce this in any version, see http://3v4l.org/nRbiO
 [2013-11-22 19:18 UTC] crog at gustavus dot edu
Good catch. When I was writing the script to trigger the issue, I was testing it through the browser. Running on the command-line, I can't trigger it there, either (despite a few efforts at making terribly large circular structures).

Just to be certain, I did run the test again through the browser, ensuring our auto-append and prepends were disabled to check that it still happens (it does).

Some relevant bits from phpinfo():
Build Date      Nov 13 2013 09:29:31 
Server API      Apache 2.0 Handler 
Apache Version  Apache/2.2.15 (Red Hat) 


In any event, it doesn't even matter. The problem is that SplObjectStorage adds properties that contain null bytes (which you can see by looking at the source -- it's even commented as intentional to "make tampering in user-land more difficult" (spl_observer.c:391).

The negative effects of this "bug," aren't that the property exists, it's that other PHP functions explode horribly when they encounter a property containing a null byte. If that's all we're interested in observing, we can do so a lot easier (and on the command-line :D ) with the following:

  var_dump((object) ["\x00key" => "value"]);


So, that said, I suppose this issue could be resolved by changing everything else to not choke on null bytes in property names; but it seems slightly more reasonable to change SplObjectStorage such that it doesn't store its internal data in the object itself (and/or not use a null byte in its property names).
 [2013-11-22 19:25 UTC] crog at gustavus dot edu
Sorry, forgot links:
SplObjectStorage \x00gcdata entry:
http://lxr.php.net/xref/PHP_5_4/ext/spl/spl_observer.c#391

Corrupt member name example:
http://3v4l.org/jfcJr
 [2014-03-02 15:25 UTC] adrian at foeder dot de
Totally having the same issue on Windows 8, PHP 5.4.11 (Zend Server CE).
Did anybody try this with igbinary? Does it happen there too?
 [2014-03-25 13:59 UTC] levim@php.net
-Status: Open +Status: Verified -Operating System: Linux/Redhat +Operating System: Any -PHP Version: 5.4.21 +PHP Version: 5.2+
 [2014-03-25 13:59 UTC] levim@php.net
Can confirm that this affects PHP 5.2 through 5.6 alpha 3 on several OS's.
 [2015-03-13 17:01 UTC] laruence@php.net
Automatic comment on behalf of adam.scarr@99designs.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=950d3d6e9b94b75b266c67bf9e3a85ae9c31905d
Log: Fix bug #69227 and #65967
 [2016-03-18 19:39 UTC] nikic@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: nikic
 [2016-03-18 19:39 UTC] nikic@php.net
Closing, as this is fixed by the aforementioned commit. Automatic closer didn't pick that format up.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Thu Jan 23 15:01:24 2020 UTC