[php-src] master: ext/sqlite3: reject NUL bytes in SQLite3::escapeString() (#22774)

Ilia Alshanetsky via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Committer: GitHub (web-flow)
Pusher: iliaal
Date: 2026-07-17T07:09:41-04:00

Commit: https://github.com/php/php-src/commit/7522c496300417d7cfbbf6a3cac48651e4574f3b
Raw diff: https://github.com/php/php-src/commit/7522c496300417d7cfbbf6a3cac48651e4574f3b.diff

ext/sqlite3: reject NUL bytes in SQLite3::escapeString() (#22774)

escapeString() used sqlite3_mprintf("%q"), which stops at the first C NUL.
PHP strings are length-aware and may embed NULs, so values such as "a\0b"
were silently truncated to "a" when interpolated into SQL. Prepared binds
pass an explicit length and are unaffected.

ext/pdo_sqlite rejected the same inputs for PDO::quote in 0a10f6db268
(GH-13952), on 8.5 and up. That check consults the connection's error mode;
escapeString() is static and has no connection, so it throws instead.

Closes GH-22774

Changed paths:
  A  ext/sqlite3/tests/gh_sqlite3_escapestring_nul.phpt
  M  ext/sqlite3/sqlite3.c


Diff:

diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c
index 67ff850cadd0..b81a0677a6c2 100644
--- a/ext/sqlite3/sqlite3.c
+++ b/ext/sqlite3/sqlite3.c
@@ -469,7 +469,7 @@ PHP_METHOD(SQLite3, escapeString)
 	zend_string *sql;
 	char *ret;
 
-	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "S", &sql)) {
+	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "P", &sql)) {
 		RETURN_THROWS();
 	}
 
diff --git a/ext/sqlite3/tests/gh_sqlite3_escapestring_nul.phpt b/ext/sqlite3/tests/gh_sqlite3_escapestring_nul.phpt
new file mode 100644
index 000000000000..49d66a1accf5
--- /dev/null
+++ b/ext/sqlite3/tests/gh_sqlite3_escapestring_nul.phpt
@@ -0,0 +1,35 @@
+--TEST--
+SQLite3::escapeString() rejects strings with embedded NUL bytes
+--EXTENSIONS--
+sqlite3
+--FILE--
+<?php
+
+$cases = [
+    "\0",
+    "a\0b",
+    "secret\0x",
+    "foo\0\0bar",
+];
+
+foreach ($cases as $in) {
+    try {
+        SQLite3::escapeString($in);
+        echo "FAIL: no exception for ", bin2hex($in), "\n";
+    } catch (ValueError $e) {
+        echo "ValueError: ", $e->getMessage(), "\n";
+    }
+}
+
+echo "ok: ", SQLite3::escapeString("ok"), "\n";
+echo "quote: ", SQLite3::escapeString("test''%"), "\n";
+echo "empty: ", var_export(SQLite3::escapeString(""), true), "\n";
+?>
+--EXPECT--
+ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain any null bytes
+ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain any null bytes
+ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain any null bytes
+ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain any null bytes
+ok: ok
+quote: test''''%
+empty: ''
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.