Fix GH-11028: Heap Buffer Overflow in zval_undefined_cv.

For analysis see https://github.com/php/php-src/issues/11028#issuecomment-1508460440

Closes GH-11083.
This commit is contained in:
Niels Dossche 2023-04-08 15:23:58 +02:00
parent 2eeff96df5
commit fc32d39b7f
5 changed files with 93 additions and 1 deletions

1
NEWS
View file

@ -10,6 +10,7 @@ PHP NEWS
array is contained in the second). (ilutov)
. Fixed bug GH-10737 (PHP 8.1.16 segfaults on line 597 of
sapi/apache2handler/sapi_apache2.c). (nielsdos, ElliotNB)
. Fixed bug GH-11028 (Heap Buffer Overflow in zval_undefined_cv.). (nielsdos)
- DOM:
. Fixed bug #80602 (Segfault when using DOMChildNode::before()).

View file

@ -0,0 +1,35 @@
--TEST--
GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - other types variant
--FILE--
<?php
function generator($x) {
try {
yield $x => 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

View file

@ -0,0 +1,24 @@
--TEST--
GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - original variant
--FILE--
<?php
$c = (function () {
[
...($r = (function () {
try {
yield $a => 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

View file

@ -0,0 +1,21 @@
--TEST--
GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - throw in finally variant
--FILE--
<?php
function generator() {
try {
yield null => 0;
} finally {
throw new Exception("exception");
return [];
}
}
try {
var_dump([...generator()]);
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
exception

View file

@ -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;