From: Adam Baratz Date: Fri, 10 Nov 2017 18:29:14 +0000 (-0500) Subject: More robust handling of stringified column data X-Git-Tag: php-7.3.0alpha1~1061 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=01f2427b838d55f8b6837585dadeb16edf4ed02b;p=php More robust handling of stringified column data - Use at least the FreeTDS maximum when converting datetime data - Pass buffer length to dbconvert() - Use dbconvert() return value to set string lengths or handle errors - Move shared code into shared function --- diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 175cb30332..3e4625ee67 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -260,7 +260,7 @@ static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno) return 1; } -static int pdo_dblib_stmt_stringify_col(pdo_stmt_t *stmt, int coltype) +static int pdo_dblib_stmt_should_stringify_col(pdo_stmt_t *stmt, int coltype) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; @@ -305,6 +305,48 @@ static int pdo_dblib_stmt_stringify_col(pdo_stmt_t *stmt, int coltype) return 0; } +static void pdo_dblib_stmt_stringify_col(int coltype, LPBYTE data, DBINT data_len, zval **ptr) +{ + DBCHAR *tmp_data; + DBINT tmp_data_len; + zval *zv; + + /* FIXME: We allocate more than we need here */ + tmp_data_len = 32 + (2 * (data_len)); + + switch (coltype) { + case SQLDATETIME: + case SQLDATETIM4: { + if (tmp_data_len < DATETIME_MAX_LEN) { + tmp_data_len = DATETIME_MAX_LEN; + } + break; + } + } + + tmp_data = emalloc(tmp_data_len); + data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len); + + zv = emalloc(sizeof(zval)); + if (data_len > 0) { + /* to prevent overflows, tmp_data_len is provided as a dest len for dbconvert() + * this code previously passed a dest len of -1 + * the FreeTDS impl of dbconvert() does an rtrim with that value, so replicate that behavior + */ + while (data_len > 0 && tmp_data[data_len - 1] == ' ') { + data_len--; + } + + ZVAL_STRINGL(zv, tmp_data, data_len); + } else { + ZVAL_EMPTY_STRING(zv); + } + + efree(tmp_data); + + *ptr = zv; +} + static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees) { @@ -323,15 +365,8 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, data_len = dbdatlen(H->link, colno+1); if (data_len != 0 || data != NULL) { - if (pdo_dblib_stmt_stringify_col(stmt, coltype) && dbwillconvert(coltype, SQLCHAR)) { - tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */ - tmp_data = emalloc(tmp_data_len); - data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, -1); - - zv = emalloc(sizeof(zval)); - ZVAL_STRING(zv, tmp_data); - - efree(tmp_data); + if (pdo_dblib_stmt_should_stringify_col(stmt, coltype) && dbwillconvert(coltype, SQLCHAR)) { + pdo_dblib_stmt_stringify_col(coltype, data, data_len, &zv); } if (!zv) { @@ -438,7 +473,6 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zv = emalloc(sizeof(zval)); ZVAL_STRINGL(zv, tmp_data, data_len); efree(tmp_data); - } else { /* 16-byte binary representation */ zv = emalloc(sizeof(zval)); @@ -449,14 +483,7 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, default: { if (dbwillconvert(coltype, SQLCHAR)) { - tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */ - tmp_data = emalloc(tmp_data_len); - data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, -1); - - zv = emalloc(sizeof(zval)); - ZVAL_STRING(zv, tmp_data); - - efree(tmp_data); + pdo_dblib_stmt_stringify_col(coltype, data, data_len, &zv); } break; diff --git a/ext/pdo_dblib/php_pdo_dblib_int.h b/ext/pdo_dblib/php_pdo_dblib_int.h index 805435a1c2..99ea4c3f3a 100644 --- a/ext/pdo_dblib/php_pdo_dblib_int.h +++ b/ext/pdo_dblib/php_pdo_dblib_int.h @@ -90,6 +90,10 @@ typedef unsigned char *LPBYTE; typedef float DBFLT4; #endif +// hardcoded string length from FreeTDS +// src/tds/convert.c:tds_convert_datetimeall() +# define DATETIME_MAX_LEN 63 + int pdo_dblib_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);