Skip to Content.
Sympa Menu

freetds - [freetds] assert in odbc driver for long error messages

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: Mark Brand <mabrand AT mabrand.nl>
  • To: FreeTDS Development Group <freetds AT lists.ibiblio.org>
  • Subject: [freetds] assert in odbc driver for long error messages
  • Date: Tue, 17 Apr 2012 15:35:23 +0200

Hi,

It seems that a rather long error message raised from a stored procedure triggers an ASSERT in the FreeTDS ODBC driver:

unicode: odbc_util.c:391: odbc_set_string_flag: Assertion `dest == ((void *)0) || dest-(SQLWCHAR*) buffer == out_len' failed.
Aborted

The attached rather primitive code illustrates the problem. The stored procedure is in the comment at the top.

I'm not very familiar with the code in question, but it looks to me that odbc_set_string_flag() is copying more than bytes than the limit imposed by cbBuffer, which is called cbErrorMsgMax from tdsdump_log() in error.c. In my debugger I see that initial_size is 1024 while out_len is 1127 just before the ASSERT.

regards,

Mark
#define UNICODE 1
#define _UNICODE 1
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <wchar.h>
#include <sql.h>
#include <sqlext.h>
#include <stdlib.h>
#include <string.h>


/*
Demonstration of triggered assert when invoking this stored procedure
using FreeTDS odbc driver:

create procedure proc_longerror as
begin
    raiserror('reallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylongreallylong error', 16, 1)
end

*/

static void extract_error(
    char *fn,
    SQLHANDLE handle,
    SQLSMALLINT type);

SQLTCHAR* toSQLTCHAR(const char *s)
{
  int size = strlen(s) + 1;
  SQLTCHAR* t = (SQLTCHAR*)malloc(size * sizeof(SQLTCHAR));
  int i;
  for (i = 0; i < size; i++)
      t[i] = s[i];
  return t;
}

char* fromSQLTCHAR(SQLTCHAR* t, int len)
{
    char *s = (char*)malloc(len +1);
    int i;
    for(i = 0; i < len; i++)
        s[i] = t[i];
    s[len] = '\0';
    return s;
}

main() {
  SQLHENV env;
  SQLHDBC dbc;
  SQLHSTMT stmt;
  SQLRETURN ret; /* ODBC API return status */
  SQLTCHAR outstr[1024];
  SQLSMALLINT outstrlen = 0;

  /* Allocate an environment handle */
  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
  /* We want ODBC 3 support */
  SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
  /* Allocate a connection handle */
  SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

  printf("SQLTCHAR size is: %d\n", sizeof(SQLTCHAR));

  /* Connect to the DSN mydsn */
  const char *cn = "DSN=dsn;UID=user;PWD=password;";
  SQLTCHAR  *tcn = toSQLTCHAR(cn);
  memset(outstr, 0, 1024 * sizeof(SQLTCHAR));
  ret = SQLDriverConnect(dbc, NULL, tcn, strlen(cn) * sizeof(SQLTCHAR),
             outstr, 1024, &outstrlen,
             0);
  if (SQL_SUCCEEDED(ret)) {
    printf("Connected\n");
    printf("Returned connection string was:\n\t%s\n", fromSQLTCHAR(outstr, outstrlen));
    if (ret == SQL_SUCCESS_WITH_INFO) {
      printf("Driver reported the following diagnostics\n");
      extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
    }
  } else {
    fprintf(stderr, "Failed to connect\n");
    extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
  }

  SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
  const char *cmd = "{CALL proc_longerror}";
  SQLTCHAR* tcmd = toSQLTCHAR(cmd);
  ret = SQLExecDirect(stmt, tcmd, SQL_NTS);
  
  if (SQL_SUCCEEDED(ret)) {
    printf("Success\n");
    if (ret == SQL_SUCCESS_WITH_INFO) {
      printf("Driver reported the following diagnostics\n");
      extract_error("SQLExecDirect", stmt, SQL_HANDLE_STMT);
    }
  } else {
    fprintf(stderr, "SQLExecDirect Failed\n");
    extract_error("SQLExecDirect", stmt, SQL_HANDLE_STMT);
  }
  SQLFreeHandle(SQL_HANDLE_DBC, stmt);

  SQLDisconnect(dbc);
  /* free up allocated handles */
  SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  SQLFreeHandle(SQL_HANDLE_ENV, env);
}

void extract_error(
    char *fn,
    SQLHANDLE handle,
    SQLSMALLINT type)
{
    SQLINTEGER   i = 0;
    SQLINTEGER   native;
    SQLWCHAR  state[ 7 ];
    SQLWCHAR  text[256];
    SQLSMALLINT  len;
    SQLRETURN    ret;

    fprintf(stderr,
            "\n"
            "The driver reported the following diagnostics whilst running "
            "%s\n\n",
            fn);

    do
    {
        ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
                            256, &len );
        if (SQL_SUCCEEDED(ret))
            printf("%ls:%ld:%ld:%s\n", state, i, native, fromSQLTCHAR(text, len));
    }
    while( ret == SQL_SUCCESS );
}



Archive powered by MHonArc 2.6.24.

Top of Page