Skip to content

Commit dd5274e

Browse files
divinity76Niels Dossche
and
Niels Dossche
committed
Use x'hex' syntax for null bytes in pdo::quote
Implements x'hex' encoding in pdo::quote for handling strings with null bytes, providing a reliable workaround for issue GH-13952. An alternative fix is discussed in PR #13956 PR #13956 does something interesting, it avoids the overhead of copying to/from sqlite3_snprintf, probably speeding up PDO::quote, but right now I just want to keep things simple. Co-authored-by: Niels Dossche <[email protected]>
1 parent 565fe3a commit dd5274e

File tree

2 files changed

+22
-15
lines changed

2 files changed

+22
-15
lines changed

ext/pdo_sqlite/sqlite_driver.c

+7-6
Original file line numberDiff line numberDiff line change
@@ -227,20 +227,21 @@ static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unqu
227227
return NULL;
228228
}
229229
if(ZSTR_LEN(unquoted) != 0 && memchr(ZSTR_VAL(unquoted), '\0', ZSTR_LEN(unquoted))) {
230-
// x'hex'
231-
zend_string *quoted = zend_string_safe_alloc(3 + (2 * ZSTR_LEN(unquoted)), 1, 0, 0);
230+
// (''||x'hex')
231+
// the odd (''||) thing is to make sure quote produce a sqlite datatype "string" rather than "blob" ...
232+
// https://github.com/php/php-src/pull/13962/files#r1565485792
233+
zend_string *quoted = zend_string_safe_alloc(9 + (2 * ZSTR_LEN(unquoted)), 1, 0, 0);
232234
char *outptr = ZSTR_VAL(quoted);
233235
const char *inptr = ZSTR_VAL(unquoted);
234236
const char *const inendptr = inptr + ZSTR_LEN(unquoted);
235-
*outptr++ = 'x';
236-
*outptr++ = '\'';
237+
memcpy(outptr, "(''||x'", 7);
238+
outptr += 7;
237239
while(inptr != inendptr) {
238240
const unsigned char c = *inptr++;
239241
*outptr++ = "0123456789ABCDEF"[c >> 4];
240242
*outptr++ = "0123456789ABCDEF"[c & 0x0F];
241243
}
242-
*outptr++ = '\'';
243-
*outptr = '\0'; // todo: does zend_string_safe_alloc do this for us? i don't know
244+
memcpy(outptr, "')", 3); // todo: does zend_string_safe_alloc write the null terminator? if it does, reduce this to 2
244245
return quoted;
245246
}
246247
quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);

ext/pdo_sqlite/tests/gh13952.phpt

+15-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ $test_cases = [
2525
"\x00foo\x00\x00\x00bar\x00",
2626
"\x00\x00\x00foo",
2727
"foo\x00\x00\x00",
28+
"\x80", // << invalid UTF8
29+
"\x00\x80\x00", // << invalid UTF8
2830
];
2931

3032
foreach($test_cases as $test){
@@ -38,7 +40,7 @@ $db->exec('CREATE TABLE test (name TEXT)');
3840

3941
foreach ($test_cases as $test_case) {
4042
$quoted = $db->quote($test_case);
41-
echo trim(json_encode($test_case), '"'), " -> $quoted\n";
43+
echo trim(json_encode($test_case, JSON_PARTIAL_OUTPUT_ON_ERROR), '"'), " -> $quoted\n";
4244
$db->exec("INSERT INTO test (name) VALUES (" . $quoted . ")");
4345
}
4446

@@ -51,17 +53,19 @@ foreach ($stmt->fetchAll() as $result) {
5153
--EXPECTF--
5254
-> ''
5355
x -> 'x'
54-
\u0000 -> x'00'
55-
a\u0000b -> x'610062'
56-
\u0000\u0000\u0000 -> x'000000'
56+
\u0000 -> (''||x'00')
57+
a\u0000b -> (''||x'610062')
58+
\u0000\u0000\u0000 -> (''||x'000000')
5759
foobar -> 'foobar'
5860
foo'''bar -> 'foo''''''bar'
5961
'foo'''bar' -> '''foo''''''bar'''
60-
'foo'\u0000'bar' -> x'27666F6F27002762617227'
61-
foo\u0000\u0000\u0000bar -> x'666F6F000000626172'
62-
\u0000foo\u0000\u0000\u0000bar\u0000 -> x'00666F6F00000062617200'
63-
\u0000\u0000\u0000foo -> x'000000666F6F'
64-
foo\u0000\u0000\u0000 -> x'666F6F000000'
62+
'foo'\u0000'bar' -> (''||x'27666F6F27002762617227')
63+
foo\u0000\u0000\u0000bar -> (''||x'666F6F000000626172')
64+
\u0000foo\u0000\u0000\u0000bar\u0000 -> (''||x'00666F6F00000062617200')
65+
\u0000\u0000\u0000foo -> (''||x'000000666F6F')
66+
foo\u0000\u0000\u0000 -> (''||x'666F6F000000')
67+
null -> '€'
68+
null -> (''||x'008000')
6569
string(0) ""
6670
string(1) "x"
6771
string(1) "%0"
@@ -75,3 +79,5 @@ string(9) "foo%0%0%0bar"
7579
string(11) "%0foo%0%0%0bar%0"
7680
string(6) "%0%0%0foo"
7781
string(6) "foo%0%0%0"
82+
string(1) "€"
83+
string(3) "%0€%0"

0 commit comments

Comments
 (0)