assert in odbc driver for long error messages

Mark Brand <[email protected]>
Newsgroups gmane.comp.db.tds.freetds
Message-ID <[email protected]>
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

_______________________________________________
FreeTDS mailing list
[email protected]
http://lists.ibiblio.org/mailman/listinfo/freetds
demo.c (text/x-c++src, 4.4 KB)
#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 );
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.