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)