php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75351 Segmentation fault on fatal error during Generator::send
Submitted: 2017-10-10 15:15 UTC Modified: 2018-06-24 04:25 UTC
Votes:2
Avg. Score:4.0 ± 1.0
Reproduced:2 of 2 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (50.0%)
From: bashofmann at gmail dot com Assigned:
Status: No Feedback Package: Scripting Engine problem
PHP Version: 7.1.10 OS: Ubuntu 14.04.5 LTS (GNU/Linux 4.
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2017-10-10 15:15 UTC] bashofmann at gmail dot com
Description:
------------
We are experiencing segmentation faults related to probably fatal errors when using Generator::send. Unfortunately I was not able to create a simple script to reproduce this issue.

gdb back trace:

(gdb) bt
#0  calc_gc_buffer_size (generator=0x7f64d68f4a80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.c:279
#1  zend_generator_get_gc (object=0x7ffe4c963eb0, table=0x7ffe4c963ea8, n=0x7ffe4c963ea4) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.c:305
#2  0x00005576280aa23a in gc_mark_grey (ref=0x7f64d68f4a80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:499
#3  0x00005576280aa2b6 in gc_mark_grey (ref=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:511
#4  0x00005576280aa2b6 in gc_mark_grey (ref=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:511
#5  0x00005576280aa346 in gc_mark_grey (ref=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:572
#6  0x00005576280aa346 in gc_mark_grey (ref=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:572
#7  0x00005576280ab1e8 in gc_mark_roots () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:598
#8  zend_gc_collect_cycles () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:1072
#9  0x00005576280aae3e in gc_possible_root (ref=0x7ffe4c963eb0, ref@entry=0x7f64d68f5f80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_gc.c:286
#10 0x00005576280af551 in zend_object_release (obj=0x7f64d68f5f80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_objects_API.h:80
#11 zend_generator_update_current (generator=generator@entry=0x7f64d68f4a80, leaf=0x7f64d68f4a80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.c:667
#12 0x00005576280aefdd in zend_generator_get_current (generator=0x7f64d68f4a80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.h:133
#13 zend_generator_resume (orig_generator=orig_generator@entry=0x7f64d68f4a80) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.c:845
#14 0x00005576280afe7f in zim_Generator_send (execute_data=0x7f6555a14070, return_value=0x7ffe4c964210) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.c:1001
#15 0x000055762811a0cc in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:972
#16 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#17 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#18 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#19 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#20 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#21 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#22 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#23 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#24 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#25 0x0000557628119ae5 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:1076
#26 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#27 0x0000557628119ae5 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:1076
#28 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#29 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#30 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#31 0x00005576280aeede in zend_generator_resume (orig_generator=0x7f64de060780) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_generators.c:815
#32 0x000055762811a0cc in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:972
#33 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#34 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#35 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#36 0x0000557628119ae5 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:1076
#37 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#38 0x00005576280747e1 in zend_call_function (fci=fci@entry=0x7ffe4c964810, fci_cache=fci_cache@entry=0x7ffe4c9647e0) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_execute_API.c:855
#39 0x0000557627f762ad in reflection_method_invoke (execute_data=<optimized out>, return_value=0x7f6555a13610, variadic=0) at /build/php7.1-zUmk8m/php7.1-7.1.9/ext/reflection/php_reflection.c:3342
#40 0x0000557628119c50 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:1099
#41 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#42 0x0000557628119ae5 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:1076
#43 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#44 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#45 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#46 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#47 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#48 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#49 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#50 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#51 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#52 0x0000557628119f65 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:949
#53 0x00005576280c8edb in execute_ex (ex=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:429
#54 0x000055762811bf74 in zend_execute (op_array=0x7f6555a67000, op_array@entry=0x7f64f0edbe88, return_value=return_value@entry=0x7f6555a13550) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend_vm_execute.h:474
#55 0x0000557628083e44 in zend_execute_scripts (type=type@entry=8, retval=0x7f6555a13550, retval@entry=0x0, file_count=file_count@entry=3) at /build/php7.1-zUmk8m/php7.1-7.1.9/Zend/zend.c:1480
#56 0x00005576280234a8 in php_execute_script (primary_file=0x7ffe4c966f90) at /build/php7.1-zUmk8m/php7.1-7.1.9/main/main.c:2552
#57 0x0000557627f0c4ac in main (argc=<optimized out>, argv=<optimized out>) at /build/php7.1-zUmk8m/php7.1-7.1.9/sapi/fpm/fpm/fpm_main.c:1966

Excerpt of zbacktrace

(gdb) zbacktrace
[0x7f6555a14070] Generator->send(object[0x7f6555a140c0]) [internal function]
[0x7f6555a13fd0] rg\core\pow\preparer\handler\GeneratorHandler->returnResponse(object[0x7f6555a14020], object[0x7f6555a14030], reference) /home/researchgate/community/tor_prd/blue/src/core/pow/preparer/handler/GeneratorHandler.php:60
[0x7f6555a13f40] rg\core\pow\preparer\handler\PreparableHandler->returnResponse(object[0x7f6555a13f90], object[0x7f6555a13fa0], array(1)[0x7f6555a13fb0]) /home/researchgate/community/tor_prd/blue/src/core/pow/preparer/handler/PreparableHandler.php:82
...

the code of returnResponse in GeneratorHandler looks rather innocent:

    public function returnResponse($preparable, \Traversable $iterator, array $data) {
        if (!is_array($iterator->rawCurrent()) && count($data) === 1) {
            $data = reset($data);
        }

        $preparable->send($data);
    }

The reason why I'm suspecting it has something to do with a fatal error is that it seems to go through this line https://github.com/php/php-src/blob/PHP-7.1.9/Zend/zend_generators.c#L133 in the bt


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-10-11 09:43 UTC] cmb@php.net
-Package: *Programming Data Structures +Package: Scripting Engine problem
 [2017-12-11 22:18 UTC] tandre@php.net
I'm encountering similar issues, also in Generator->send(). However, the issues I see are in zend_generator_get_gc instead

It seems like garbage collection is getting triggered when Generator->send() is getting called, which may be an edge case that isn't handled properly, and may be related to why it's hard to reproduce.

I'm using `$retval = yield from`, `$retval = yield some_helper()`, `yield other_helper()` in the code in question, but the bug happens so infrequently I'm not sure which part it is. I haven't checked how deeply nested yields are.

I'm using PHP 7.1.9. The source of zend_generators.c is identical for 7.1.9 and 7.1.12 (latest).

Stack trace:

```
... Lines added by newrelic segfault stack trace dumper omitted
/usr/local/php/modules/libphp7.so(+0x431748)[0x7ff795959748]
/usr/local/php/modules/libphp7.so(+0x42d4fa)[0x7ff7959554fa]
/usr/local/php/modules/libphp7.so(zend_gc_collect_cycles+0x78)[0x7ff795956478]
/usr/local/php/modules/libphp7.so(gc_possible_root+0x9e)[0x7ff7959560ee]
/usr/local/php/modules/libphp7.so(+0x43220c)[0x7ff79595a20c]
/usr/local/php/modules/libphp7.so(zend_objects_store_del+0x271)[0x7ff79596c231]
/usr/local/php/modules/libphp7.so(zend_generator_update_current+0x1f0)[0x7ff79595a9b0]
/usr/local/php/modules/libphp7.so(zend_generator_resume+0x1dd)[0x7ff79595a4ad]
/usr/local/php/modules/libphp7.so(+0x433bdf)[0x7ff79595bbdf]
/usr/local/php/modules/libphp7.so(+0x4b70c6)[0x7ff7959df0c6]
/usr/local/php/modules/libphp7.so(execute_ex+0x2b)[0x7ff79597738b]
/usr/local/php/lib/php/20160303/newrelic.so(+0x231bb)[0x7ff78b0b61bb]
/usr/local/php/lib/php/20160303/newrelic.so(+0x23802)[0x7ff78b0b6802]
/usr/local/php/modules/libphp7.so(+0x4b6985)[0x7ff7959de985]
/usr/local/php/modules/libphp7.so(execute_ex+0x2b)[0x7ff79597738b]
/usr/local/php/lib/php/20160303/newrelic.so(+0x231bb)[0x7ff78b0b61bb]
/usr/local/php/lib/php/20160303/newrelic.so(+0x23802)[0x7ff78b0b6802]
```

I added comments to parts of the code that showed up in my stack trace, and 

```
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -275,7 +275,7 @@ static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
                /* Yield from root references */
                if (generator->node.children == 0) {
                        zend_generator *child = generator, *root = generator->node.ptr.root;
-                       while (root != child) {
+                       while (root != child && child != NULL) {  // Not sure if checking for null would solve the underlying issue for the other bug reported, and I'm not familiar with how deep the chain goes.
                                child = child->node.parent;
                                size++;
                        }
@@ -340,8 +340,8 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {
 
        if (generator->node.children == 0) {
                zend_generator *child = generator, *root = generator->node.ptr.root;
-               while (root != child) {
-                       child = child->node.parent;
+               while (root != child && child != NULL) {  // Not sure if checking for null would solve the underlying issue
+                       child = child->node.parent;  // child->node is somehow equal to null when the segfault occurs
                        ZVAL_OBJ(gc_buffer++, &child->std);
                }
        }
@@ -608,7 +608,7 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator
        }
 
        while (!root->execute_data && root != generator) {
-               OBJ_RELEASE(&old_root->std);
+               OBJ_RELEASE(&old_root->std);  // Not caused by this OBJ_RELEASE. It's caused by the below one.
                old_root = root;
 
                root = zend_generator_get_child(&root->node, leaf);
@@ -638,10 +638,11 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator
                                                EG(current_execute_data) = original_execute_data;
 
                                                if (!((old_root ? old_root : generator)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
-                                                       leaf->node.ptr.root = root;
-                                                       root->node.parent = NULL;
+                                                       leaf->node.ptr.root = root;  // The root pointer and root->node pointer were changed.
+                                                       root->node.parent = NULL;  // And a parent gets set to null, which might interfere with zend_generator_get_gc
+                            // What happens if there are nested yield froms, though? Would those still have (something)->node.ptr.root point to old_root?
                                                        if (old_root) {
-                                                               OBJ_RELEASE(&old_root->std);
+                                                               OBJ_RELEASE(&old_root->std);  // Disassembly shows that this triggers a garbage collection
                                                        }
                                                        zend_generator_resume(leaf);
                                                        return leaf->node.ptr.root; /* this may be updated during zend_generator_resume! */
```
 [2017-12-11 23:31 UTC] tandre@php.net
A workaround I'm considering is to call Generator->next(), since my code was already always calling send with NULL.

The implementation Generator->send() calls zend_generator_get_current(), but Generator->next() does not.
 [2018-01-09 14:36 UTC] daniel dot tschinder at researchgate dot net
Did anyone of you find a workaround? We have the same problem, but I cannot really figure out how to workaround and it does not happen every time.

I once managed to debug when this happened, but all I saw was that the process died on Generator::send.
 [2018-01-12 21:59 UTC] nikic@php.net
-Status: Open +Status: Feedback
 [2018-01-12 21:59 UTC] nikic@php.net
I've applied https://github.com/php/php-src/commit/cab0a814bdbfc653754f74b42056c38bdf4fbadb, which fixes some crashes of the type described here I was able to produce under artificial GC configurations.

Please check whether current 7.1/7.2/master snapshots resolve the issue for you.

I think there is still a lingering problem here, because root and parent references are not necessarily consistent, in that root references might point deeper into the tree than the parent chain under some circumstances. However, I'm not sure if checking for null is the right answer for that (as I think this might miss the generators that are between the root reference and the end of the parent chain) or whether we should be using a reverse loop from root to leaf as we do in most other places.
 [2018-01-13 10:12 UTC] nikic@php.net
I've applied another fix for the last issue I mentioned: https://github.com/php/php-src/commit/8c07170ddbe28d8cc31e71f0ceb94fc4ac329657
 [2018-06-24 04:25 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Apr 23 12:01:31 2024 UTC