From b3e3369c91b957d4b9d9caae2c868b20de131f61 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 7 Aug 2025 19:27:58 +0200 Subject: [PATCH 1/2] Fix GH-19397: mb_list_encodings() can cause crashes on shutdown The request shutdown does not necessarily hold the last reference, if there is still a CV that refers to the array. --- ext/mbstring/mbstring.c | 6 ++++-- ext/mbstring/tests/gh19397.phpt | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 ext/mbstring/tests/gh19397.phpt diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index dec565707fa78..67992a5cd2a74 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -1165,8 +1165,10 @@ PHP_RSHUTDOWN_FUNCTION(mbstring) MBSTRG(outconv_state) = 0; if (MBSTRG(all_encodings_list)) { - GC_DELREF(MBSTRG(all_encodings_list)); - zend_array_destroy(MBSTRG(all_encodings_list)); + if (GC_DELREF(MBSTRG(all_encodings_list)) == 0) { + /* must be *array* destroy to remove from GC root buffer and free the hashtable itself */ + zend_array_destroy(MBSTRG(all_encodings_list)); + } MBSTRG(all_encodings_list) = NULL; } diff --git a/ext/mbstring/tests/gh19397.phpt b/ext/mbstring/tests/gh19397.phpt new file mode 100644 index 0000000000000..e6e07b161c089 --- /dev/null +++ b/ext/mbstring/tests/gh19397.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-19397 (mb_list_encodings() can cause crashes on shutdown) +--EXTENSIONS-- +mbstring +--FILE-- + 0); +?> +--EXPECT-- +bool(true) From e879eadf69e797c15a7bc7b9bc8463ad9b3f76f0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:58:02 +0200 Subject: [PATCH 2/2] Cleaner --- ext/mbstring/mbstring.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 67992a5cd2a74..1d5c27a2a3815 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -1165,10 +1165,8 @@ PHP_RSHUTDOWN_FUNCTION(mbstring) MBSTRG(outconv_state) = 0; if (MBSTRG(all_encodings_list)) { - if (GC_DELREF(MBSTRG(all_encodings_list)) == 0) { - /* must be *array* destroy to remove from GC root buffer and free the hashtable itself */ - zend_array_destroy(MBSTRG(all_encodings_list)); - } + /* must be *array* release to remove from GC root buffer and free the hashtable itself */ + zend_array_release(MBSTRG(all_encodings_list)); MBSTRG(all_encodings_list) = NULL; }