php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78010 Segmentation fault during GC
Submitted: 2019-05-14 12:37 UTC Modified: 2019-07-15 10:08 UTC
Votes:4
Avg. Score:5.0 ± 0.0
Reproduced:3 of 3 (100.0%)
Same Version:3 (100.0%)
Same OS:2 (66.7%)
From: valera dot ymnik at gmail dot com Assigned: nikic (profile)
Status: Closed Package: Reproducible crash
PHP Version: 7.3.5 OS: Debian 9 && Ubuntu 18.04
Private report: No CVE-ID: None
 [2019-05-14 12:37 UTC] valera dot ymnik at gmail dot com
Description:
------------
Reproduced on "PHP 7.3.5-1+0~20190503093827.38+stretch~1.gbp60a41b (cli) (built: May  3 2019 09:38:28) ( NTS )" and "PHP 7.3.5-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: May  3 2019 10:00:24) ( NTS )"

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

class Test {

	private $data;
	private $values;

	public function __construct()
	{
		$this->data = new stdClass;
		$this->data->context = $this;

		$this->values = new stdClass;
		$this->values->store = [];

		for ($i = 0; $i < 526; $i++) {
			$obj = new stdClass;
			$obj->data = new stdClass;
			$obj->z = new stdClass;
			$obj->z->a = new stdClass;
			$obj->z->b = new stdClass;

			$this->values->store[] = $obj;
		}
	}
}

$data = [array_fill(0, 400, []), array_fill(0, 400, [])];

foreach ($data as $row_id => $values) {
	foreach ($values as $id => &$params) {
		$params["store"] = new Test;
	}
	unset($params);
}

echo "Completed\n";

Expected result:
----------------
Completed

Actual result:
--------------
Segmentation fault

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-05-14 12:43 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2019-05-14 12:43 UTC] nikic@php.net
First valgrind warning:

==21347== Invalid read of size 4
==21347==    at 0x94623D: zend_gc_collect_cycles (zend_gc.c:1529)
==21347==    by 0x9436EA: gc_possible_root_when_full (zend_gc.c:579)
==21347==    by 0x943996: gc_possible_root (zend_gc.c:629)
==21347==    by 0x970CF4: zend_assign_to_variable (zend_execute.h:146)
==21347==    by 0x9E99E5: ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:48960)
==21347==    by 0x9FBA14: execute_ex (zend_vm_execute.h:65005)
==21347==    by 0x9FC649: zend_execute (zend_vm_execute.h:65726)
==21347==    by 0x908A98: zend_execute_scripts (zend.c:1661)
==21347==    by 0x8518DA: php_execute_script (main.c:2676)
==21347==    by 0x9FF646: do_cli (php_cli.c:985)
==21347==    by 0xA00965: main (php_cli.c:1375)
==21347==  Address 0x2661b414 is 4 bytes inside a block of size 40 free'd
==21347==    at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21347==    by 0x8C7FC4: _efree (zend_alloc.c:2497)
==21347==    by 0x96619B: zend_objects_store_del (zend_objects_API.c:198)
==21347==    by 0x902E4E: rc_dtor_func (zend_variables.c:57)
==21347==    by 0x91C30D: i_zval_ptr_dtor (zend_variables.h:44)
==21347==    by 0x921881: zend_array_destroy (zend_hash.c:1589)
==21347==    by 0x95DBA1: zend_object_std_dtor (zend_objects.c:53)
==21347==    by 0x966137: zend_objects_store_del (zend_objects_API.c:194)
==21347==    by 0x902E4E: rc_dtor_func (zend_variables.c:57)
==21347==    by 0x91C30D: i_zval_ptr_dtor (zend_variables.h:44)
==21347==    by 0x921881: zend_array_destroy (zend_hash.c:1589)
==21347==    by 0x902E4E: rc_dtor_func (zend_variables.c:57)
==21347==  Block was alloc'd at
==21347==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21347==    by 0x8C8F07: __zend_malloc (zend_alloc.c:2889)
==21347==    by 0x8C7E9C: _emalloc (zend_alloc.c:2483)
==21347==    by 0x95E27A: zend_objects_new (zend_objects.c:195)
==21347==    by 0x90FFCE: _object_and_properties_init (zend_API.c:1356)
==21347==    by 0x9100AE: object_init_ex (zend_API.c:1379)
==21347==    by 0x992655: ZEND_NEW_SPEC_CONST_UNUSED_HANDLER (zend_vm_execute.h:9173)
==21347==    by 0x9F7273: execute_ex (zend_vm_execute.h:60503)
==21347==    by 0x9FC649: zend_execute (zend_vm_execute.h:65726)
==21347==    by 0x908A98: zend_execute_scripts (zend.c:1661)
==21347==    by 0x8518DA: php_execute_script (main.c:2676)
==21347==    by 0x9FF646: do_cli (php_cli.c:985)
 [2019-05-15 11:04 UTC] nikic@php.net
-Summary: Segmentation fault +Summary: Segmentation fault during GC
 [2019-07-12 16:11 UTC] kolja dot zuelsdorf at deinhandy dot de
We have this issue under strong suspicion to break one of our core business processes and wonder why it was not addressed in the last bugfix releases. Is there anything we can do to help mitigate the issue? So far we are running said process on an old (7.1) version.

Furthermore, I've confirmed that the same thing happens on 7.3.6 and 7.3.7 too.
 [2019-07-12 20:50 UTC] grzegorz129 at gmail dot com
Maybe stating the obvious but putting `gc_disable()` at the beginning of the script makes the code complete. Also tested on 7.4alpha3 - the same result.
 [2019-07-13 00:00 UTC] grzegorz129 at gmail dot com
I simplified the reproducer and added some comments:

<?php
//gc_disable();

class foo
{
    public function __construct()
    {
        $this->x = $this;

        for ($i = 0; $i < 898; $i++) { //Will not trigger with <898
            $obj = [new stdClass, new stdClass]; //This must have at least 2 elements
            $this->y[] = $obj;
        }
    }
}

for ($i = 0; $i < 2; ++$i) { //This must run >=2 (increasing the number of elements in the array *2 will not do)
    $x = []; //This must be reset
    foreach (array_fill(0, 389, 'x') as &$params) { //Will not trigger <389
        $x[] = new foo;
    }
}

echo "Completed\n";


Valgring output: https://zerobin.net/?ca2ef1bcab5e1b03#qc85nUSpB0Yp7oK4Bvd1hSOV3B9kybe34XmH6wvZE6o=
 [2019-07-13 04:28 UTC] grzegorz129 at gmail dot com
After some more testing this bug seems to be introduced in 7.3.0 (or during it's stabilization period). The 7.2.20 runs my short example without a problem while 7.3.0 crashes with a SIGSEGV.

While I'm unable to run the whole suite on 3v4l (since it uses substantial amount of resources after all) I also verified all three master branches (normal/jit/opcache) and all of them are crashing in the same way.
 [2019-07-15 09:26 UTC] nikic@php.net
-Status: Verified +Status: Analyzed
 [2019-07-15 09:26 UTC] nikic@php.net
What I believe is happening here is the following: An object gets assigned GC address 2^20 (or a multiple thereof), which is compressed to 0. The object is found to be part of a cycle and will at the end of root collection be marked as black (color 0). Together these two mean that the object has 0 gc_info. During the GC destruction phase, some other object is destroyed first and recursively destroyed our gc_info=0 object. When that happens we attempt to remove it from the root buffer, but because gc_info=0 it looks like the object is not part of the root buffer and this is skipped, eventually resulting in a double free.
 [2019-07-15 10:08 UTC] nikic@php.net
-Status: Analyzed +Status: Assigned -Assigned To: +Assigned To: nikic
 [2019-07-15 10:08 UTC] nikic@php.net
Candidate patch at https://github.com/php/php-src/pull/4414.
 [2019-07-15 11:50 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=193f28c7d557df887c4456d072113cc2478e1c3e
Log: Fixed bug #78010
 [2019-07-15 11:50 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Nov 22 03:01:27 2024 UTC