From cb07d57234c8b6e43b85c4ad342ad16fd0648ecb Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 16 Jan 2023 11:03:32 -0400 Subject: [PATCH 1/2] PDO_DBLIB: Quote LOB arguments as binary literals If the user is specifying a PDO::PARAM_LOB, there's a good chance that it's a binary file (i.e. an image), that is too fragile to survive in a string, as escaping it is insufficient. Because PDO_DBLIB relies on PDO to fill in a parameterized query and submits the filled string to the server, it ends up mangling the query. This adds logic in the PDO_DBLIB quoter to handle binary parameters by using the SQL Server binary literal syntax (a hex string that begins with `0x`). This resolves the issue because PDO consults the quoter for filling in the query string. --- ext/pdo_dblib/dblib_driver.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 02ec2466a05fb..956b5a27c55b4 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -145,7 +145,7 @@ static zend_long dblib_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) static zend_string* dblib_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; - bool use_national_character_set = 0; + bool use_national_character_set = 0, is_binary = false; size_t i; char *q; size_t quotedlen = 0; @@ -160,6 +160,29 @@ static zend_string* dblib_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo if ((paramtype & PDO_PARAM_STR_CHAR) == PDO_PARAM_STR_CHAR) { use_national_character_set = 0; } + /* + * A user could be passing a binary (i.e. an image file) in a query. + * It's fragile trying to escape it as a string, so encode it as a + * binary literal instead. + */ + if (paramtype == PDO_PARAM_LOB) { + is_binary = true; + } + + if (is_binary) { + /* 1 char = 2 chars in hex, plus 0x */ + quotedlen = ZSTR_LEN(unquoted) * 2; /* XXX: Overflow? */ + quotedlen += 2; + + quoted_str = zend_string_alloc(quotedlen, 0); + q = ZSTR_VAL(quoted_str); + *q++ = '0'; + *q++ = 'x'; + for (i = 0; i < ZSTR_LEN(unquoted); i++) { + q += sprintf(q, "%02X", (unsigned char)ZSTR_VAL(unquoted)[i]); + } + return quoted_str; + } /* Detect quoted length, adding extra char for doubled single quotes */ for (i = 0; i < ZSTR_LEN(unquoted); i++) { From 1d2062b15312355880488f59239eddb435e23e73 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 16 Jan 2023 11:41:18 -0400 Subject: [PATCH 2/2] PDO_DBLIB: Test binary literal support This makes sure that a PDO::PARAM_LOB parameter gets converted to a binary literal when doing the substitution for the query. --- ext/pdo_dblib/tests/pdo_dblib_binary.phpt | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 ext/pdo_dblib/tests/pdo_dblib_binary.phpt diff --git a/ext/pdo_dblib/tests/pdo_dblib_binary.phpt b/ext/pdo_dblib/tests/pdo_dblib_binary.phpt new file mode 100644 index 0000000000000..6614bfc075371 --- /dev/null +++ b/ext/pdo_dblib/tests/pdo_dblib_binary.phpt @@ -0,0 +1,46 @@ +--TEST-- +PDO_DBLIB: Ensure binary bound parameter is a binary literal +--EXTENSIONS-- +pdo_dblib +--SKIPIF-- + +--FILE-- +prepare($query); +// PARAM_LOB gets converted to a binary literal instead of a string literal +$stmt->bindParam(1, $binary, PDO::PARAM_LOB); +$result = $stmt->execute(); + +// Verify we sent the binary literal over the wire +var_dump($stmt->debugDumpParams()); + +// Verify that we get the same PNG back over the wire +$rows = $stmt->fetchAll(); +var_dump(base64_encode($rows[0][0])); + +?> +--EXPECT-- +SQL: [8] SELECT ? +Sent SQL: [149] SELECT 0x89504E470D0A1A0A0000000D49484452000000010000000108060000001F15C4890000000D49444154085B63606060F80F0001040100C12D8E500000000049454E44AE426082 +Params: 1 +Key: Position #0: +paramno=0 +name=[0] "" +is_param=1 +param_type=3 +NULL +string(96) "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg=="