]> granicus.if.org Git - php/commitdiff
Fix bug #63830: Segfault on undefined function call in nested generator
authorNikita Popov <nikic@php.net>
Fri, 1 Feb 2013 18:48:05 +0000 (19:48 +0100)
committerNikita Popov <nikic@php.net>
Fri, 1 Feb 2013 18:53:04 +0000 (19:53 +0100)
This also reverses the destruction order of the pushed arguments to
align with how it is done everywhere else.

I'm not exactly sure whether this is the right way to fix it, but it
seems to work fine.

NEWS
Zend/tests/generators/nested_calls_with_die.phpt [new file with mode: 0644]
Zend/zend_generators.c

diff --git a/NEWS b/NEWS
index 14c77d022e3f5a6f4726fc0bb76d3e679b88e4ac..e79cffed237492bda01eb74add99dfb81bfa0a4b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@ PHP                                                                        NEWS
   . Fixed bug #60833 (self, parent, static behave inconsistently 
     case-sensitive). (Stas, mario at include-once dot org)
   . Implemented FR #60524 (specify temp dir by php.ini). (ALeX Kazik).
+  . Fixed bug #63830 (Segfault on undefined function call in nested generator).
+    (Nikita Popov)
 
 - CLI server:
   . Fixed bug #64128 (buit-in web server is broken on ppc64). (Remi)
diff --git a/Zend/tests/generators/nested_calls_with_die.phpt b/Zend/tests/generators/nested_calls_with_die.phpt
new file mode 100644 (file)
index 0000000..f43d89b
--- /dev/null
@@ -0,0 +1,30 @@
+--TEST--
+Test nested calls with die() in a generator
+--FILE--
+<?php
+
+function gen() {
+    die('Test');
+    yield; // force generator
+}
+
+function function_with_3_args() {
+    $gen = gen();
+    $gen->rewind();
+}
+
+function function_with_4_args() {
+    function_with_3_args(4, 5, 6);
+}
+
+function outerGen() {
+    function_with_4_args(0, 1, 2, 3);
+    yield; // force generator
+}
+
+$outerGen = outerGen();
+$outerGen->rewind();
+
+?>
+--EXPECT--
+Test
index 621320614ca61446edc3d84d66ffe8be18dee047..c1dbee124f3868bd394fa3c7623e1144691bb331 100644 (file)
@@ -94,10 +94,16 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
 
                /* Clear any backed up stack arguments */
                if (generator->stack != EG(argument_stack)) {
-                       void **stack_frame = zend_vm_stack_frame_base(execute_data);
-                       while (generator->stack->top != stack_frame) {
-                               zval_ptr_dtor((zval**)stack_frame);
-                               stack_frame++;
+                       void **ptr = generator->stack->top - 1;
+                       void **end = zend_vm_stack_frame_base(execute_data);
+
+                       /* If the top stack element is the argument count, skip it */
+                       if (execute_data->function_state.arguments) {
+                               ptr--;
+                       }
+
+                       for (; ptr >= end; --ptr) {
+                               zval_ptr_dtor((zval**) ptr);
                        }
                }