From 70a550884bc0f208e935e8fbe5b50ca1e9bc9214 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Thu, 16 Jul 2026 13:01:33 -0400 Subject: [PATCH] ext/sqlite3: reject NUL bytes in SQLite3::escapeString() 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 --- ext/sqlite3/sqlite3.c | 5 +++ .../tests/gh_sqlite3_escapestring_nul.phpt | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 ext/sqlite3/tests/gh_sqlite3_escapestring_nul.phpt diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 67ff850cadd0..24e84e2e3d6e 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -473,6 +473,11 @@ PHP_METHOD(SQLite3, escapeString) RETURN_THROWS(); } + if (UNEXPECTED(zend_str_has_nul_byte(sql))) { + zend_argument_value_error(1, "must not contain null bytes"); + RETURN_THROWS(); + } + if (ZSTR_LEN(sql)) { ret = sqlite3_mprintf("%q", ZSTR_VAL(sql)); if (ret) { 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..089ac7d54a60 --- /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-- +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 null bytes +ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain null bytes +ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain null bytes +ValueError: SQLite3::escapeString(): Argument #1 ($string) must not contain null bytes +ok: ok +quote: test''''% +empty: ''