]> granicus.if.org Git - php/commitdiff
More robust handling of stringified column data
authorAdam Baratz <adambaratz@php.net>
Fri, 10 Nov 2017 18:29:14 +0000 (13:29 -0500)
committerAdam Baratz <adambaratz@php.net>
Fri, 10 Nov 2017 18:29:14 +0000 (13:29 -0500)
 - 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

ext/pdo_dblib/dblib_stmt.c
ext/pdo_dblib/php_pdo_dblib_int.h

index 175cb303326270e4f7f007ff844569210eaac025..3e4625ee67066b797f65c5c5b1c9c08d63ed636b 100644 (file)
@@ -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;
index 805435a1c2e4597aca1a1e840716a63014a9d01a..99ea4c3f3ac1be5ffee6c96ba11c221b1bcd1e87 100644 (file)
@@ -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);