From b811f1282b8981bef13db65ae0ff841220fad929 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 20 Dec 2022 14:52:43 +0100 Subject: [PATCH 1/3] Prevent dtor of generator in suspended fiber Generators that suspended a fiber should not be dtor because they will be executed during the fiber dtor. Fiber dtor throws an exception in the fiber's context in order to unwind and execute finally blocks, which will also properly dtor the generator. --- Zend/tests/gh9916-001.phpt | 27 ++++++++++++++++++ Zend/tests/gh9916-002.phpt | 21 ++++++++++++++ Zend/tests/gh9916-003.phpt | 36 ++++++++++++++++++++++++ Zend/tests/gh9916-004.phpt | 26 +++++++++++++++++ Zend/tests/gh9916-005.phpt | 25 +++++++++++++++++ Zend/tests/gh9916-006.phpt | 37 +++++++++++++++++++++++++ Zend/tests/gh9916-007.phpt | 57 ++++++++++++++++++++++++++++++++++++++ Zend/tests/gh9916-008.phpt | 47 +++++++++++++++++++++++++++++++ Zend/tests/gh9916-009.phpt | 35 +++++++++++++++++++++++ Zend/zend_generators.c | 13 +++++++-- Zend/zend_generators.h | 1 + 11 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh9916-001.phpt create mode 100644 Zend/tests/gh9916-002.phpt create mode 100644 Zend/tests/gh9916-003.phpt create mode 100644 Zend/tests/gh9916-004.phpt create mode 100644 Zend/tests/gh9916-005.phpt create mode 100644 Zend/tests/gh9916-006.phpt create mode 100644 Zend/tests/gh9916-007.phpt create mode 100644 Zend/tests/gh9916-008.phpt create mode 100644 Zend/tests/gh9916-009.phpt diff --git a/Zend/tests/gh9916-001.phpt b/Zend/tests/gh9916-001.phpt new file mode 100644 index 0000000000000..3e518807238bc --- /dev/null +++ b/Zend/tests/gh9916-001.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug GH-9916 001 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Not executed"; +}); +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== +Finally diff --git a/Zend/tests/gh9916-002.phpt b/Zend/tests/gh9916-002.phpt new file mode 100644 index 0000000000000..6f0b81cf3e91a --- /dev/null +++ b/Zend/tests/gh9916-002.phpt @@ -0,0 +1,21 @@ +--TEST-- +Bug GH-9916 002 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Not executed"; +}); +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== diff --git a/Zend/tests/gh9916-003.phpt b/Zend/tests/gh9916-003.phpt new file mode 100644 index 0000000000000..c4bfb815118bc --- /dev/null +++ b/Zend/tests/gh9916-003.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug GH-9916 003 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Not executed"; +}); +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== +Finally (inner) +Finally diff --git a/Zend/tests/gh9916-004.phpt b/Zend/tests/gh9916-004.phpt new file mode 100644 index 0000000000000..1a51a841e8bb6 --- /dev/null +++ b/Zend/tests/gh9916-004.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug GH-9916 004 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Not executed"; +}); +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== diff --git a/Zend/tests/gh9916-005.phpt b/Zend/tests/gh9916-005.phpt new file mode 100644 index 0000000000000..f84c756919cd0 --- /dev/null +++ b/Zend/tests/gh9916-005.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug GH-9916 005 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +send($fiber); + $gen->current(); +}); +$fiber->start(); + +$gen = null; +$fiber = null; +gc_collect_cycles(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== diff --git a/Zend/tests/gh9916-006.phpt b/Zend/tests/gh9916-006.phpt new file mode 100644 index 0000000000000..28c57105c7b1f --- /dev/null +++ b/Zend/tests/gh9916-006.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug GH-9916 006 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Fiber return\n"; +}); +$fiber->start(); +$fiber->resume(); +$gen->next(); +$gen->current(); +?> +==DONE== +--EXPECT-- +Before suspend +After suspend +Fiber return +Before exit diff --git a/Zend/tests/gh9916-007.phpt b/Zend/tests/gh9916-007.phpt new file mode 100644 index 0000000000000..e1ec3fb19f32b --- /dev/null +++ b/Zend/tests/gh9916-007.phpt @@ -0,0 +1,57 @@ +--TEST-- +Bug GH-9916 007 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Not executed"; +}); +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== +Finally (iterator) +Finally diff --git a/Zend/tests/gh9916-008.phpt b/Zend/tests/gh9916-008.phpt new file mode 100644 index 0000000000000..84521d69e2120 --- /dev/null +++ b/Zend/tests/gh9916-008.phpt @@ -0,0 +1,47 @@ +--TEST-- +Bug GH-9916 008 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Not executed"; +}); +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before suspend +==DONE== diff --git a/Zend/tests/gh9916-009.phpt b/Zend/tests/gh9916-009.phpt new file mode 100644 index 0000000000000..75643d871dea0 --- /dev/null +++ b/Zend/tests/gh9916-009.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug GH-9916 009 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- + new stdClass]; + print "Not executed\n"; + } +})(); +$fiber = new Fiber(function() use ($gen, &$fiber) { + $gen->current(); + print "Not executed\n"; +}); +$fiber->start(); +?> +==DONE== +--EXPECTF-- +Before suspend +==DONE== +Finally + +Fatal error: Uncaught Error: Cannot use "yield from" in a force-closed generator in %s:%d +Stack trace: +#0 [internal function]: {closure}() +#1 %s(%d): Generator->current() +#2 [internal function]: {closure}() +#3 {main} + thrown in %s on line %d diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index d5253e46eddf2..bc6a34ed2a6af 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -223,6 +223,14 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ uint32_t op_num, try_catch_offset; int i; + /* Generator is running in a suspended fiber. + * Will be dtor during fiber dtor */ + if (generator->flags & ZEND_GENERATOR_IN_FIBER) { + /* Prevent finally blocks from yielding */ + generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; + return; + } + /* leave yield from mode to properly allow finally execution */ if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) { zval_ptr_dtor(&generator->values); @@ -727,7 +735,8 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ } /* Resume execution */ - generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING; + generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING + | (EG(active_fiber) ? ZEND_GENERATOR_IN_FIBER : 0); if (!ZEND_OBSERVER_ENABLED) { zend_execute_ex(generator->execute_data); } else { @@ -778,7 +787,7 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ goto try_again; } - orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT; + orig_generator->flags &= ~(ZEND_GENERATOR_DO_INIT | ZEND_GENERATOR_IN_FIBER); } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 17b25a99b87c1..4ccc42b92f668 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -92,6 +92,7 @@ static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1; static const zend_uchar ZEND_GENERATOR_FORCED_CLOSE = 0x2; static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4; static const zend_uchar ZEND_GENERATOR_DO_INIT = 0x8; +static const zend_uchar ZEND_GENERATOR_IN_FIBER = 0x10; void zend_register_generator_ce(void); ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution); From 2ff362d190e310acf506039cb9070ff9ed703a1a Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 27 Jan 2023 18:02:25 +0100 Subject: [PATCH 2/3] Add tests --- Zend/tests/gh9916-010.phpt | 35 ++++++++++++++++++++++++++++++++ Zend/tests/gh9916-011.phpt | 41 ++++++++++++++++++++++++++++++++++++++ Zend/zend_generators.c | 3 ++- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/gh9916-010.phpt create mode 100644 Zend/tests/gh9916-011.phpt diff --git a/Zend/tests/gh9916-010.phpt b/Zend/tests/gh9916-010.phpt new file mode 100644 index 0000000000000..f7b3ae84185b7 --- /dev/null +++ b/Zend/tests/gh9916-010.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug GH-9916 010 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +current(); + print "Before next\n"; + $gen->next(); + print "Not executed\n"; +}); + +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before current +Before yield +Before yield 2 +Before next +Before suspend +==DONE== +. diff --git a/Zend/tests/gh9916-011.phpt b/Zend/tests/gh9916-011.phpt new file mode 100644 index 0000000000000..05fa884c29337 --- /dev/null +++ b/Zend/tests/gh9916-011.phpt @@ -0,0 +1,41 @@ +--TEST-- +Bug GH-9916 011 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes) +--FILE-- +next(); + } +})(); + +$fiber = new Fiber(function () use ($gen, &$fiber) { + print "Before current\n"; + $gen->current(); + print "Before next\n"; + $gen->next(); + print "Not executed\n"; +}); + +$fiber->start(); +?> +==DONE== +--EXPECT-- +Before current +Before yield +Before yield 2 +Before next +Before suspend +==DONE== diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index bc6a34ed2a6af..24316dc4e896b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -787,7 +787,8 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ goto try_again; } - orig_generator->flags &= ~(ZEND_GENERATOR_DO_INIT | ZEND_GENERATOR_IN_FIBER); + generator->flags &= ~ZEND_GENERATOR_IN_FIBER; + orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT; } /* }}} */ From c191ccf9bcc8b4aea8314ced5828b93cdc4c7c8b Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 27 Jan 2023 18:29:35 +0100 Subject: [PATCH 3/3] Fix test --- Zend/tests/gh9916-010.phpt | 1 - 1 file changed, 1 deletion(-) diff --git a/Zend/tests/gh9916-010.phpt b/Zend/tests/gh9916-010.phpt index f7b3ae84185b7..d3a841d7ceb31 100644 --- a/Zend/tests/gh9916-010.phpt +++ b/Zend/tests/gh9916-010.phpt @@ -32,4 +32,3 @@ Before yield 2 Before next Before suspend ==DONE== -.