diff --git a/NEWS b/NEWS index a011c225aa69..f825f938da58 100644 --- a/NEWS +++ b/NEWS @@ -170,6 +170,10 @@ PHP NEWS . Fixed bug GH-23730 (use-after-free when XSLTProcessor::importStylesheet() is called during a transformation). (David Carlier) +- Zend: + . Fixed use-after-free when a dl()-loaded extension declares a frameless + function and a later request calls it. (Ilia Alshanetsky) + - Zip: . Fixed ZipArchive::extractTo() ignoring files given in a non-list array. (David Carlier) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 3c9891a00e92..2b3068dadefc 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2976,7 +2976,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend internal_function->prototype = NULL; internal_function->prop_info = NULL; internal_function->attributes = NULL; - internal_function->frameless_function_infos = ptr->frameless_function_infos; + if (type == MODULE_TEMPORARY) { + internal_function->frameless_function_infos = NULL; + } else { + internal_function->frameless_function_infos = ptr->frameless_function_infos; + } if (EG(active)) { // at run-time: this ought to only happen if registered with dl() or somehow temporarily at runtime ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_calloc(&CG(arena), 1, zend_internal_run_time_cache_reserved_size())); } else { diff --git a/ext/dl_test/dl_test.c b/ext/dl_test/dl_test.c index 7aebf5431338..3492a25a3a2f 100644 --- a/ext/dl_test/dl_test.c +++ b/ext/dl_test/dl_test.c @@ -52,6 +52,28 @@ PHP_FUNCTION(dl_test_test2) } /* }}}*/ +PHP_FUNCTION(dl_test_frameless) +{ + zend_long value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END(); + + RETURN_LONG(value); +} + +ZEND_FRAMELESS_FUNCTION(dl_test_frameless, 1) +{ + zend_long value; + + Z_FLF_PARAM_LONG(1, value); + + RETVAL_LONG(value); + +flf_clean:; +} + /* {{{ PHP_DL_TEST_USE_REGISTER_FUNCTIONS_DIRECTLY */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_use_register_functions_directly, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/ext/dl_test/dl_test.stub.php b/ext/dl_test/dl_test.stub.php index c2d8ff577780..4f5a445e9837 100644 --- a/ext/dl_test/dl_test.stub.php +++ b/ext/dl_test/dl_test.stub.php @@ -9,6 +9,11 @@ function dl_test_test1(): void {} function dl_test_test2(string $str = ""): string {} +/** + * @frameless-function {"arity": 1} + */ +function dl_test_frameless(int $value): int {} + class DlTest { public function test(string $str = ""): string {} } diff --git a/ext/dl_test/dl_test_arginfo.h b/ext/dl_test/dl_test_arginfo.h index 84c7c151ae79..bd7752211150 100644 --- a/ext/dl_test/dl_test_arginfo.h +++ b/ext/dl_test/dl_test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0641a8eeff00e6c8083fe4a8639f970e3ba80db9 */ + * Stub hash: 0ab7bc0f3289a77ecb1850a1cc7056b7c5297fda */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -8,18 +8,31 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test2, 0, 0, IS_STRING, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, str, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_frameless, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_DlTest_test arginfo_dl_test_test2 #define arginfo_class_DlTestSuperClass_test arginfo_dl_test_test2 + +ZEND_FRAMELESS_FUNCTION(dl_test_frameless, 1); +static const zend_frameless_function_info frameless_function_infos_dl_test_frameless[] = { + { ZEND_FRAMELESS_FUNCTION_NAME(dl_test_frameless, 1), 1 }, + { 0 }, +}; + ZEND_FUNCTION(dl_test_test1); ZEND_FUNCTION(dl_test_test2); +ZEND_FUNCTION(dl_test_frameless); ZEND_METHOD(DlTest, test); ZEND_METHOD(DlTestSuperClass, test); static const zend_function_entry ext_functions[] = { ZEND_FE(dl_test_test1, arginfo_dl_test_test1) ZEND_FE(dl_test_test2, arginfo_dl_test_test2) + ZEND_RAW_FENTRY("dl_test_frameless", zif_dl_test_frameless, arginfo_dl_test_frameless, 0, frameless_function_infos_dl_test_frameless, NULL) ZEND_FE_END }; diff --git a/ext/dl_test/tests/frameless_temporary.inc b/ext/dl_test/tests/frameless_temporary.inc new file mode 100644 index 000000000000..8b452a5e8dce --- /dev/null +++ b/ext/dl_test/tests/frameless_temporary.inc @@ -0,0 +1,8 @@ +getMessage(), "\n"; +} diff --git a/ext/dl_test/tests/frameless_temporary.phpt b/ext/dl_test/tests/frameless_temporary.phpt new file mode 100644 index 000000000000..fe38123d99e8 --- /dev/null +++ b/ext/dl_test/tests/frameless_temporary.phpt @@ -0,0 +1,53 @@ +--TEST-- +dl() of a frameless function does not keep the freed function record +--SKIPIF-- + +--FILE-- + ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], +], $pipes); +fclose($pipes[0]); +$out = stream_get_contents($pipes[1]); +stream_get_contents($pipes[2]); +fclose($pipes[1]); +fclose($pipes[2]); +echo $out; +$code = proc_close($proc); +if ($code !== 0) { + echo "exit:$code\n"; +} +?> +--EXPECT-- +int(7) +dl_test_frameless(): Argument #1 ($value) must be of type int, string given +int(7) +dl_test_frameless(): Argument #1 ($value) must be of type int, string given diff --git a/ext/dl_test/tests/frameless_temporary_cgi.inc b/ext/dl_test/tests/frameless_temporary_cgi.inc new file mode 100644 index 000000000000..a5a9398f0e4a --- /dev/null +++ b/ext/dl_test/tests/frameless_temporary_cgi.inc @@ -0,0 +1,7 @@ + +--EXTENSIONS-- +opcache +--INI-- +enable_dl=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x10000 +--FILE-- + +--EXPECTF-- +%A0001 INIT_FCALL 1 %d string("dl_test_frameless") +%Aint(7) +dl_test_frameless(): Argument #1 ($value) must be of type int, string given