From d47798b6cd84502c7106e079195950ceaf837ce3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 8 Apr 2023 15:23:58 +0200 Subject: [PATCH] Fix GH-11028 (Heap Buffer Overflow in zval_undefined_cv.) For analysis see https://github.com/php/php-src/issues/11028#issuecomment-1508460440 --- Zend/tests/generators/gh11028_1.phpt | 35 ++++++++++++++++++++++++++++ Zend/tests/generators/gh11028_2.phpt | 24 +++++++++++++++++++ Zend/tests/generators/gh11028_3.phpt | 21 +++++++++++++++++ Zend/zend_generators.c | 13 ++++++++++- 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/generators/gh11028_1.phpt create mode 100644 Zend/tests/generators/gh11028_2.phpt create mode 100644 Zend/tests/generators/gh11028_3.phpt diff --git a/Zend/tests/generators/gh11028_1.phpt b/Zend/tests/generators/gh11028_1.phpt new file mode 100644 index 0000000000000..e1e7aa5019e54 --- /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 0000000000000..27b36c711f5c1 --- /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 0000000000000..7ea1aac6f6cfc --- /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 5d7bef3854f5e..a3610fa8f7b13 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;