From c5f28c7cf0a052f48e47877c7aa5c5bcc54f1cfc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:11:38 +0100 Subject: [PATCH 1/7] Fix GH-20584: Information Leak of Memory The string added had uninitialized memory due to php_read_stream_all_chunks() not moving the buffer position, resulting in the same data always being overwritten instead of new data being added to the end of the buffer. This is backport as there is a security impact as described in GHSA-3237-qqm7-mfv7 . --- ext/standard/image.c | 1 + ext/standard/tests/image/gh20584.phpt | 39 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 ext/standard/tests/image/gh20584.phpt diff --git a/ext/standard/image.c b/ext/standard/image.c index 5200295f3af28..269117318c4b9 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -434,6 +434,7 @@ static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_ if (read_now < stream->chunk_size && read_total != length) { return 0; } + buffer += read_now; } while (read_total < length); return read_total; diff --git a/ext/standard/tests/image/gh20584.phpt b/ext/standard/tests/image/gh20584.phpt new file mode 100644 index 0000000000000..d117f21820264 --- /dev/null +++ b/ext/standard/tests/image/gh20584.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-20584 (Information Leak of Memory) +--CREDITS-- +Nikita Sveshnikov (Positive Technologies) +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) From 727a4ddc39ca9b78131e06b5537fe8b6c3dd6fe7 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 11 Oct 2025 19:37:26 +0200 Subject: [PATCH 2/7] Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref --- ext/pdo/pdo_sql_parser.re | 6 +++++ ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt | 28 ++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index 6bb0837fb31f9..7f4721d12a630 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -287,6 +287,12 @@ safe: } plc->quoted = stmt->dbh->methods->quoter(stmt->dbh, buf, param_type); + if (plc->quoted == NULL) { + /* bork */ + ret = -1; + strncpy(stmt->error_code, stmt->dbh->error_code, 6); + goto clean_up; + } } } diff --git a/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt b/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt new file mode 100644 index 0000000000000..736354cab13cb --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt @@ -0,0 +1,28 @@ +--TEST-- +#GHSA-8xr5-qppj-gvwj: NULL Pointer Derefernce for failed user input quoting +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + +$sql = "SELECT * FROM users where username = :username"; +$stmt = $db->prepare($sql); + +$p1 = "alice\x99"; +var_dump($stmt->execute(['username' => $p1])); + +?> +--EXPECT-- +bool(false) From 8b801151bd54b36aae4593ed6cfc096e8122b415 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:23:11 +0100 Subject: [PATCH 3/7] Fix GHSA-h96m-rvf9-jgm2 --- ext/standard/array.c | 7 ++++++- .../tests/array/GHSA-h96m-rvf9-jgm2.phpt | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 86d70a8844e30..e3474c7c8fda2 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3771,7 +3771,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET int argc, i; zval *src_entry; HashTable *src, *dest; - uint32_t count = 0; + uint64_t count = 0; ZEND_PARSE_PARAMETERS_START(0, -1) Z_PARAM_VARIADIC('+', args, argc) @@ -3791,6 +3791,11 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET count += zend_hash_num_elements(Z_ARRVAL_P(arg)); } + if (UNEXPECTED(count >= HT_MAX_SIZE)) { + zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE); + RETURN_THROWS(); + } + if (argc == 2) { zval *ret = NULL; diff --git a/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt b/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt new file mode 100644 index 0000000000000..2e3e85357e13c --- /dev/null +++ b/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt @@ -0,0 +1,16 @@ +--TEST-- +GHSA-h96m-rvf9-jgm2 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +The total number of elements must be lower than %d From ed70b1ea43a9b7ffa2f53b3e5d6ba403f37ae81c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 6 Sep 2025 21:55:13 +0200 Subject: [PATCH 4/7] Fix GHSA-www2-q4fc-65wf --- ext/standard/basic_functions.c | 12 ++-- ext/standard/dns.c | 6 +- ext/standard/dns_win32.c | 6 +- .../tests/network/ghsa-www2-q4fc-65wf.phpt | 62 +++++++++++++++++++ 4 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index ddaf1368410bc..18db1604678ca 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -638,7 +638,7 @@ PHP_FUNCTION(inet_pton) char buffer[17]; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(address, address_len) + Z_PARAM_PATH(address, address_len) ZEND_PARSE_PARAMETERS_END(); memset(buffer, 0, sizeof(buffer)); @@ -675,7 +675,7 @@ PHP_FUNCTION(ip2long) #endif ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(addr, addr_len) + Z_PARAM_PATH(addr, addr_len) ZEND_PARSE_PARAMETERS_END(); #ifdef HAVE_INET_PTON @@ -2249,8 +2249,8 @@ PHP_FUNCTION(getservbyname) struct servent *serv; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(name) - Z_PARAM_STRING(proto, proto_len) + Z_PARAM_PATH_STR(name) + Z_PARAM_PATH(proto, proto_len) ZEND_PARSE_PARAMETERS_END(); @@ -2293,7 +2293,7 @@ PHP_FUNCTION(getservbyport) ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(port) - Z_PARAM_STRING(proto, proto_len) + Z_PARAM_PATH(proto, proto_len) ZEND_PARSE_PARAMETERS_END(); serv = getservbyport(htons((unsigned short) port), proto); @@ -2316,7 +2316,7 @@ PHP_FUNCTION(getprotobyname) struct protoent *ent; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(name, name_len) + Z_PARAM_PATH(name, name_len) ZEND_PARSE_PARAMETERS_END(); ent = getprotobyname(name); diff --git a/ext/standard/dns.c b/ext/standard/dns.c index 6d22e644a8eff..73a60f2a8259b 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -399,7 +399,7 @@ PHP_FUNCTION(dns_check_record) #endif ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(hostname, hostname_len) + Z_PARAM_PATH(hostname, hostname_len) Z_PARAM_OPTIONAL Z_PARAM_STR(rectype) ZEND_PARSE_PARAMETERS_END(); @@ -846,7 +846,7 @@ PHP_FUNCTION(dns_get_record) bool raw = 0; ZEND_PARSE_PARAMETERS_START(1, 5) - Z_PARAM_STRING(hostname, hostname_len) + Z_PARAM_PATH(hostname, hostname_len) Z_PARAM_OPTIONAL Z_PARAM_LONG(type_param) Z_PARAM_ZVAL(authns) @@ -1084,7 +1084,7 @@ PHP_FUNCTION(dns_get_mx) #endif ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(hostname, hostname_len) + Z_PARAM_PATH(hostname, hostname_len) Z_PARAM_ZVAL(mx_list) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(weight_list) diff --git a/ext/standard/dns_win32.c b/ext/standard/dns_win32.c index bef90cd61f283..0302b8542a0de 100644 --- a/ext/standard/dns_win32.c +++ b/ext/standard/dns_win32.c @@ -48,7 +48,7 @@ PHP_FUNCTION(dns_get_mx) /* {{{ */ DNS_STATUS status; /* Return value of DnsQuery_A() function */ PDNS_RECORD pResult, pRec; /* Pointer to DNS_RECORD structure */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "pz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { RETURN_THROWS(); } @@ -102,7 +102,7 @@ PHP_FUNCTION(dns_check_record) DNS_STATUS status; /* Return value of DnsQuery_A() function */ PDNS_RECORD pResult; /* Pointer to DNS_RECORD structure */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|S", &hostname, &hostname_len, &rectype) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|S", &hostname, &hostname_len, &rectype) == FAILURE) { RETURN_THROWS(); } @@ -354,7 +354,7 @@ PHP_FUNCTION(dns_get_record) int type, type_to_fetch, first_query = 1, store_results = 1; bool raw = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lz!z!b", + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|lz!z!b", &hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) { RETURN_THROWS(); } diff --git a/ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt b/ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt new file mode 100644 index 0000000000000..3d082c8e95226 --- /dev/null +++ b/ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt @@ -0,0 +1,62 @@ +--TEST-- +GHSA-www2-q4fc-65wf +--DESCRIPTION-- +This is a ZPP test but *keep* this as it is security-sensitive! +--FILE-- +getMessage(), "\n"; +} +try { + dns_get_mx("\0", $out); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + dns_get_record("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getprotobyname("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getservbyname("\0", "tcp"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getservbyname("x", "tcp\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getservbyport(0, "tcp\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + inet_pton("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + ip2long("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +dns_check_record(): Argument #1 ($hostname) must not contain any null bytes +dns_get_mx(): Argument #1 ($hostname) must not contain any null bytes +dns_get_record(): Argument #1 ($hostname) must not contain any null bytes +getprotobyname(): Argument #1 ($protocol) must not contain any null bytes +getservbyname(): Argument #1 ($service) must not contain any null bytes +getservbyname(): Argument #2 ($protocol) must not contain any null bytes +getservbyport(): Argument #2 ($protocol) must not contain any null bytes +inet_pton(): Argument #1 ($ip) must not contain any null bytes +ip2long(): Argument #1 ($ip) must not contain any null bytes From c48a9f42d336dc22e72141775022f4588ab4b2dd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Dec 2025 13:49:02 +0100 Subject: [PATCH 5/7] Update NEWS with info about security issues --- NEWS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NEWS b/NEWS index 998b5d97d6351..6ac638073fae6 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,18 @@ PHP NEWS . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) +- PDO: + . Fixed GHSA-8xr5-qppj-gvwj (PDO quoting result null deref). (CVE-2025-14180) + (Jakub Zelenka) + +- Standard: + . Fixed GHSA-www2-q4fc-65wf (Null byte termination in dns_get_record()). + (ndossche) + . Fixed GHSA-h96m-rvf9-jgm2 (Heap buffer overflow in array_merge()). + (CVE-2025-14178) (ndossche) + . Fixed GHSA-3237-qqm7-mfv7 (Information Leak of Memory in getimagesize). + (CVE-2025-14177) (ndossche) + 03 Jul 2025, PHP 8.1.33 - PGSQL: From fb1ec9a5a7c35e0bdb4aa5b793d4aac7bb765a9c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 15 Dec 2025 23:39:07 +0100 Subject: [PATCH 6/7] Fix uncatchable exception thrown in generator This procedure may be called during i_free_compiled_variables(), when EG(current_execute_data) is unfortunately already reset to the parent frame. EG(opline_before_exception) does not actually belong to this frame. Furthermore, setting opline to EG(exception_op) early will miss a later zend_rethrow_exception(), which will also miss installation of the correct EG(opline_before_exception). Fixes GH-20714 Closes GH-20716 --- NEWS | 1 + Zend/tests/gh20714.phpt | 29 +++++++++++++++++++++++++++++ Zend/zend_generators.c | 6 ++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh20714.phpt diff --git a/NEWS b/NEWS index beff3f224ce2f..11ae976b3238f 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ PHP NEWS with dynamic class const lookup default argument). (ilutov) . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string()). (ndossche) + . Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov) - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). diff --git a/Zend/tests/gh20714.phpt b/Zend/tests/gh20714.phpt new file mode 100644 index 0000000000000..10ffde555f896 --- /dev/null +++ b/Zend/tests/gh20714.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-20714: Uncatchable exception thrown in generator +--CREDITS-- +Grégoire Paris (greg0ire) +--FILE-- + +--EXPECT-- +Caught diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5ba215f788ce9..cfadf06b46f0b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -321,7 +321,9 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_object *old_exception = NULL; const zend_op *old_opline_before_exception = NULL; if (EG(exception)) { - if (EG(current_execute_data)) { + if (EG(current_execute_data) + && EG(current_execute_data)->opline + && EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) { EG(current_execute_data)->opline = EG(opline_before_exception); old_opline_before_exception = EG(opline_before_exception); } @@ -337,7 +339,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_generator_resume(generator); if (old_exception) { - if (EG(current_execute_data)) { + if (old_opline_before_exception) { EG(current_execute_data)->opline = EG(exception_op); EG(opline_before_exception) = old_opline_before_exception; } From b19a3536e83a58a0f5319703c9e34f18c9153b5f Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 16 Dec 2025 15:45:08 -0300 Subject: [PATCH 7/7] [ci skip] PHP 8.5 | UPGRADING: document IMAGETYPE_HEIF constant (#20713) --- UPGRADING | 1 + 1 file changed, 1 insertion(+) diff --git a/UPGRADING b/UPGRADING index 94b272c69c70f..cc1ff9938ad53 100644 --- a/UPGRADING +++ b/UPGRADING @@ -935,6 +935,7 @@ PHP 8.5 UPGRADE NOTES . T_PIPE. - Standard: + . IMAGETYPE_HEIF. . IMAGETYPE_SVG when libxml is loaded. ========================================