php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71436 Segfault in AppendIterator with empty generator
Submitted: 2016-01-23 22:43 UTC Modified: 2021-04-23 16:30 UTC
Votes:12
Avg. Score:4.1 ± 1.1
Reproduced:10 of 11 (90.9%)
Same Version:4 (40.0%)
Same OS:2 (20.0%)
From: matej21 at matej21 dot cz Assigned: nikic (profile)
Status: Duplicate Package: SPL related
PHP Version: 7.0.2 OS:
Private report: No CVE-ID: None
 [2016-01-23 22:43 UTC] matej21 at matej21 dot cz
Description:
------------
AppendIterator produces a segfault when it iterates over an empty generator.

https://3v4l.org/q3I67

Test script:
---------------
<?php
function foo() {
        foreach ([] as $foo) {
                yield $foo;
        }
}
$iterator = new AppendIterator();
$iterator->append(foo());
var_dump(iterator_to_array($iterator));


Expected result:
----------------
array(0) {
}

Actual result:
--------------
Program received signal SIGSEGV, Segmentation fault.


#0  spl_dual_it_rewind (intern=0x7fffed2783c0) at /usr/local/src/php/ext/spl/spl_iterators.c:1682
#1  spl_append_it_next_iterator (intern=intern@entry=0x7fffed2783c0) at /usr/local/src/php/ext/spl/spl_iterators.c:3332
#2  0x000000000071fff4 in zim_spl_AppendIterator_rewind (execute_data=<optimized out>, return_value=<optimized out>) at /usr/local/src/php/ext/spl/spl_iterators.c:3403
#3  0x00000000008157df in zend_call_function (fci=fci@entry=0x7fffffffa410, fci_cache=fci_cache@entry=0x7fffffffa3e0) at /usr/local/src/php/Zend/zend_execute_API.c:879
#4  0x000000000083ec54 in zend_call_method (object=object@entry=0x7fffed2691f8, obj_ce=<optimized out>, fn_proxy=0x1287000, function_name=function_name@entry=0x989d4c "rewind", function_name_len=function_name_len@entry=6, 
    retval_ptr=retval_ptr@entry=0x0, param_count=param_count@entry=0, arg1=arg1@entry=0x0, arg2=arg2@entry=0x0) at /usr/local/src/php/Zend/zend_interfaces.c:104
#5  0x000000000083f25a in zend_user_it_rewind (_iter=0x7fffed2691c0) at /usr/local/src/php/Zend/zend_interfaces.c:245
#6  0x0000000000720212 in spl_iterator_apply (obj=<optimized out>, apply_func=0x719ae0 <spl_iterator_to_array_apply>, puser=puser@entry=0x7fffed2130f0) at /usr/local/src/php/ext/spl/spl_iterators.c:3503
#7  0x00000000007202e5 in zif_iterator_to_array (execute_data=<optimized out>, return_value=0x7fffed2130f0) at /usr/local/src/php/ext/spl/spl_iterators.c:3590
#8  0x000000000089c12b in ZEND_DO_FCALL_SPEC_HANDLER () at /usr/local/src/php/Zend/zend_vm_execute.h:842
#9  0x000000000085ed3b in execute_ex (ex=<optimized out>) at /usr/local/src/php/Zend/zend_vm_execute.h:414
#10 0x00000000008a7c27 in zend_execute (op_array=0x7fffed283000, op_array@entry=0x7fffed288180, return_value=return_value@entry=0x7fffed213030) at /usr/local/src/php/Zend/zend_vm_execute.h:458
#11 0x0000000000823494 in zend_execute_scripts (type=type@entry=8, retval=0x7fffed213030, retval@entry=0x0, file_count=file_count@entry=3) at /usr/local/src/php/Zend/zend.c:1427
#12 0x00000000007c7698 in php_execute_script (primary_file=primary_file@entry=0x7fffffffc9c0) at /usr/local/src/php/main/main.c:2484
#13 0x00000000008a97f3 in do_cli (argc=2, argv=0x119cc10) at /usr/local/src/php/sapi/cli/php_cli.c:974
#14 0x000000000044d9e0 in main (argc=2, argv=0x119cc10) at /usr/local/src/php/sapi/cli/php_cli.c:1345


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-01-24 06:47 UTC] laruence@php.net
-Assigned To: +Assigned To: nikic
 [2016-01-24 06:47 UTC] laruence@php.net
with a quick fix (check null pointer) : 
diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c
index c6181d9..bb86e76 100644
--- a/ext/spl/spl_iterators.c
+++ b/ext/spl/spl_iterators.c
@@ -1682,7 +1682,7 @@ static inline void spl_dual_it_rewind(spl_dual_it_object *intern TSRMLS_DC)
 {
 	spl_dual_it_free(intern TSRMLS_CC);
 	intern->current.pos = 0;
-	if (intern->inner.iterator->funcs->rewind) {
+	if (intern->inner.iterator && intern->inner.iterator->funcs->rewind) {
 		intern->inner.iterator->funcs->rewind(intern->inner.iterator TSRMLS_CC);
 	}
 }

will get:
Fatal error: Uncaught exception 'Exception' with message 'Cannot traverse an already closed generator' in /tmp/1.php:9
Stack trace:
#0 [internal function]: AppendIterator->rewind()
#1 /tmp/1.php(9): iterator_to_array(Object(AppendIterator))
#2 {main}
  thrown in /tmp/1.php on line 9


which seems not quite reasonable error message. 

@nikic, what do you think?
 [2016-01-24 10:07 UTC] nikic@php.net
@laruence: There are two bugs here:
a) It does not handle exceptions thrown from get_iterator(). This is what you fixed.
b) It calls get_iterator() multiple times for some reason, which is why there is an exception in the first place.
 [2016-03-09 21:49 UTC] crahobzy at ukr dot net
I experience the same problem with PHP 5.6.19. @nikic are there any chances to fix it?
 [2016-03-24 16:54 UTC] savageman86 at gmail dot com
Same bug on 5.5.9
 [2016-07-28 23:32 UTC] brian dot carpenter at gmail dot com
This crash is present in 5.6.24.
 [2017-03-02 16:08 UTC] tom60 at op dot pl
Possibly related:

-------------------------------------------------------
#0  zend_mm_alloc_small (size=56, bin_num=6, heap=0x7f8c9de00040) at /src/php-7.1.2/Zend/zend_alloc.c:1261
#1  _emalloc_56 () at /src/php-7.1.2/Zend/zend_alloc.c:2336
#2  0x000000000080d7a0 in _array_init (arg=arg@entry=0x7ffffdaf06f0, size=size@entry=0) at /src/php-7.1.2/Zend/zend_API.c:1060
#3  0x0000000000824f31 in zend_fetch_debug_backtrace (return_value=return_value@entry=0x7ffffdaf0780, skip_last=skip_last@entry=0, options=options@entry=0, 
    limit=limit@entry=0) at /src/php-7.1.2/Zend/zend_builtin_functions.c:2612
#4  0x000000000082bb0f in zend_default_exception_new_ex (class_type=0x2015d80, skip_top_traces=0) at /src/php-7.1.2/Zend/zend_exceptions.c:216
#5  0x000000000080df3b in _object_and_properties_init (arg=arg@entry=0x7ffffdaf07f0, class_type=class_type@entry=0x2015d80, properties=properties@entry=0x0)
    at /src/php-7.1.2/Zend/zend_API.c:1302
#6  0x000000000080e047 in _object_init_ex (arg=arg@entry=0x7ffffdaf07f0, class_type=class_type@entry=0x2015d80)
    at /src/php-7.1.2/Zend/zend_API.c:1310
#7  0x0000000000438429 in zend_throw_exception (exception_ce=0x2015d80, exception_ce@entry=0x0, message=message@entry=0xdf6b78 "Cannot traverse an already closed generator", 
    code=code@entry=0) at /src/php-7.1.2/Zend/zend_exceptions.c:922
#8  0x00000000008365ee in zend_generator_get_iterator (ce=<optimized out>, object=0x7f8c9de13980, by_ref=<optimized out>)
    at /src/php-7.1.2/Zend/zend_generators.c:1186
#9  0x00000000006e59c5 in spl_iterator_apply (obj=<optimized out>, apply_func=0x6df320 <spl_iterator_to_values_apply>, puser=puser@entry=0x7f8c9de13920)
    at /src/php-7.1.2/ext/spl/spl_iterators.c:3495
#10 0x00000000006e5af5 in zif_iterator_to_array (execute_data=<optimized out>, return_value=0x7f8c9de13920)
    at /src/php-7.1.2/ext/spl/spl_iterators.c:3590
#11 0x00000000008aa572 in ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER () at /src/php-7.1.2/Zend/zend_vm_execute.h:876
#12 0x000000000085215b in execute_ex (ex=<optimized out>) at /src/php-7.1.2/Zend/zend_vm_execute.h:429
#13 0x0000000000836c32 in zend_generator_resume (orig_generator=0x7f8c9de00040) at /src/php-7.1.2/Zend/zend_generators.c:817
#14 0x00000000008696b9 in ZEND_FE_FETCH_R_SPEC_VAR_HANDLER () at /src/php-7.1.2/Zend/zend_vm_execute.h:16816
#15 0x000000000085215b in execute_ex (ex=<optimized out>) at /src/php-7.1.2/Zend/zend_vm_execute.h:429
#16 0x0000000000836c32 in zend_generator_resume (orig_generator=0x7f8c9de00040) at /src/php-7.1.2/Zend/zend_generators.c:817
#17 0x00000000008696b9 in ZEND_FE_FETCH_R_SPEC_VAR_HANDLER () at /src/php-7.1.2/Zend/zend_vm_execute.h:16816
#18 0x000000000085215b in execute_ex (ex=<optimized out>) at /src/php-7.1.2/Zend/zend_vm_execute.h:429
#19 0x00000000008ad410 in zend_execute (op_array=0x7f8c9de7d000, op_array@entry=0x7f8c9d089d00, return_value=return_value@entry=0x7f8c9de13500)
    at /src/php-7.1.2/Zend/zend_vm_execute.h:474
#20 0x000000000080b834 in zend_execute_scripts (type=type@entry=8, retval=0x7f8c9de13500, retval@entry=0x0, file_count=file_count@entry=3)
    at /src/php-7.1.2/Zend/zend.c:1475
#21 0x00000000007abfb0 in php_execute_script (primary_file=0x7ffffdaf3060) at /src/php-7.1.2/main/main.c:2537
#22 0x00000000008af59a in do_cli (argc=-1646264256, argv=0x0) at /src/php-7.1.2/sapi/cli/php_cli.c:993
#23 0x000000000043a36c in main (argc=-1646264256, argv=0x0) at /src/php-7.1.2/sapi/cli/php_cli.c:1381
 [2017-04-15 20:38 UTC] matej21 at matej21 dot cz
first rewind is invoked by spl_append_it_next_iterator(), second one is invoked by spl_iterator_apply(). 

so maybe some flag telling that append iterator is already rewinded would solve it?
 [2018-04-10 08:08 UTC] sahli at gyselroth dot com
Same here, PHP 7.2.0, AppendIterator is pretty much unusable because of this behaviour.
 [2020-03-25 23:03 UTC] php at seanmorr dot is
You just need to use NoRewindIterator:

<?php
function foo() {
        foreach ([] as $foo) {
                yield $foo;
        }
}
$append = new AppendIterator();
$append->append(new NoRewindIterator(foo()));

var_dump(iterator_to_array($append));
 [2021-04-23 16:30 UTC] cmb@php.net
-Status: Assigned +Status: Duplicate
 [2021-04-23 16:30 UTC] cmb@php.net
The segfault has been already fixed as bug #72684; the other issue
mentioned by Nikita is tracked as bug #72692, so I'm closing this
ticket as duplicate.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 04:01:31 2024 UTC