Bug #117279 heap-buffer-overflow in SQLGetData
Submitted: 23 Jan 9:31 Modified: 24 Jan 5:37
Reporter: mysql my Email Updates:
Status: Need Feedback Impact on me:
None 
Category:Connector / ODBC Severity:S3 (Non-critical)
Version:9.2.0 OS:Ubuntu (22.04)
Assigned to: Rafal Somla CPU Architecture:x86

[23 Jan 9:31] mysql my
Description:
If using SQLPrepare/SQLExecute to query sql with address sanitize/valgrind, it will report hep-build-overflow

How to repeat:
compiling the following code with -fsanitize=address, and run

#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    SQLHENV hEnv;
    SQLHDBC hDbc;
    SQLHSTMT hStmt;
    SQLRETURN ret;

    // Allocate environment handle
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
    SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);

    // Allocate connection handle
    SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);

    // Connect to the database
    SQLConnect(hDbc, (SQLCHAR *)"xxx", SQL_NTS,
               (SQLCHAR *)"xxx", SQL_NTS,
               (SQLCHAR *)"xxx", SQL_NTS);

    // Allocate statement handle
    SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

    // Execute query
    if (SQLPrepare(hStmt, (SQLCHAR *)"SELECT * FROM xxx", SQL_NTS) != SQL_SUCCESS) {
        printf("Unable to prepare SQL statement!\n");
        return 0;
    }

    SQLRETURN result = SQLExecute(hStmt);

    if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO && result != SQL_NO_DATA) {
        printf("execute error!\n");
        return 0;
    }

    // Get the number of columns
    SQLSMALLINT numCols;
    SQLNumResultCols(hStmt, &numCols);
    printf("Number of columns: %d\n", numCols);

    int done = 0;

    while (!done) {
        int name_len = 256;
        char **names;
        char **vals;
        int y = 0;

        SQLRETURN result = SQLFetch(hStmt);

        if (result != SQL_SUCCESS) {
            break;
        }

        names = calloc(numCols, sizeof(*names));
        vals = calloc(numCols, sizeof(*vals));

        for (int x = 1; x <= numCols; x++) {
            SQLSMALLINT NameLength = 0, DataType = 0, DecimalDigits = 0, Nullable = 0;
            SQLULEN ColumnSize = 0;
            SQLLEN indicator = 0;
            names[y] = malloc(name_len);
            memset(names[y], 0, name_len);

            SQLRETURN ret = SQLDescribeCol(hStmt, x, (SQLCHAR *)names[y], (SQLSMALLINT)name_len, &NameLength, &DataType, &ColumnSize, &DecimalDigits, &Nullable);
            if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
                printf("Column:%d, ret:%d, name: %s, data_type:%d, size:%lu, nullable:%d\n", x, ret, names[y], DataType, ColumnSize, Nullable);
            } else {
                printf("Failed to describe column:%d, ret:%d\n", x, ret);
            }
            if (!ColumnSize) {
                ColumnSize = 255;
            }
            ColumnSize++;

            vals[y] = malloc(ColumnSize);
            memset(vals[y], 0, ColumnSize);
            ret = SQLGetData(hStmt, x, SQL_C_CHAR, (SQLCHAR *)vals[y], ColumnSize, &indicator);
            if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
                if (indicator == SQL_NULL_DATA) {
                    printf("Column:%d, ret:%d, name: %s, data_type:%d, size:%lu, nullable:%d\n", x, ret, names[y], DataType, ColumnSize, Nullable);
                }
            } else {
                printf("Failed to retrieve data for column:%d, ret:%d\n", x, ret);
            }
            y++;
        }

        done = 1;

        for (int x = 0; x < y; x++) {
            free(names[x]);
            free(vals[x]);
        }
        free(names);
        free(vals);
    }

    // Cleanup
    SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
    SQLDisconnect(hDbc);
    SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

    return 0;
}
[24 Jan 5:37] Bogdan Degtyariov
Thank you for providing the test case.

We were not able to get errors about heap or buffer overflow when running it using your instructions.

It is possible the leak happens on a specific data set and table structure.

Can you send a sample data to reproduce the issue?