From a098ae8d4a3b6513dc89cd47614674ad1b41606c Mon Sep 17 00:00:00 2001 From: lazerg Date: Sun, 25 Oct 2026 12:50:57 +0500 Subject: [PATCH] Fix GH-24668: zend_set_local_var() leaks the previous CV value Co-authored-by: yasu20200121web-prog <258152685+yasu20200121web-prog@users.noreply.github.com> --- NEWS | 2 ++ Zend/zend_execute_API.c | 7 ++++++- ext/zend_test/test.c | 18 ++++++++++++++++++ ext/zend_test/test.stub.php | 2 ++ ext/zend_test/test_arginfo.h | 9 ++++++++- ext/zend_test/tests/gh24668.phpt | 31 +++++++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 ext/zend_test/tests/gh24668.phpt diff --git a/NEWS b/NEWS index 4a3383c25a4f..f78fe08cdddd 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ PHP NEWS for grouping. (Klaas Skelte van der Werf) . Fixed bug GH-24265 (Unwind/GracefulExit discarded in zend_fiber_execute()). (David Carlier) + . Fixed bug GH-24668 (zend_set_local_var() leaks the previous value of an + existing CV). (yasu20200121web-prog, lazerg) - Date: . Fixed bug GH-20503 (Assertion failure with ext/date DateInterval property diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 30eb6ec8af3d..01555e6165c4 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1960,7 +1960,10 @@ ZEND_API zend_result zend_set_local_var(zend_string *name, zval *value, bool for if (ZSTR_H(*str) == h && zend_string_equal_content(*str, name)) { zval *var = EX_VAR_NUM(str - op_array->vars); + zval garbage; + ZVAL_COPY_VALUE(&garbage, var); ZVAL_COPY_VALUE(var, value); + zval_ptr_dtor(&garbage); return SUCCESS; } str++; @@ -2002,8 +2005,10 @@ ZEND_API zend_result zend_set_local_var_str(const char *name, size_t len, zval * if (ZSTR_H(*str) == h && zend_string_equals_cstr(*str, name, len)) { zval *var = EX_VAR_NUM(str - op_array->vars); - zval_ptr_dtor(var); + zval garbage; + ZVAL_COPY_VALUE(&garbage, var); ZVAL_COPY_VALUE(var, value); + zval_ptr_dtor(&garbage); return SUCCESS; } str++; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 09940e664e11..d9d80bf6ccf1 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1056,6 +1056,24 @@ static ZEND_FUNCTION(zend_new_singleton_array) array_init_singleton(return_value); } +static ZEND_FUNCTION(zend_test_set_local_var) +{ + zend_string *name; + zval *value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(name) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + + Z_TRY_ADDREF_P(value); + if (zend_set_local_var(name, value, false) == FAILURE) { + zval_ptr_dtor(value); + RETURN_FALSE; + } + RETURN_TRUE; +} + static zend_object *zend_test_class_new(zend_class_entry *class_type) { zend_object *obj = zend_objects_new(class_type); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 6ea169d315b7..36d1be17a177 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -341,6 +341,8 @@ function zend_test_gh19792(): void {} function zend_test_handle_post_data(mixed $stream, string $content_type, int $content_length): void {} function zend_new_singleton_array(): array {} + + function zend_test_set_local_var(string $name, mixed $value): bool {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index aa11ba1f77cb..e2a388659ecd 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: de4bd0213b06d7be9e5a55ef7757ca137b03fa0e */ + * Stub hash: f25b9c451734f903ba385f9fbf05fb198ecd83af */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -200,6 +200,11 @@ ZEND_END_ARG_INFO() #define arginfo_zend_new_singleton_array arginfo_zend_test_array_return +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_set_local_var, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -343,6 +348,7 @@ static ZEND_FUNCTION(zend_test_gh18756); static ZEND_FUNCTION(zend_test_gh19792); static ZEND_FUNCTION(zend_test_handle_post_data); static ZEND_FUNCTION(zend_new_singleton_array); +static ZEND_FUNCTION(zend_test_set_local_var); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -465,6 +471,7 @@ static const zend_function_entry ext_functions[] = { #endif ZEND_FE(zend_test_handle_post_data, arginfo_zend_test_handle_post_data) ZEND_FE(zend_new_singleton_array, arginfo_zend_new_singleton_array) + ZEND_FE(zend_test_set_local_var, arginfo_zend_test_set_local_var) #if (PHP_VERSION_ID >= 80400) ZEND_RAW_FENTRY(ZEND_NS_NAME("ZendTestNS2", "namespaced_func"), zif_ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func, 0, NULL, NULL) #else diff --git a/ext/zend_test/tests/gh24668.phpt b/ext/zend_test/tests/gh24668.phpt new file mode 100644 index 000000000000..e3b456b40a71 --- /dev/null +++ b/ext/zend_test/tests/gh24668.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-24668 (zend_set_local_var() leaks the previous value of an existing CV) +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +dtor +bool(true) +int(42) +array(1) { + [0]=> + int(42) +}