From f441305a367efefa937ed0eab903e131fc6baec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLamentXU123=E2=80=9D?= <108666168+LamentXU123@users.noreply.github.com> Date: Tue, 12 May 2026 18:16:47 +0800 Subject: [PATCH] Zend: Fix leaks when destructors throw during shutdown --- NEWS | 2 ++ Zend/tests/gh22010.phpt | 22 ++++++++++++++++++++++ Zend/zend_objects.c | 7 ++++++- Zend/zend_objects_API.c | 7 ++++++- 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh22010.phpt diff --git a/NEWS b/NEWS index c2767fd3c30c..a0becbde9965 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,8 @@ PHP NEWS . Fixed bug GH-21603 (Missing addref for __unset). (ilutov) . Fixed bug GH-21760 (Trait with class constant name conflict against enum case causes SEGV). (Pratik Bhujel) + . Fixed GH-22010 (Exception thrown in destructor during shutdown causes a + memory leak). (Weilin Du) - CLI: . Fixed bug GH-21754 (`--rf` command line option with a method triggers diff --git a/Zend/tests/gh22010.phpt b/Zend/tests/gh22010.phpt new file mode 100644 index 000000000000..826949eba6b8 --- /dev/null +++ b/Zend/tests/gh22010.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-22010: Exception thrown in destructor during shutdown +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception: A::__destruct in %s:%d +Stack trace: +#0 [internal function]: A->__destruct() +#1 {main} + thrown in %s on line %d diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 8a54a8398691..56390efe6116 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -193,7 +193,12 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } } - zend_call_known_instance_method_with_0_params(destructor, object, NULL); + zend_try { + zend_call_known_instance_method_with_0_params(destructor, object, NULL); + } zend_catch { + OBJ_RELEASE(object); + zend_bailout(); + } zend_end_try(); if (old_exception) { if (EG(current_execute_data)) { diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index c19873cf3be3..4eac6a8004dc 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -54,7 +54,12 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_sto if (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor) { GC_ADDREF(obj); - obj->handlers->dtor_obj(obj); + zend_try { + obj->handlers->dtor_obj(obj); + } zend_catch { + GC_DELREF(obj); + zend_bailout(); + } zend_end_try(); GC_DELREF(obj); } }