Hello All, In reviewing source code files in sqlite 3.8.11.1, I found some instances of calls to Tcl_Alloc() which are not checked for a return value of NULL, indicating failure in directory '/tea/generic', file 'tclsqlite3.c'. Additionally, in the event of failure, there are some cases where memset()/memcpy() is called after Tcl_Alloc(), but in the event that Tcl_Alloc() returns NULL, memset()/memcpy() will generate a segmentation fault/violation if memset()/memcpy() is called with a address location pointing to NULL (see test program below the patch file).
The patch file below should catch and handle all conditions where Tcl_Alloc() is called, but are NOT checked for a return value of NULL: ======================================================================= --- tclsqlite3.c.orig 2015-08-22 18:50:01.656000000 -0700 +++ tclsqlite3.c 2015-08-22 19:12:05.716000000 -0700 @@ -380,6 +380,10 @@ } p = (IncrblobChannel *)Tcl_Alloc(sizeof(IncrblobChannel)); + if( !p ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } p->iSeek = 0; p->pBlob = pBlob; @@ -439,6 +443,10 @@ SqlFunc *p, *pNew; int nName = strlen30(zName); pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 ); + if( !pNew ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return NULL; /* what should be returned here? */ + } pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName+1); for(p=pDb->pFunc; p; p=p->pNext){ @@ -1168,6 +1176,10 @@ nVar = sqlite3_bind_parameter_count(pStmt); nByte = sizeof(SqlPreparedStmt) + nVar*sizeof(Tcl_Obj *); pPreStmt = (SqlPreparedStmt*)Tcl_Alloc(nByte); + if( !pPreStmt ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memset(pPreStmt, 0, nByte); pPreStmt->pStmt = pStmt; @@ -1177,6 +1189,11 @@ #ifdef SQLITE_TEST if( pPreStmt->zSql==0 ){ char *zCopy = Tcl_Alloc(pPreStmt->nSql + 1); + if( !zCopy ) { + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + Tcl_Free(pPreStmt); + return TCL_ERROR; + } memcpy(zCopy, zSql, pPreStmt->nSql); zCopy[pPreStmt->nSql] = '\0'; pPreStmt->zSql = zCopy; @@ -1372,6 +1389,10 @@ p->nCol = nCol = sqlite3_column_count(pStmt); if( nCol>0 && (papColName || p->pArray) ){ apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); + if( !apColName ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return; + } for(i=0; i<nCol; i++){ apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1); Tcl_IncrRefCount(apColName[i]); @@ -1715,6 +1736,10 @@ zAuth = Tcl_GetStringFromObj(objv[2], &len); if( zAuth && len>0 ){ pDb->zAuth = Tcl_Alloc( len + 1 ); + if( !pDb->zAuth ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zAuth, zAuth, len+1); }else{ pDb->zAuth = 0; @@ -1804,6 +1829,10 @@ zBusy = Tcl_GetStringFromObj(objv[2], &len); if( zBusy && len>0 ){ pDb->zBusy = Tcl_Alloc( len + 1 ); + if( !pDb->zBusy ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zBusy, zBusy, len+1); }else{ pDb->zBusy = 0; @@ -1970,6 +1999,10 @@ zCommit = Tcl_GetStringFromObj(objv[2], &len); if( zCommit && len>0 ){ pDb->zCommit = Tcl_Alloc( len + 1 ); + if( !pDb->zCommit ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zCommit, zCommit, len+1); }else{ pDb->zCommit = 0; @@ -2315,6 +2348,10 @@ Tcl_IncrRefCount(pScript); p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); + if( !p ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } dbEvalInit(p, pDb, objv[2], pArray); cd2[0] = (void *)p; @@ -2458,6 +2495,10 @@ } if( zNull && len>0 ){ pDb->zNull = Tcl_Alloc( len + 1 ); + if( !pDb->zNULL ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zNull, zNull, len); pDb->zNull[len] = '\0'; }else{ @@ -2513,6 +2554,10 @@ zProgress = Tcl_GetStringFromObj(objv[3], &len); if( zProgress && len>0 ){ pDb->zProgress = Tcl_Alloc( len + 1 ); + if( !pDb->zProgress ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zProgress, zProgress, len+1); }else{ pDb->zProgress = 0; @@ -2555,6 +2600,10 @@ zProfile = Tcl_GetStringFromObj(objv[2], &len); if( zProfile && len>0 ){ pDb->zProfile = Tcl_Alloc( len + 1 ); + if( !pDb->zProfile ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zProfile, zProfile, len+1); }else{ pDb->zProfile = 0; @@ -2741,6 +2790,10 @@ zTrace = Tcl_GetStringFromObj(objv[2], &len); if( zTrace && len>0 ){ pDb->zTrace = Tcl_Alloc( len + 1 ); + if( !pDb->zTrace ){ + Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC); + return TCL_ERROR; + } memcpy(pDb->zTrace, zTrace, len+1); }else{ pDb->zTrace = 0; ======================================================================= Here is a test program which shows the potential issue with memcpy() and a NULL address location (compiled with: gcc -O2 -Wall -g memcpy.c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char *buf; int x = 10; buf = malloc(80); #if 1 buf = NULL; /* force buf to point to NULL */ #endif while (0 <-- x) printf("Value of x is: %d\n", x); printf("before memcpy() is called...\n"); memcpy(buf, "hello", 5); /* what happens? */ printf("after memcpy() is called...\n"); printf("Address of buf is: %p, value of buf is: %s\n", buf, buf); return 0; } Here is the output from 'a.out' (Fedora 22 Server, gcc 5.1) [bill at moocow ~]$ ./a.out Value of x is: 9 Value of x is: 8 Value of x is: 7 Value of x is: 6 Value of x is: 5 Value of x is: 4 Value of x is: 3 Value of x is: 2 Value of x is: 1 before memcpy() is called... Segmentation fault (core dumped) Here is the same program with the buf = NULL statement commented out via the pre-processor: [bill at moocow ~]$ cat memcpy-works.c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char *buf; int x = 10; buf = malloc(80); #if 0 buf = NULL; /* force buf to point to NULL */ #endif while (0 <-- x) printf("Value of x is: %d\n", x); printf("before memcpy() is called...\n"); memcpy(buf, "hello", 5); /* what happens? */ printf("after memcpy() is called...\n"); printf("Address of buf is: %p, value of buf is: %s\n", buf, buf); return 0; } Here is the output from ./a.out: [bill at moocow ~]$ ./a.out Value of x is: 9 Value of x is: 8 Value of x is: 7 Value of x is: 6 Value of x is: 5 Value of x is: 4 Value of x is: 3 Value of x is: 2 Value of x is: 1 before memcpy() is called... after memcpy() is called... Address of buf is: 0x24df010, value of buf is: hello I am attaching the patch file to this bug report... Questions, Comments, Suggestions, Complaints? :) Bill Parker (wp02855 at gmail dot com)