Commit eaac0378e3c for php.net

commit eaac0378e3c2e16814f9aeef55a07c2418d6d27a
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date:   Sat Aug 1 23:12:12 2026 +0200

    sqlite3: Fix leak when trying to close db if blob stream is still open

    Discovered coincidentally with ESSS [1], even though I was actually using
    it for something else.

    [1] https://github.com/csl-ugent/ESSS

    Closes GH-22992.

diff --git a/NEWS b/NEWS
index ee333ae9f20..6738d104f5b 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,10 @@ PHP                                                                        NEWS
   . Fixed bug GH-21036 (mb_ereg_search_getregs() crashes after mb_eregi()
     invalidates the regex cache). (Matthias Goergens)

+- Opcache:
+  . Fixed bug GH-22857 (Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on a
+    property hook getter, losing register-held variables). (Zhao Hao)
+
 - PCRE:
   . Fixed bug GH-21134 (Crash with \C + UTF-8). Using \C in UTF-8 patterns is
     now forbidden. (Arnaud)
@@ -29,6 +33,9 @@ PHP                                                                        NEWS
 - Sockets:
   . Fixed various memory related issues in ext/sockets. (David Carlier)

+- SQLite:
+  . Fix leak when trying to close db if blob stream is still open. (ndossche)
+
 - Streams:
   . Fixed bug GH-15836 (Use-after-free when a user stream filter accesses
     $this->stream during the close flush). (iliaal)
@@ -37,10 +44,6 @@ PHP                                                                        NEWS
   . Fixed out-of-bounds write when shm_attach() opens an existing segment with
     a size larger than the segment actually is. (David Carlier)

-- Opcache:
-  . Fixed bug GH-22857 (Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on a
-    property hook getter, losing register-held variables). (Zhao Hao)
-
 30 Jul 2026, PHP 8.4.24

 - BCMath:
diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c
index 1bb93e0e267..20714c38aad 100644
--- a/ext/sqlite3/sqlite3.c
+++ b/ext/sqlite3/sqlite3.c
@@ -2255,7 +2255,9 @@ static void php_sqlite3_object_free_storage(zend_object *object) /* {{{ */
 	}

 	if (intern->initialised && intern->db) {
-		sqlite3_close(intern->db);
+		/* Use sqlite3_close_v2() because the object may be destroyed while resources depending on the connection are still alive,
+		 * e.g. a blob stream created by SQLite3::openBlob(). */
+		sqlite3_close_v2(intern->db);
 		intern->initialised = 0;
 	}

diff --git a/ext/sqlite3/tests/sqlite3_close_blob_stream.phpt b/ext/sqlite3/tests/sqlite3_close_blob_stream.phpt
new file mode 100644
index 00000000000..d2426445a7c
--- /dev/null
+++ b/ext/sqlite3/tests/sqlite3_close_blob_stream.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Destroying the SQLite3 object while a blob stream is still open must not leak the connection
+--EXTENSIONS--
+sqlite3
+--FILE--
+<?php
+
+$db = new SQLite3(':memory:');
+$db->exec('CREATE TABLE test (data BLOB)');
+$db->exec("INSERT INTO test (data) VALUES (x'34323432')");
+
+$stream = $db->openBlob('test', 'data', 1);
+var_dump($db->close());
+unset($db);
+var_dump(fread($stream, 4));
+fclose($stream);
+
+?>
+--EXPECTF--
+Warning: SQLite3::close(): Unable to close database: %s in %s on line %d
+bool(false)
+string(4) "4242"