diff --git a/Zend/tests/generators/gh11028_1.phpt b/Zend/tests/generators/gh11028_1.phpt new file mode 100644 index 000000000000..e1e7aa5019e5 --- /dev/null +++ b/Zend/tests/generators/gh11028_1.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - other types variant +--FILE-- + 0; + } finally { + return []; + } +} + +function test($msg, $x) { + echo "yield $msg\n"; + try { + var_dump([...generator($x)]); + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } +} + +test("null", null); +test("false", false); +test("true", true); +test("object", new stdClass); +?> +--EXPECT-- +yield null +Keys must be of type int|string during array unpacking +yield false +Keys must be of type int|string during array unpacking +yield true +Keys must be of type int|string during array unpacking +yield object +Keys must be of type int|string during array unpacking diff --git a/Zend/tests/generators/gh11028_2.phpt b/Zend/tests/generators/gh11028_2.phpt new file mode 100644 index 000000000000..27b36c711f5c --- /dev/null +++ b/Zend/tests/generators/gh11028_2.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - original variant +--FILE-- + 0; + } finally { + return []; + } + })()), + ]; +})()[0]; +?> +--EXPECTF-- +Warning: Undefined variable $a in %s on line %d + +Fatal error: Uncaught Error: Keys must be of type int|string during array unpacking in %s:%d +Stack trace: +#0 %s(%d): {closure}() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/generators/gh11028_3.phpt b/Zend/tests/generators/gh11028_3.phpt new file mode 100644 index 000000000000..7ea1aac6f6cf --- /dev/null +++ b/Zend/tests/generators/gh11028_3.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - throw in finally variant +--FILE-- + 0; + } finally { + throw new Exception("exception"); + return []; + } +} + +try { + var_dump([...generator()]); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +exception diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5d7bef3854f5..a3610fa8f7b1 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -279,14 +279,25 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[try_catch->finally_end].op1.var); zend_generator_cleanup_unfinished_execution(generator, ex, try_catch->finally_op); - Z_OBJ_P(fast_call) = EG(exception); + zend_object *old_exception = EG(exception); + const zend_op *old_opline_before_exception = EG(opline_before_exception); EG(exception) = NULL; + Z_OBJ_P(fast_call) = NULL; Z_OPLINE_NUM_P(fast_call) = (uint32_t)-1; ex->opline = &ex->func->op_array.opcodes[try_catch->finally_op]; generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; zend_generator_resume(generator); + if (old_exception) { + EG(opline_before_exception) = old_opline_before_exception; + if (EG(exception)) { + zend_exception_set_previous(EG(exception), old_exception); + } else { + EG(exception) = old_exception; + } + } + /* TODO: If we hit another yield inside try/finally, * should we also jump to the next finally block? */ break;