On 08/25/2015 11:36 PM, Bill Parker wrote:
> 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:
Does Tcl_Alloc() actually return NULL if a malloc fails? I thought if
memory can not be allocated it calls Tcl_Panic() to report an error
message and then aborts the process.
Dan.
>
> =======================================================================
>
> --- 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)
>
>
> _______________________________________________
> sqlite-users mailing list
> sqlite-users at mailinglists.sqlite.org
> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users