Commit d3f361c3b7f for php.net

commit d3f361c3b7ff7d9fd428f9ee5112885898cb4e43
Merge: 1b091dcf375 416985389d0
Author: Ilija Tovilo <ilija.tovilo@me.com>
Date:   Tue Jul 28 11:50:34 2026 +0200

    Merge branch 'PHP-8.3' into PHP-8.4

    * PHP-8.3:
      Add NEWS entries
      Fix GHSA-vc5h-9ppw-p5f3: phar circular symlink crash
      Fix SQL injection in ext-pgsql via E'...' backslash breakout
      libgd patch for CVE-2026-9672

diff --cc NEWS
index 1dc666e361b,875982a3eb9..54dea5289ff
--- a/NEWS
+++ b/NEWS
@@@ -1,198 -1,26 +1,211 @@@
  PHP                                                                        NEWS
  |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- ?? ??? ????, PHP 8.4.24
 -?? ??? ????, PHP 8.3.33
++?? ??? ????, PHP 8.4.25

  - Date:
    . Fixed leak on double DatePeriod::__construct() call. (ilutov)

 +- DOM:
 +  . Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
 +    declares a default value for the attribute). (iliaal)
 +  . Fixed bug GH-22447 (UAF at dom_objects_free_storage when setting an
 +    attribute node that collides by local name with a namespaced
 +    attribute). (David Carlier)
 +
 +- MBString:
 +  . Fixed bug GH-22779 (mb_strrpos() returns the wrong position for a negative
 +    offset in a non-UTF-8 encoding). (Eyüp Can Akman)
 +
 +- PCRE:
 +  . Fixed bug GH-21134 (Crash with \C + UTF-8). Using \C in UTF-8 patterns is
 +    now forbidden. (Arnaud)
 +
 +- Sockets:
 +  . Fixed various memory related issues in ext/sockets. (David Carlier)
 +
 +- Streams:
 +  . Fixed bug GH-15836 (Use-after-free when a user stream filter accesses
 +    $this->stream during the close flush). (iliaal)
 +
 +30 Jul 2026, PHP 8.4.24
 +
++- BCMath:
++  . Fixed GHSA-x692-q9x7-8c3f (Out-of-bounds write in bccomp()).
++    (CVE-2026-17544) (Recep Asan)
++
 +- Calendar:
 +  . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with
 +    INT_MAX year). (arshidkv12)
 +
 +- Date:
 +  . Update timelib to 2022.17. (Derick)
 +  . Fixed bug GH-19803 (Parsing a string with a single white space does create
 +    an error). (Derick)
 +  . Fixed Unix timestamps in February of the year 0 are misparsed with
 +    @-notation. (LukasGelbmann)
 +  . Fixed bug GH-11310 (__debugInfo does nothing on userland classes extending
 +    Date classes). (Derick)
 +
 +- DBA:
 +  . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)
 +
 +- DOM:
 +  . Fixed bug GH-22570 (Stack overflow when serializing a deeply nested
 +    Dom\XMLDocument). (iliaal)
 +
 +- Exif:
 +  . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
 +    warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman)
 +
+ - GD:
+   . Upgrade libgd. (CVE-2026-9672) (Pierre Joye)
+
 +- Hash:
 +  . Fixed bug GH-18173 (ext/hash relies on implementation-defined malloc
 +    alignment). (iliaal)
 +
 +- Intl:
 +  . Fixed Locale::lookup() and locale_lookup() to return NULL instead of the
 +    fallback locale when a language tag cannot be canonicalized. (Weilin Du)
 +  . Fixed memory leaks when calling Collator::__construct() or
 +    Spoofchecker::__construct() twice. (Weilin Du)
 +  . Fixed IntlChar methods leaving stale global error state after successful
 +    calls. (Xuyang Zhang)
 +
 +- ODBC:
 +  . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
 +    driver-reported display size). (iliaal)
 +
 +- OpenSSL:
 +  . Fixed timeout for supplemental read at end of a blocking stream in SSL
 +    stream wrapper. (ilutov)
 +
 +- PDO_ODBC:
 +  . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN
 +    carries no credentials). (iliaal)
 +  . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
 +    driver-reported display size). (iliaal)
 +  . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
 +    longer than the declared maxlen). (iliaal)
 +  . Fixed bug GH-22665 (Out-of-bounds write when the ODBC driver reports a
 +    diagnostic message length beyond the error buffer). (iliaal)
 +
+ - PGSQL:
+   . Fixed GHSA-7qpv-r5mr-78m4 (SQL injection via E'...' backslash breakout).
+     (CVE-2026-17543) (ilutov)
+
  - Phar:
 +  . Fixed inconsistent handling of the magic ".phar" directory. Paths such as
 +    "/.phar" remain protected, while non-magic paths that merely start with
 +    ".phar" are handled consistently across file and directory creation,
 +    copying, ArrayAccess, stream lookup, directory iteration and extraction.
 +    (Weilin Du)
+   . Fixed GHSA-vc5h-9ppw-p5f3 (Crash via recursive symlinks). (CVE-2026-7260)
+     (Jakub Zelenka)

 -02 Jul 2026, PHP 8.3.32
 +- PHPDBG:
 +  . Fixed bug GH-17387 (Trivial crash in phpdbg lexer). (iliaal)
 +  . Fixed fleaked lowercased lookup keys in phpdbg_resolve_opline_break.
 +    (jorgsowa)
 +  . Fixed off-by-one in phpdbg_safe_class_lookup() causing class lookups to
 +    always fail during phpdbg's signal-safe interruption path. (jorgsowa)
 +
 +- Reflection:
 +  . Fixed bug GH-22324 (Ignore leading namespace separator in
 +    ReflectionParameter::__construct()). (jorgsowa)
 +  . Fixed bug GH-22441 (ReflectionClass::hasProperty() and getProperty() ignore
 +    dynamic properties shadowing a private parent property). (iliaal)
 +  . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value
 +    with null bytes truncates output). (DanielEScherzer)
 +  . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes).
 +    (DanielEScherzer)
 +
 +- Session:
 +  . Fixed bug GH-21314 (Different session garbage collector behavior between
 +    PHP 8.3 and PHP 8.5). (jorgsowa)
 +
 +- SPL:
 +  . Fix class_parents for classes with leading slash in non-autoload mode.
 +    (jorgsowa)
 +  . Ignore leading back-slash in class_parents(), class_implements(), and
 +    class_uses(). (jorgsowa)
 +  . Fixed bug GH-16217 (SplFileObject::fputcsv() on an uninitialized object
 +    segfaults). (iliaal)
 +
 +- Standard:
 +  . Fixed bug GH-22360 (convert.base64-encode corruption on
 +    incremental flush). (David Carlier)
 +  . Fixed bug GH-22395 (base_convert() outputs at most 64 characters).
 +    (Weilin Du)
 +  . Fixed integer overflow in getimagesize() and getimagesizefromstring()
 +    when parsing an IFF chunk with a size of INT_MAX. (David Carlier, Weilin Du)
 +  . Fixed bug GH-22678 (Use-after-free in array_multisort() when the comparator
 +    mutates the array being sorted). (azchin, iliaal)

  - Streams:
 -  . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL
 -    and a proxy set). (CVE-2026-12184) (ndossche)
 +  . Fixed bug GH-22617 (persistent stream keys truncated at null bytes, causing
 +    distinct abstract unix domain sockets to share a resource). (David Carlier)
 +
 +- Zip:
 +  . Fixed bug GH-22649 (ZipArchive::setCommentName() and setCommentIndex()
 +    could crash after overwriting an entry and resetting its inherited
 +    unchanged comment). (Weilin Du)
 +  . Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores
 +    ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du)
 +
 +02 Jul 2026, PHP 8.4.23
 +
 +- Core:
 +  . Fixed bug GH-22280 (Incorrect compile error for goto to label preceding
 +    try/finally block). (Pratik Bhujel)
 +  . Fixed bug GH-22290 (AST pretty printing does not correctly handle strings
 +    containing NUL). (iliaal)
 +
 +- BCMath:
 +  . Fixed issues with oversized allocations and signed overflow in bcround()
 +    and BcMath\Number::round(). (edorian)
 +
 +- Date:
 +  . Fix incorrect recurrence check of DatePeriod::createFromISO8601String().
 +    (ndossche)
 +
 +- DOM:
 +  . Fix GH-22219 (Dom\XMLDocument::schemaValidate fails to resolve
 +    xs:QName with prefix from imported schema). (David Carlier)
 +
 +- Exif:
 +  . Read correct value for single and double tags. (ndossche)
 +
 +- GD:
 +  . Fixed bug GH-22121 (Double free in gdImageSetStyle() after
 +    overflow-triggered early return). (iliaal)
 +  . Fixed bug GH-19666 (imageconvolution() unexpected nan filter value).
 +    (David Carlier)
 +  . Fixed bug GH-19739 (imageellipse/imagefilledellipse overflow).
 +    (David Carlier)
 +  . Fixed bug GH-19730 (imageaffine overflow).
 +    (David Carlier)
 +
 +- Intl:
 +  . Fix incorrect argument positions for uninitialized calendar arguments in
 +    IntlCalendar::equals(), ::before(), ::after(), and ::isEquivalentTo(), and
 +    for invalid start/end arguments in transliterator_transliterate().
 +    (Weilin Du)
 +  . Fixed IntlTimeZone::getDisplayName() to synchronize object error state
 +    for invalid display types. (Weilin Du)
 +  . Fixed Spoofchecker restriction-level APIs to only be exposed with ICU 53
 +    and later. (Graham Campbell)
 +
 +- mysqli:
 +  . Fix stmt->query leak in mysqli_execute_query() validation errors.
 +    (David Carlier)
 +
 +- Opcache:
 +  . Fixed bug GH-20469 (Unsafe inheritance cache replay with reentrant
 +    autoloading). (Levi Morrison)
 +  . Fixed bug GH-22158 (Tracing JIT dispatches the observer begin handler
 +    through the wrong run_time_cache slot on megamorphic calls). (ptondereau,
 +    iliaal)

  - OpenSSL:
    . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
diff --cc ext/pgsql/tests/10pg_convert_9.phpt
index d733b888fe0,fb36e5801c8..2ef325dbc97
--- a/ext/pgsql/tests/10pg_convert_9.phpt
+++ b/ext/pgsql/tests/10pg_convert_9.phpt
@@@ -51,15 -50,11 +53,19 @@@ try
  } catch (\TypeError $e) {
      echo $e->getMessage(), \PHP_EOL;
  }
+
+ /* standard_conforming_strings = 1 */
+ pg_query($db, "SET standard_conforming_strings = 1");
+ var_dump(pg_convert($db, $table_name, ['str' => "\\' OR 1=1"]));
  ?>
 +--CLEAN--
 +<?php
 +include('inc/config.inc');
 +$table_name = "table_10pg_convert_9";
 +
 +$db = pg_connect($conn_str);
 +pg_query($db, "DROP TABLE IF EXISTS {$table_name}");
 +?>
  --EXPECT--
  array(3) {
    [""num""]=>
diff --cc ext/pgsql/tests/12pg_insert_9.phpt
index b986d14e15d,699426c89b2..7782c93ef7a
--- a/ext/pgsql/tests/12pg_insert_9.phpt
+++ b/ext/pgsql/tests/12pg_insert_9.phpt
@@@ -56,17 -53,9 +56,17 @@@ try

  echo "Ok\n";
  ?>
 +--CLEAN--
 +<?php
 +include('inc/config.inc');
 +$table_name = "table_12pg_insert_9";
 +
 +$db = pg_connect($conn_str);
 +pg_query($db, "DROP TABLE IF EXISTS {$table_name}");
 +?>
  --EXPECTF--
- INSERT INTO "table_12pg_insert_9" ("num","str","bin") VALUES (1234,E'AAA',E'\\x424242');
 -INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES (1234,'AAA','\\x424242');
 -INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES ('1234','AAA','BBB');
++INSERT INTO "table_12pg_insert_9" ("num","str","bin") VALUES (1234,'AAA','\\x424242');
 +INSERT INTO "table_12pg_insert_9" ("num","str","bin") VALUES ('1234','AAA','BBB');
  object(PgSql\Result)#%d (0) {
  }
  Array of values must be an associative array with string keys
diff --cc ext/pgsql/tests/14pg_update_9.phpt
index 602019c2fb6,64493075490..6bcea512150
--- a/ext/pgsql/tests/14pg_update_9.phpt
+++ b/ext/pgsql/tests/14pg_update_9.phpt
@@@ -30,15 -25,7 +30,15 @@@ echo pg_update($db, $table_name, $field

  echo "Ok\n";
  ?>
 +--CLEAN--
 +<?php
 +include('inc/config.inc');
 +$table_name = "table_14pg_update_9";
 +
 +$db = pg_connect($conn_str);
 +pg_query($db, "DROP TABLE IF EXISTS {$table_name}");
 +?>
  --EXPECT--
- UPDATE "table_14pg_update_9" SET "num"=1234,"str"=E'ABC',"bin"=E'\\x58595a' WHERE "num"=1234;
 -UPDATE "php_pgsql_test" SET "num"=1234,"str"='ABC',"bin"='\\x58595a' WHERE "num"=1234;
 -UPDATE "php_pgsql_test" SET "num"='1234',"str"='ABC',"bin"='XYZ' WHERE "num"='1234';
++UPDATE "table_14pg_update_9" SET "num"=1234,"str"='ABC',"bin"='\\x58595a' WHERE "num"=1234;
 +UPDATE "table_14pg_update_9" SET "num"='1234',"str"='ABC',"bin"='XYZ' WHERE "num"='1234';
  Ok
diff --cc ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt
index 00000000000,88b37a8432f..93f6376bb1c
mode 000000,100644..100644
--- a/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt
+++ b/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt
@@@ -1,0 -1,58 +1,58 @@@
+ --TEST--
+ GHSA-7qpv-r5mr-78m4: SQL injection via E'...' backslash breakout
+ --CREDITS--
+ expatch.llc
+ --EXTENSIONS--
+ pgsql
+ --SKIPIF--
 -<?php include("skipif.inc"); ?>
++<?php include("inc/skipif.inc"); ?>
+ --FILE--
+ <?php
 -include 'config.inc';
++include 'inc/config.inc';
+
+ $db = pg_connect($conn_str);
+
+ pg_query($db, "SET standard_conforming_strings = 1");
+
+ pg_query($db, "DROP TABLE IF EXISTS ghsa_7qpv_r5mr_78m4");
+ pg_query($db, "CREATE TABLE ghsa_7qpv_r5mr_78m4 (id serial primary key, name text, admin boolean)");
+ pg_query($db, "INSERT INTO ghsa_7qpv_r5mr_78m4 (name, admin) VALUES ('alice', false), ('bob', false)");
+
+ $params = ['name' => "zzz' OR 1=1 --"];
+ echo pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
+ printf("returned: %d\n\n", count(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)));
+
+ $params = ['name' => "zzz\\' OR 1=1 --"];
+ echo pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
+ printf("returned: %d\n\n", count(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)));
+
+ $params = ['name' => "john\\', true) --", 'admin' => 'false'];
+ echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
+ pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params);
+ var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 3])[0]['admin']);
+ echo "\n";
+
+ $params = ['name' => "jake\\', true) --", 'admin' => 'f'];
+ echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_ESCAPE|PGSQL_DML_STRING) . "\n";
+ pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_EXEC|PGSQL_DML_ESCAPE);
+ var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 4])[0]['admin']);
+
+ ?>
+ --EXPECT--
+ SELECT * FROM "ghsa_7qpv_r5mr_78m4" WHERE "name"='zzz'' OR 1=1 --';
+ returned: 0
+
+ SELECT * FROM "ghsa_7qpv_r5mr_78m4" WHERE "name"='zzz\'' OR 1=1 --';
+ returned: 0
+
+ INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('john\'', true) --','f');
+ string(1) "f"
+
+ INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('jake\'', true) --','f');
+ string(1) "f"
+ --CLEAN--
+ <?php
 -include('config.inc');
++include('inc/config.inc');
+ $db = pg_connect($conn_str);
+ pg_query($db, "DROP TABLE IF EXISTS ghsa_7qpv_r5mr_78m4");
+ ?>
diff --cc ext/pgsql/tests/bug68638.phpt
index 957e9a33abf,2a1025cad8d..267423a2226
--- a/ext/pgsql/tests/bug68638.phpt
+++ b/ext/pgsql/tests/bug68638.phpt
@@@ -30,18 -30,11 +30,18 @@@ while ($row = pg_fetch_assoc($rs))
          var_dump($row);
  }

 -pg_query($conn, "DROP TABLE $table");

 +?>
 +--CLEAN--
 +<?php
 +require_once('inc/config.inc');
 +$conn = pg_connect($conn_str);
 +$table='test_68638';
 +
 +pg_query($conn, "DROP TABLE IF EXISTS $table");
  ?>
  --EXPECT--
- string(52) "UPDATE "test_68638" SET "value"=E'inf' WHERE "id"=1;"
+ string(51) "UPDATE "test_68638" SET "value"='inf' WHERE "id"=1;"
  array(2) {
    ["id"]=>
    string(1) "1"
diff --cc ext/phar/util.c
index 2c896c6f658,4df7060c1d3..d3bdf3d52a7
--- a/ext/phar/util.c
+++ b/ext/phar/util.c
@@@ -91,18 -109,8 +112,16 @@@ phar_entry_info *phar_get_link_source(p
  			return NULL;
  		}
  	}
- 	return entry;
  }
- /* }}} */

 +static php_stream *phar_get_entrypufp(const phar_entry_info *entry)
 +{
 +	if (!entry->is_persistent) {
 +		return entry->phar->ufp;
 +	}
 +	return PHAR_G(cached_fp)[entry->phar->phar_pos].ufp;
 +}
 +
  /* retrieve a phar_entry_info's current file pointer for reading contents */
  php_stream *phar_get_efp(phar_entry_info *entry, int follow_links) /* {{{ */
  {