From 20f9772063cc3eb1e021508adc1433b62b6e5a25 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:48:18 +0100 Subject: [PATCH 1/2] ext/standard: Fix memory leak in mail() when header key is numeric Closes GH-20776 --- NEWS | 1 + ext/standard/mail.c | 3 ++- ext/standard/tests/mail/gh20776.phpt | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/mail/gh20776.phpt diff --git a/NEWS b/NEWS index 2fb8cd3620fec..604f81d3078b8 100644 --- a/NEWS +++ b/NEWS @@ -57,6 +57,7 @@ PHP NEWS - Standard: . Fix error check for proc_open() command. (ndossche) + . Fix memory leak in mail() when header key is numeric. (Girgias) 18 Dec 2025, PHP 8.4.16 diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 35c23a0be76c0..c9b34fbdfc92d 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -214,7 +214,8 @@ PHPAPI zend_string *php_mail_build_headers(HashTable *headers) ZEND_HASH_FOREACH_KEY_VAL(headers, idx, key, val) { if (!key) { zend_type_error("Header name cannot be numeric, " ZEND_LONG_FMT " given", idx); - break; + smart_str_free(&s); + return NULL; } ZVAL_DEREF(val); /* https://tools.ietf.org/html/rfc2822#section-3.6 */ diff --git a/ext/standard/tests/mail/gh20776.phpt b/ext/standard/tests/mail/gh20776.phpt new file mode 100644 index 0000000000000..aec68c4719202 --- /dev/null +++ b/ext/standard/tests/mail/gh20776.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-20776: mail() memory leak when header array contains numeric keys +--FILE-- + 'Value', 5 => 'invalid key'])); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +TypeError: Header name cannot be numeric, 5 given From 7d4ba807054425074641f9e63a9445cbed12caca Mon Sep 17 00:00:00 2001 From: Giovanni Giacobbi Date: Thu, 25 Dec 2025 21:10:29 +0100 Subject: [PATCH 2/2] gen_stub: Fix php-parser package download (#20775) If the system wgetrc has the `content-disposition = on` option, the file is actually saved as `PHP-Parser-5.0.0.tar.gz`, causing a subsequent failure. Even with `content-disposition = off`, if for any reason the download file already exists and is corrupted, it won't be overwritten, and a new file such as `v5.0.0.tar.gz.1` is saved instead. We solve both problems by enforcing the name of the downloaded file. Also, if for any other reason the unpacking should fail, remove the created directory to allow further attempts. --- build/gen_stub.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index f1d8b43862e62..a2f488a6a226d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -5970,9 +5970,10 @@ function installPhpParser(string $version, string $phpParserDir) { chdir(__DIR__); $tarName = "v$version.tar.gz"; - passthru("wget https://github.com/nikic/PHP-Parser/archive/$tarName", $exit); + $downloadUrl = "https://github.com/nikic/PHP-Parser/archive/$tarName"; + passthru("wget -O $tarName $downloadUrl", $exit); if ($exit !== 0) { - passthru("curl -LO https://github.com/nikic/PHP-Parser/archive/$tarName", $exit); + passthru("curl -LO $downloadUrl", $exit); } if ($exit !== 0) { throw new Exception("Failed to download PHP-Parser tarball"); @@ -5982,6 +5983,7 @@ function installPhpParser(string $version, string $phpParserDir) { } passthru("tar xvzf $tarName -C PHP-Parser-$version --strip-components 1", $exit); if ($exit !== 0) { + rmdir($phpParserDir); throw new Exception("Failed to extract PHP-Parser tarball"); } unlink(__DIR__ . "/$tarName");