Refactor PDO's quoter handler to return a zend_string

Closes GH-6547
This commit is contained in:
George Peter Banyard 2020-12-23 15:58:10 +01:00
parent df0fa5b178
commit 63cda0fea8
10 changed files with 124 additions and 109 deletions

View file

@ -47,8 +47,12 @@ PHP 8.1 INTERNALS UPGRADE NOTES
getColumnMeta(). The type provided here does not need to match the type getColumnMeta(). The type provided here does not need to match the type
returned by get_col (in fact no corresponding type might exist, e.g. for returned by get_col (in fact no corresponding type might exist, e.g. for
floats). It should be the closest logical equivalent for the column type. floats). It should be the closest logical equivalent for the column type.
- The transaction, set_attribute, quoter, and preparer handler's return type - The transaction, set_attribute, and preparer handler's return type
has been formalized to bool instead of int. has been formalized to bool instead of int.
- The check_liveness handler's return type has been formalized to zend_return - The check_liveness handler's return type has been formalized to zend_return
instead of int. instead of int.
- The closer, and fetch_error handlers have been voidified. - The closer, and fetch_error handlers have been voidified.
- The quoter handler now returns the quoted string as zend_string* instead
of returning a boolean, and the quoted string as a pair of out params.
Similarly the unquoted string is now a zend_string* instead of a pair of
char* and size_t length.

View file

@ -1119,18 +1119,17 @@ PHP_METHOD(PDO, query)
} }
/* }}} */ /* }}} */
/* {{{ quotes string for use in a query. The optional paramtype acts as a hint for drivers that have alternate quoting styles. The default value is PDO_PARAM_STR */ /* {{{ quotes string for use in a query.
* The optional paramtype acts as a hint for drivers that have alternate quoting styles.
* The default value is PDO_PARAM_STR */
PHP_METHOD(PDO, quote) PHP_METHOD(PDO, quote)
{ {
pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS); pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
char *str; zend_string *str;
size_t str_len;
zend_long paramtype = PDO_PARAM_STR; zend_long paramtype = PDO_PARAM_STR;
char *qstr;
size_t qlen;
ZEND_PARSE_PARAMETERS_START(1, 2) ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STRING(str, str_len) Z_PARAM_STR(str)
Z_PARAM_OPTIONAL Z_PARAM_OPTIONAL
Z_PARAM_LONG(paramtype) Z_PARAM_LONG(paramtype)
ZEND_PARSE_PARAMETERS_END(); ZEND_PARSE_PARAMETERS_END();
@ -1143,13 +1142,7 @@ PHP_METHOD(PDO, quote)
RETURN_FALSE; RETURN_FALSE;
} }
if (dbh->methods->quoter(dbh, str, str_len, &qstr, &qlen, paramtype)) { RETURN_STR(dbh->methods->quoter(dbh, str, paramtype));
RETVAL_STRINGL(qstr, qlen);
efree(qstr);
return;
}
PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
} }
/* }}} */ /* }}} */

View file

@ -235,21 +235,18 @@ safe:
php_stream_from_zval_no_verify(stm, parameter); php_stream_from_zval_no_verify(stm, parameter);
if (stm) { if (stm) {
zend_string *buf; zend_string *buf;
zend_string *quoted_buf;
buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
if (!buf) { if (!buf) {
buf = ZSTR_EMPTY_ALLOC(); buf = ZSTR_EMPTY_ALLOC();
} }
if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
param->param_type)) { quoted_buf = stmt->dbh->methods->quoter(stmt->dbh, buf, param->param_type);
/* bork */ plc->quoted = estrndup(ZSTR_VAL(quoted_buf), ZSTR_LEN(quoted_buf));
ret = -1; plc->qlen = ZSTR_LEN(quoted_buf);
strncpy(stmt->error_code, stmt->dbh->error_code, 6); zend_string_release_ex(quoted_buf, 0);
if (buf) {
zend_string_release_ex(buf, 0);
}
goto clean_up;
}
if (buf) { if (buf) {
zend_string_release_ex(buf, 0); zend_string_release_ex(buf, 0);
} }
@ -289,21 +286,29 @@ safe:
plc->freeq = 0; plc->freeq = 0;
break; break;
default: default: {
zend_string *quoted_buf;
/* TODO Should this be zval_try_get_string_func() ? */
buf = zval_get_string(parameter); buf = zval_get_string(parameter);
if (EG(exception) || /* TODO Check when this can occur? */
!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), if (EG(exception)) {
ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
param_type)) {
/* bork */ /* bork */
ret = -1; ret = -1;
strncpy(stmt->error_code, stmt->dbh->error_code, 6); strncpy(stmt->error_code, stmt->dbh->error_code, 6);
/* TODO Is this dead code now? */
if (buf) { if (buf) {
zend_string_release_ex(buf, 0); zend_string_release_ex(buf, 0);
} }
goto clean_up; goto clean_up;
} }
quoted_buf = stmt->dbh->methods->quoter(stmt->dbh, buf, param_type);
plc->quoted = estrndup(ZSTR_VAL(quoted_buf), ZSTR_LEN(quoted_buf));
plc->qlen = ZSTR_LEN(quoted_buf);
plc->freeq = 1; plc->freeq = 1;
zend_string_release_ex(quoted_buf, 0);
}
} }
if (buf) { if (buf) {

View file

@ -236,7 +236,7 @@ typedef bool (*pdo_dbh_prepare_func)(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_
typedef zend_long (*pdo_dbh_do_func)(pdo_dbh_t *dbh, const char *sql, size_t sql_len); typedef zend_long (*pdo_dbh_do_func)(pdo_dbh_t *dbh, const char *sql, size_t sql_len);
/* quote a string */ /* quote a string */
typedef bool (*pdo_dbh_quote_func)(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype); typedef zend_string* (*pdo_dbh_quote_func)(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype);
/* transaction related (beingTransaction(), commit, rollBack, inTransaction) /* transaction related (beingTransaction(), commit, rollBack, inTransaction)
* Return true if currently inside a transaction, false otherwise. */ * Return true if currently inside a transaction, false otherwise. */

View file

@ -142,14 +142,14 @@ static zend_long dblib_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_l
return DBCOUNT(H->link); return DBCOUNT(H->link);
} }
static bool dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) 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; pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
zend_bool use_national_character_set = 0; zend_bool use_national_character_set = 0;
size_t i; size_t i;
char *q; char *q;
*quotedlen = 0; size_t quotedlen = 0;
zend_string *quoted_str;
if (H->assume_national_character_set_strings) { if (H->assume_national_character_set_strings) {
use_national_character_set = 1; use_national_character_set = 1;
@ -162,34 +162,34 @@ static bool dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unq
} }
/* Detect quoted length, adding extra char for doubled single quotes */ /* Detect quoted length, adding extra char for doubled single quotes */
for (i = 0; i < unquotedlen; i++) { for (i = 0; i < ZSTR_LEN(unquoted); i++) {
if (unquoted[i] == '\'') ++*quotedlen; if (ZSTR_VAL(unquoted)[i] == '\'') ++quotedlen;
++*quotedlen; ++quotedlen;
} }
*quotedlen += 2; /* +2 for opening, closing quotes */ quotedlen += 2; /* +2 for opening, closing quotes */
if (use_national_character_set) { if (use_national_character_set) {
++*quotedlen; /* N prefix */ ++quotedlen; /* N prefix */
} }
q = *quoted = emalloc(*quotedlen + 1); /* Add byte for terminal null */ quoted_str = zend_string_alloc(quotedlen, 0);
q = ZSTR_VAL(quoted_str);
if (use_national_character_set) { if (use_national_character_set) {
*q++ = 'N'; *q++ = 'N';
} }
*q++ = '\''; *q++ = '\'';
for (i = 0; i < unquotedlen; i++) { for (i = 0; i < ZSTR_LEN(unquoted); i++) {
if (unquoted[i] == '\'') { if (ZSTR_VAL(unquoted)[i] == '\'') {
*q++ = '\''; *q++ = '\'';
*q++ = '\''; *q++ = '\'';
} else { } else {
*q++ = unquoted[i]; *q++ = ZSTR_VAL(unquoted)[i];
} }
} }
*q++ = '\''; *q++ = '\'';
*q = '\0';
*q = 0; return quoted_str;
return true;
} }
static bool pdo_dblib_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) static bool pdo_dblib_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)

View file

@ -649,30 +649,29 @@ free_statement:
/* }}} */ /* }}} */
/* called by the PDO SQL parser to add quotes to values that are copied into SQL */ /* called by the PDO SQL parser to add quotes to values that are copied into SQL */
static bool firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, /* {{{ */ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
char **quoted, size_t *quotedlen, enum pdo_param_type paramtype)
{ {
int qcount = 0; int qcount = 0;
char const *co, *l, *r; char const *co, *l, *r;
char *c; char *c;
size_t quotedlen;
zend_string *quoted_str;
if (!unquotedlen) { if (ZSTR_LEN(unquoted) == 0) {
*quotedlen = 2; return zend_string_init("''", 2, 0);
*quoted = emalloc(*quotedlen+1);
strcpy(*quoted, "''");
return true;
} }
/* Firebird only requires single quotes to be doubled if string lengths are used */ /* Firebird only requires single quotes to be doubled if string lengths are used */
/* count the number of ' characters */ /* count the number of ' characters */
for (co = unquoted; (co = strchr(co,'\'')); qcount++, co++); for (co = ZSTR_VAL(unquoted); (co = strchr(co,'\'')); qcount++, co++);
*quotedlen = unquotedlen + qcount + 2; quotedlen = ZSTR_LEN(unquoted) + qcount + 2;
*quoted = c = emalloc(*quotedlen+1); quoted_str = zend_string_alloc(quotedlen, 0);
c = ZSTR_VAL(quoted_str);
*c++ = '\''; *c++ = '\'';
/* foreach (chunk that ends in a quote) */ /* foreach (chunk that ends in a quote) */
for (l = unquoted; (r = strchr(l,'\'')); l = r+1) { for (l = ZSTR_VAL(unquoted); (r = strchr(l,'\'')); l = r+1) {
strncpy(c, l, r-l+1); strncpy(c, l, r-l+1);
c += (r-l+1); c += (r-l+1);
/* add the second quote */ /* add the second quote */
@ -680,11 +679,11 @@ static bool firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t
} }
/* copy the remainder */ /* copy the remainder */
strncpy(c, l, *quotedlen-(c-*quoted)-1); strncpy(c, l, quotedlen-(c-ZSTR_VAL(quoted_str))-1);
(*quoted)[*quotedlen-1] = '\''; ZSTR_VAL(quoted_str)[quotedlen-1] = '\'';
(*quoted)[*quotedlen] = '\0'; ZSTR_VAL(quoted_str)[quotedlen] = '\0';
return true; return quoted_str;
} }
/* }}} */ /* }}} */

View file

@ -301,10 +301,13 @@ static char *pdo_mysql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *
#endif #endif
/* {{{ mysql_handle_quoter */ /* {{{ mysql_handle_quoter */
static bool mysql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) static zend_string* mysql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype )
{ {
pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data;
zend_bool use_national_character_set = 0; zend_bool use_national_character_set = 0;
char *quoted;
size_t quotedlen;
zend_string *quoted_str;
if (H->assume_national_character_set_strings) { if (H->assume_national_character_set_strings) {
use_national_character_set = 1; use_national_character_set = 1;
@ -318,24 +321,27 @@ static bool mysql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unq
PDO_DBG_ENTER("mysql_handle_quoter"); PDO_DBG_ENTER("mysql_handle_quoter");
PDO_DBG_INF_FMT("dbh=%p", dbh); PDO_DBG_INF_FMT("dbh=%p", dbh);
PDO_DBG_INF_FMT("unquoted=%.*s", (int)unquotedlen, unquoted); PDO_DBG_INF_FMT("unquoted=%.*s", (int)ZSTR_LEN(unquoted), ZSTR_VAL(unquoted));
*quoted = safe_emalloc(2, unquotedlen, 3 + (use_national_character_set ? 1 : 0)); quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3 + (use_national_character_set ? 1 : 0));
if (use_national_character_set) { if (use_national_character_set) {
*quotedlen = mysql_real_escape_string_quote(H->server, *quoted + 2, unquoted, unquotedlen, '\''); quotedlen = mysql_real_escape_string_quote(H->server, quoted + 2, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), '\'');
(*quoted)[0] = 'N'; quoted[0] = 'N';
(*quoted)[1] = '\''; quoted[1] = '\'';
++*quotedlen; /* N prefix */ ++quotedlen; /* N prefix */
} else { } else {
*quotedlen = mysql_real_escape_string_quote(H->server, *quoted + 1, unquoted, unquotedlen, '\''); quotedlen = mysql_real_escape_string_quote(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), '\'');
(*quoted)[0] = '\''; quoted[0] = '\'';
} }
(*quoted)[++*quotedlen] = '\''; quoted[++quotedlen] = '\'';
(*quoted)[++*quotedlen] = '\0'; quoted[++quotedlen] = '\0';
PDO_DBG_INF_FMT("quoted=%.*s", (int)*quotedlen, *quoted); PDO_DBG_INF_FMT("quoted=%.*s", (int)quotedlen, quoted);
PDO_DBG_RETURN(true);
quoted_str = zend_string_init(quoted, quotedlen, 0);
efree(quoted);
PDO_DBG_RETURN(quoted_str);
} }
/* }}} */ /* }}} */

View file

@ -350,40 +350,40 @@ static zend_long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len
} }
/* }}} */ /* }}} */
static bool oci_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) /* {{{ */ static zend_string* oci_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype ) /* {{{ */
{ {
int qcount = 0; int qcount = 0;
char const *cu, *l, *r; char const *cu, *l, *r;
char *c; char *c, *quoted;
zend_string *quoted_str;
if (!unquotedlen) { if (ZSTR_LEN(unquoted) == 0) {
*quotedlen = 2; return zend_string_init("''", 2, 0);
*quoted = emalloc(*quotedlen+1);
strcpy(*quoted, "''");
return true;
} }
/* count single quotes */ /* count single quotes */
for (cu = unquoted; (cu = strchr(cu,'\'')); qcount++, cu++) for (cu = ZSTR_VAL(unquoted); (cu = strchr(cu,'\'')); qcount++, cu++)
; /* empty loop */ ; /* empty loop */
*quotedlen = unquotedlen + qcount + 2; quotedlen = ZSTR_LEN(unquoted) + qcount + 2;
*quoted = c = emalloc(*quotedlen+1); quoted = c = emalloc(quotedlen+1);
*c++ = '\''; *c++ = '\'';
/* foreach (chunk that ends in a quote) */ /* foreach (chunk that ends in a quote) */
for (l = unquoted; (r = strchr(l,'\'')); l = r+1) { for (l = ZSTR_VAL(unquoted); (r = strchr(l,'\'')); l = r+1) {
strncpy(c, l, r-l+1); strncpy(c, l, r-l+1);
c += (r-l+1); c += (r-l+1);
*c++ = '\''; /* add second quote */ *c++ = '\''; /* add second quote */
} }
/* Copy remainder and add enclosing quote */ /* Copy remainder and add enclosing quote */
strncpy(c, l, *quotedlen-(c-*quoted)-1); strncpy(c, l, quotedlen-(c-quoted)-1);
(*quoted)[*quotedlen-1] = '\''; quoted[quotedlen-1] = '\'';
(*quoted)[*quotedlen] = '\0'; quoted[quotedlen] = '\0';
return true; quoted_str = zend_string_init(quoted, quotedlen, 0);
efree(quoted);
return quoted_str;
} }
/* }}} */ /* }}} */

View file

@ -317,33 +317,39 @@ static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_l
return ret; return ret;
} }
static bool pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
{ {
unsigned char *escaped; unsigned char *escaped;
char *quoted;
size_t quotedlen;
zend_string *quoted_str;
pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
size_t tmp_len; size_t tmp_len;
switch (paramtype) { switch (paramtype) {
case PDO_PARAM_LOB: case PDO_PARAM_LOB:
/* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);
*quotedlen = tmp_len + 1; quotedlen = tmp_len + 1;
*quoted = emalloc(*quotedlen + 1); quoted = emalloc(quotedlen + 1);
memcpy((*quoted)+1, escaped, *quotedlen-2); memcpy(quoted+1, escaped, quotedlen-2);
(*quoted)[0] = '\''; quoted[0] = '\'';
(*quoted)[*quotedlen-1] = '\''; quoted[quotedlen-1] = '\'';
(*quoted)[*quotedlen] = '\0'; quoted[quotedlen] = '\0';
PQfreemem(escaped); PQfreemem(escaped);
break; break;
default: default:
*quoted = safe_emalloc(2, unquotedlen, 3); quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
(*quoted)[0] = '\''; quoted[0] = '\'';
*quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL);
(*quoted)[*quotedlen + 1] = '\''; quoted[quotedlen + 1] = '\'';
(*quoted)[*quotedlen + 2] = '\0'; quoted[quotedlen + 2] = '\0';
*quotedlen += 2; quotedlen += 2;
} }
return true;
quoted_str = zend_string_init(quoted, quotedlen, 0);
efree(quoted);
return quoted_str;
} }
static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len) static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t *len)

View file

@ -227,12 +227,14 @@ static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t
} }
/* NB: doesn't handle binary strings... use prepared stmts for that */ /* NB: doesn't handle binary strings... use prepared stmts for that */
static bool sqlite_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
{ {
*quoted = safe_emalloc(2, unquotedlen, 3); char *quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
sqlite3_snprintf(2*unquotedlen + 3, *quoted, "'%q'", unquoted); /* TODO use %Q format? */
*quotedlen = strlen(*quoted); sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted));
return true; zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0);
efree(quoted);
return quoted_str;
} }
static bool sqlite_handle_begin(pdo_dbh_t *dbh) static bool sqlite_handle_begin(pdo_dbh_t *dbh)