php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #74804 Segfault when instantiating object in array
Submitted: 2017-06-23 09:25 UTC Modified: 2021-06-02 15:09 UTC
From: steve dot hall+bugs dot php dot net at rg456 dot co dot uk Assigned: cmb (profile)
Status: Closed Package: Reproducible crash
PHP Version: 7.1.6 OS: Alpine 3.4 & Windows 10
Private report: No CVE-ID: None
 [2017-06-23 09:25 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
Description:
------------
I get a segmentation fault on this line: 
https://github.com/symfony/symfony/blob/v3.3.2/src/Symfony/Component/VarDumper/Cloner/VarCloner.php#L123

The line runs perfectly well a few thousand times, but then seg faults at a certain point. 

I have been unable to recreate the bug in anything less than the total application that I am working on, and at this time I'm unable to share the full source code. I have tried capturing the state of all the variables involved using xDebug and reproducing in a standalone script but that doesn't cause the seg fault. I've serialized the variables and then attempted to read them back in and run the code in question but that doesn't cause it either. 

I've tried on Windows 10 with PHP 7.1.6 and Alpine Linux 3.4 with PHP 7.1.6 (in a container) and get the same seg fault on both.





Test script:
---------------
Have been unable to demonstrate in anything less than the full application. 

Expected result:
----------------
No segmentation fault. 

Actual result:
--------------
GDB output and valgrind are here:
https://gist.github.com/sh41/c1b5b6d0f2539357303fa43961fe2c95

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-06-23 09:43 UTC] nikic@php.net
The relevant part of the valgrind log seems to be:

==787== Invalid read of size 8
==787==    at 0x5B2952: gc_remove_from_buffer (in /usr/local/bin/php)
==787==    by 0x5C78BC: zend_objects_store_del (in /usr/local/bin/php)
==787==    by 0x62A9F9: ??? (in /usr/local/bin/php)
==787==    by 0x5D267A: execute_ex (in /usr/local/bin/php)
==787==    by 0x579FB4: zend_call_function (in /usr/local/bin/php)
==787==    by 0x5A7398: zend_call_method (in /usr/local/bin/php)
==787==    by 0x5C2881: zend_objects_destroy_object (in /usr/local/bin/php)
==787==    by 0x5B305D: zend_gc_collect_cycles (in /usr/local/bin/php)
==787==    by 0x5B2900: gc_possible_root (in /usr/local/bin/php)
==787==    by 0x4ED42F: var_destroy (in /usr/local/bin/php)
==787==    by 0x4ED4B1: php_var_unserialize_destroy (in /usr/local/bin/php)
==787==    by 0x4DC2E1: ??? (in /usr/local/bin/php)
==787==    by 0x629F41: ??? (in /usr/local/bin/php)
==787==    by 0x5D267A: execute_ex (in /usr/local/bin/php)
==787==    by 0x579FB4: zend_call_function (in /usr/local/bin/php)
==787==    by 0x499A8A: ??? (in /usr/local/bin/php)
==787==    by 0x629F41: ??? (in /usr/local/bin/php)
==787==    by 0x5D267A: execute_ex (in /usr/local/bin/php)
==787==    by 0x579FB4: zend_call_function (in /usr/local/bin/php)
==787==    by 0x445213: ??? (in /usr/local/bin/php)
==787==    by 0x62AE5F: ??? (in /usr/local/bin/php)
==787==    by 0x5D267A: execute_ex (in /usr/local/bin/php)
==787==    by 0x62CDDF: zend_execute (in /usr/local/bin/php)
==787==    by 0x5890B2: zend_execute_scripts (in /usr/local/bin/php)
==787==    by 0x525FAF: php_execute_script (in /usr/local/bin/php)
==787==    by 0x62F03D: ??? (in /usr/local/bin/php)
==787==    by 0x21AAD0: ??? (in /usr/local/bin/php)
==787==    by 0x401D836: (below main) (in /lib/ld-musl-x86_64.so.1)
==787==  Address 0x4f1c4b0 is 16 bytes after a block of size 320,032 alloc'd
==787==    at 0x4C91A2C: malloc (vg_replace_malloc.c:299)
==787==    by 0x5B2846: gc_init (in /usr/local/bin/php)
==787==    by 0x5880E9: ??? (in /usr/local/bin/php)
==787==    by 0x5A474B: zend_register_ini_entries (in /usr/local/bin/php)
==787==    by 0x525534: php_module_startup (in /usr/local/bin/php)
==787==    by 0x62DE9C: ??? (in /usr/local/bin/php)
==787==    by 0x21A9C2: ??? (in /usr/local/bin/php)
==787== by 0x401D836: (below main) (in /lib/ld-musl-x86_64.so.1)

Looks like we're reading past the end of the GC buffer, but I don't see how that can happen inside that function, as all accesses to the GC buffer are bounds-checked.
 [2017-06-23 10:10 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
I may have stumbled over the problem. We had the https://github.com/igbinary/igbinary extension enabled, but weren't deliberately using anywhere. I have now removed it from the configuration  and cannot cause the seg fault anymore.
 [2017-06-23 10:39 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
Apologies, I spoke to soon. The segfault is happening again. I am attempting to capture a new valgrind without the igbinary extension, but the segfault goes away when I'm running in valgrind.
 [2017-06-23 10:49 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
With 
export USE_ZEND_ALLOC=0
I cannot cause the segfault in valgrind, but as soon as I do
export USE_ZEND_ALLOC=1
The segfault reappears.
New file here: 
https://gist.github.com/sh41/df3d2b8e3695d67ef59653b83e5604d0
 [2017-06-23 11:20 UTC] nikic@php.net
It's okay if you don't get a segfault, the important part is whether you get invalid reads/writes in valgrind. I would suggest to:

 * Try running under php -n and only enable those extensions that are necessary to reproduce this. In particular running with openssl adds a lot of noise to valgrind output, because openssl developers have some very peculiar views on reading uninitialized data from memory.
 * Try setting ZEND_DONT_UNLOAD_MODULES=1 to avoid some of the ???s in the output.
 * Try running with USE_ZEND_ALLOC=0 again and see if there are still invalid read/writes, even if there is no segfault. (Disregard the ones caused by "invalid file descriptor", those don't seem related.)
 [2017-06-23 13:55 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
New valgrind here with "export USE_ZEND_ALLOC=0" and ZEND_DONT_UNLOAD_MODULES=1 and a couple of Invalid reads/writes:
https://gist.github.com/sh41/2dd25967ec598f4f48bbdf049df2e462#file-valgrind-L7126

I will try to eliminate un-needed extensions with -n and provide another one as soon as I can.
 [2017-06-23 14:31 UTC] nikic@php.net
The last valgrind output looks pretty good. This seems to be some kind of GC issue involving destructors and nested GCs. From the valgrind output, what seems to be happening:
 1. During the outer GC, while running a destructor and object is created.
 2. During the inner GC that object is freed.
 3. During the outer GC, there is an attempt to free the object again.

Not sure how we could actually end up in that situation though. It might help to find out which __destruct() method is involved here. (From the trace, the __destruct() might be calling __invoke() on a closure, though it's not clear if that's a direct call.)
 [2017-06-23 14:41 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
Thank you for your time on this nikic, it is much appreciated. 

Here is a valgrind from `php -n` 
https://gist.github.com/sh41/30890b1021cac6bec0530292ffc5fd11

I've included the output of `php -n -i` so that you can see what extensions are loaded. I think that openssl is compiled in, and it's also required by the program I'm running, so don't think I can usefully revert to a version without it. 

Must admit that I'm out of my depth when it comes to php internals, but if it helps, the times I was able to catch the seg fault in action it seemed to happen at this line: https://github.com/symfony/symfony/blob/v3.3.2/src/Symfony/Component/VarDumper/Cloner/VarCloner.php#L123 

Not sure if that helps to point in the right direction or not.

Is there anything else I can do to provide more information on this?
 [2017-07-03 15:44 UTC] nikic@php.net
Based on what we know so far, this is likely an instance of bug #72530.
 [2017-07-08 15:03 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
Hi again nikic, 

I have gone through the code base and made every destructor just return without doing anything if there is an environment variable set:

public function __destruct()
{
    if (null !== getenv('NODESTRUCT')) return;
    //etc...
}

I've then re-run valgrind and it still comes out with things like this:
==63== Invalid read of size 4
==63==    at 0x7901BF: zval_delref_p (zend_types.h:838)
==63==    by 0x7904AC: i_zval_ptr_dtor (zend_variables.h:47)
==63==    by 0x7906A3: zend_object_std_dtor (zend_objects.c:68)
==63==    by 0x77CBB1: zend_gc_collect_cycles (zend_gc.c:1175)
==63==    by 0x77AAF4: gc_possible_root (zend_gc.c:286)
==63==    by 0x728087: gc_check_possible_root (zend_gc.h:149)
==63==    by 0x728137: i_zval_ptr_dtor (zend_variables.h:50)
==63==    by 0x729D4C: _zval_ptr_dtor (zend_execute_API.c:550)
==63==    by 0x60CC03: var_destroy (var_unserializer.c:234)
==63==    by 0x60C5B1: php_var_unserialize_destroy (var_unserializer.c:53)
==63==    by 0x5FB926: zif_unserialize (var.c:1131)
==63==    by 0x7A918A: ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:876)
==63==    by 0x7A7F69: execute_ex (zend_vm_execute.h:429)
==63==    by 0x72B19D: zend_call_function (zend_execute_API.c:855)
==63==    by 0x59B30D: zif_call_user_func_array (basic_functions.c:4860)
==63==    by 0x7A918A: ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:876)
==63==    by 0x7A7F69: execute_ex (zend_vm_execute.h:429)
==63==    by 0x72B19D: zend_call_function (zend_execute_API.c:855)
==63==    by 0x51B617: reflection_method_invoke (php_reflection.c:3325)
==63==    by 0x51B7E7: zim_reflection_method_invokeArgs (php_reflection.c:3361)
==63==    by 0x7A961A: ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:970)
==63==    by 0x7A7F69: execute_ex (zend_vm_execute.h:429)
==63==    by 0x7A807A: zend_execute (zend_vm_execute.h:474)
==63==    by 0x744DCD: zend_execute_scripts (zend.c:1476)
==63==    by 0x6A8673: php_execute_script (main.c:2537)
==63==    by 0x82EF29: do_cli (php_cli.c:993)
==63==    by 0x8300F0: main (php_cli.c:1381)


Have I misunderstood the cause of https://bugs.php.net/bug.php?id=72530 or is it possible that this fault has a different cause? My logic being that #72530 is caused by behaviour in the destructor, and I'm reasonably sure there is nothing happening in any of my destructors. 

Thanks
 [2021-06-02 15:01 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2021-06-02 15:01 UTC] cmb@php.net
Is this still an issue with any of the actively supported PHP
versions[1]?

[1] <https://www.php.net/supported-versions.php>
 [2021-06-02 15:09 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
-Status: Feedback +Status: Closed
 [2021-06-02 15:09 UTC] steve dot hall+bugs dot php dot net at rg456 dot co dot uk
I haven't seen this problem again since upgrading the version of PHP, closing the ticket.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 09:01:30 2024 UTC