]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix segfaults that can occur if a malloc failure happens just before
authordrh <drh@noemail.net>
Wed, 25 Apr 2007 18:23:52 +0000 (18:23 +0000)
committerdrh <drh@noemail.net>
Wed, 25 Apr 2007 18:23:52 +0000 (18:23 +0000)
a built-in function calls sqlite3_value_text(). (CVS 3874)

FossilOrigin-Name: 9cb0ed6ee9827bc6884a0195044d5b6ad0de698e

manifest
manifest.uuid
src/date.c
src/func.c
src/vdbeapi.c
test/malloc8.test [new file with mode: 0644]

index e13ecea3f998dca8568f7f34e3ec2ec58353d145..f72d2611fd78de06f90e33924f72ffe6b9a936e7 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Disable\stests\sin\smisc7\sthat\sdo\snot\swork\son\swindows\sdue\sto\slimitations\sof\nthe\swindows\sfile\ssystem.\s(CVS\s3873)
-D 2007-04-25T15:42:26
+C Fix\ssegfaults\sthat\scan\soccur\sif\sa\smalloc\sfailure\shappens\sjust\sbefore\na\sbuilt-in\sfunction\scalls\ssqlite3_value_text().\s(CVS\s3874)
+D 2007-04-25T18:23:53
 F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -64,11 +64,11 @@ F src/btree.h 9b2cc0d113c0bc2d37d244b9a394d56948c9acbf
 F src/build.c 1880da163d9aa404016242b8b76d69907f682cd8
 F src/callback.c 6414ed32d55859d0f65067aa5b88d2da27b3af9e
 F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
-F src/date.c 74b76691bddf58b634f6bf4a77c8c58234268c6e
+F src/date.c 94a6777df13d2aaacd19de080d9e8d3444364133
 F src/delete.c 5c0d89b3ef7d48fe1f5124bfe8341f982747fe29
 F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
 F src/expr.c 2f0f9f89efe9170e5e6ca5d5e93a9d5896fff5ac
-F src/func.c 007d957c057bb42b0d37aa6ad4be0e1c67a8871b
+F src/func.c acb2c5055629ae3eebd71868af10fe425ef05f06
 F src/hash.c 67b23e14f0257b69a3e8aa663e4eeadc1a2b6fd5
 F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564
 F src/insert.c 413cc06990cb3c401e64e596776c1e43934f8841
@@ -128,7 +128,7 @@ F src/vacuum.c 8bd895d29e7074e78d4e80f948e35ddc9cf2beef
 F src/vdbe.c 814dab208a156250bc5e77f827f4e0c8ad734820
 F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
 F src/vdbeInt.h 4b19fd8febad3fd14c4c97adaefc06754d323132
-F src/vdbeapi.c 1fca7ff056d03f131caa6b1296bb221da65ed7f4
+F src/vdbeapi.c 245263aa2d70d87b1201753cddc881996f219843
 F src/vdbeaux.c ef59545f53f90394283f2fd003375d3ebbf0bd6e
 F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
 F src/vdbemem.c 981a113405bd9b80aeb71fe246a2f01708e8a8f7
@@ -273,6 +273,7 @@ F test/malloc4.test 59cd02f71b363302a04c4e77b97c0a1572eaa210
 F test/malloc5.test f228cb7101ae403327824d327a1f5651d83ef0f2
 F test/malloc6.test 025ae0b78542e0ddd000d23f79d93e9be9ba0f15
 F test/malloc7.test 1cf52834509eac7ebeb92105dacd4669f9ca9869
+F test/malloc8.test ede3231e1d9359b3c618357e49cb1c62267382e7
 F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
 F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
 F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
@@ -461,7 +462,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 16979f4525652bfd6c6e5306eafc883bef3880aa
-R 28b0457dabf779eb2bc14720d6227390
+P 66646d6fda067e19240808aef65fafd8fa177cdd
+R 48b7346bd8cf5e77d4009859f7c6c683
 U drh
-Z 15c285b6aa265216fe50f5053ca77b43
+Z fd08492b87e717e5d32a8be7d387293c
index 102849e16b48313f9c3f58c5837d82ef8c2408d4..1a153c30327387186ab211acd94c1022ec6ecb53 100644 (file)
@@ -1 +1 @@
-66646d6fda067e19240808aef65fafd8fa177cdd
\ No newline at end of file
+9cb0ed6ee9827bc6884a0195044d5b6ad0de698e
\ No newline at end of file
index 3625fe5c724bd322e9ea7d44d8c6d4727f1396bf..5ff40458900cd066c3caa0b6b974e2e6a3f8e425 100644 (file)
@@ -16,7 +16,7 @@
 ** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
 ** All other code has file scope.
 **
-** $Id: date.c,v 1.62 2007/04/06 02:32:34 drh Exp $
+** $Id: date.c,v 1.63 2007/04/25 18:23:53 drh Exp $
 **
 ** NOTES:
 **
@@ -655,12 +655,15 @@ static int parseModifier(const char *zMod, DateTime *p){
 */
 static int isDate(int argc, sqlite3_value **argv, DateTime *p){
   int i;
+  const unsigned char *z;
   if( argc==0 ) return 1;
-  if( SQLITE_NULL==sqlite3_value_type(argv[0]) || 
-      parseDateOrTime((char*)sqlite3_value_text(argv[0]), p) ) return 1;
+  if( (z = sqlite3_value_text(argv[0]))==0 || parseDateOrTime((char*)z, p) ){
+    return 1;
+  }
   for(i=1; i<argc; i++){
-    if( SQLITE_NULL==sqlite3_value_type(argv[i]) || 
-        parseModifier((char*)sqlite3_value_text(argv[i]), p) ) return 1;
+    if( (z = sqlite3_value_text(argv[i]))==0 || parseModifier((char*)z, p) ){
+      return 1;
+    }
   }
   return 0;
 }
index 457cbd5d3bf13ca4eea8ada3a22d5ce536e7fb77..c6d67e01fede350d3e96a8ca5ab2c64b86ca9261 100644 (file)
@@ -16,7 +16,7 @@
 ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
 ** All other code has file scope.
 **
-** $Id: func.c,v 1.139 2007/04/10 13:51:18 drh Exp $
+** $Id: func.c,v 1.140 2007/04/25 18:23:53 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -102,6 +102,7 @@ static void lengthFunc(
     }
     case SQLITE_TEXT: {
       const unsigned char *z = sqlite3_value_text(argv[0]);
+      if( z==0 ) return;
       for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; }
       sqlite3_result_int(context, len);
       break;
@@ -212,30 +213,38 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
 ** Implementation of the upper() and lower() SQL functions.
 */
 static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
-  unsigned char *z;
+  char *z1;
+  const char *z2;
   int i;
   if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
-  z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1);
-  if( z==0 ) return;
-  strcpy((char*)z, (char*)sqlite3_value_text(argv[0]));
-  for(i=0; z[i]; i++){
-    z[i] = toupper(z[i]);
+  z2 = (char*)sqlite3_value_text(argv[0]);
+  if( z2 ){
+    z1 = sqlite3_malloc(sqlite3_value_bytes(argv[0])+1);
+    if( z1 ){
+      strcpy(z1, z2);
+      for(i=0; z1[i]; i++){
+        z1[i] = toupper(z1[i]);
+      }
+      sqlite3_result_text(context, z1, -1, sqlite3_free);
+    }
   }
-  sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT);
-  sqliteFree(z);
 }
 static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
-  unsigned char *z;
+  char *z1;
+  const char *z2;
   int i;
   if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
-  z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1);
-  if( z==0 ) return;
-  strcpy((char*)z, (char*)sqlite3_value_text(argv[0]));
-  for(i=0; z[i]; i++){
-    z[i] = tolower(z[i]);
+  z2 = (char*)sqlite3_value_text(argv[0]);
+  if( z2 ){
+    z1 = sqlite3_malloc(sqlite3_value_bytes(argv[0])+1);
+    if( z1 ){
+      strcpy(z1, z2);
+      for(i=0; z1[i]; i++){
+        z1[i] = tolower(z1[i]);
+      }
+      sqlite3_result_text(context, z1, -1, sqlite3_free);
+    }
   }
-  sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT);
-  sqliteFree(z);
 }
 
 /*
@@ -523,6 +532,7 @@ static void likeFunc(
     ** Otherwise, return an error.
     */
     const unsigned char *zEsc = sqlite3_value_text(argv[2]);
+    if( zEsc==0 ) return;
     if( sqlite3utf8CharLen((char*)zEsc, -1)!=1 ){
       sqlite3_result_error(context, 
           "ESCAPE expression must be a single character", -1);
@@ -625,6 +635,7 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
       const unsigned char *zArg = sqlite3_value_text(argv[0]);
       char *z;
 
+      if( zArg==0 ) return;
       for(i=n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; }
       z = sqliteMalloc( i+n+3 );
       if( z==0 ) return;
@@ -692,16 +703,14 @@ static void replaceFunc(
   int i, j;                /* Loop counters */
 
   assert( argc==3 );
-  if( sqlite3_value_type(argv[0])==SQLITE_NULL ||
-      sqlite3_value_type(argv[1])==SQLITE_NULL ||
-      sqlite3_value_type(argv[2])==SQLITE_NULL ){
-    return;
-  }
   zStr = sqlite3_value_text(argv[0]);
+  if( zStr==0 ) return;
   nStr = sqlite3_value_bytes(argv[0]);
   zPattern = sqlite3_value_text(argv[1]);
+  if( zPattern==0 ) return;
   nPattern = sqlite3_value_bytes(argv[1]);
   zRep = sqlite3_value_text(argv[2]);
+  if( zRep==0 ) return;
   nRep = sqlite3_value_bytes(argv[2]);
   if( nPattern>=nRep ){
     nOut = nStr;
@@ -746,14 +755,13 @@ static void trimFunc(
     return;
   }
   zIn = sqlite3_value_text(argv[0]);
+  if( zIn==0 ) return;
   nIn = sqlite3_value_bytes(argv[0]);
   if( argc==1 ){
     static const unsigned char zSpace[] = " ";
     zCharSet = zSpace;
-  }else if( sqlite3_value_type(argv[1])==SQLITE_NULL ){
+  }else if( (zCharSet = sqlite3_value_text(argv[1]))==0 ){
     return;
-  }else{
-    zCharSet = sqlite3_value_text(argv[1]);
   }
   cFirst = zCharSet[0];
   if( cFirst ){
@@ -834,14 +842,16 @@ static void soundexFunc(
 */
 static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
   const char *zFile = (const char *)sqlite3_value_text(argv[0]);
-  const char *zProc = 0;
+  const char *zProc;
   sqlite3 *db = sqlite3_user_data(context);
   char *zErrMsg = 0;
 
   if( argc==2 ){
     zProc = (const char *)sqlite3_value_text(argv[1]);
+  }else{
+    zProc = 0;
   }
-  if( sqlite3_load_extension(db, zFile, zProc, &zErrMsg) ){
+  if( zFile && sqlite3_load_extension(db, zFile, zProc, &zErrMsg) ){
     sqlite3_result_error(context, zErrMsg, -1);
     sqlite3_free(zErrMsg);
   }
index 3d3d815c7fa7fd2dea521dec9f4361cef3ff3116..2e67cbdcb251018ecdc0e0f8100450f3e4a418f6 100644 (file)
@@ -443,7 +443,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
   Vdbe *pVm = (Vdbe *)pStmt;
   int vals = sqlite3_data_count(pStmt);
   if( i>=vals || i<0 ){
-    static const Mem nullMem = {{0}, 0.0, "", 0, MEM_Null, MEM_Null };
+    static const Mem nullMem = {{0}, 0.0, "", 0, MEM_Null, SQLITE_NULL };
     sqlite3Error(pVm->db, SQLITE_RANGE, 0);
     return (Mem*)&nullMem;
   }
diff --git a/test/malloc8.test b/test/malloc8.test
new file mode 100644 (file)
index 0000000..e624ca0
--- /dev/null
@@ -0,0 +1,158 @@
+# 2006 July 26
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file contains additional out-of-memory checks (see malloc.tcl)
+# added to expose a bug in out-of-memory handling for sqlite3_value_text()
+#
+# $Id: malloc8.test,v 1.1 2007/04/25 18:23:53 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {[info command sqlite_malloc_stat]==""} {
+   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
+   finish_test
+   return
+}
+
+# Usage: do_malloc_test <test number> <options...>
+#
+# The first argument, <test number>, is an integer used to name the
+# tests executed by this proc. Options are as follows:
+#
+#     -tclprep          TCL script to run to prepare test.
+#     -sqlprep          SQL script to run to prepare test.
+#     -tclbody          TCL script to run with malloc failure simulation.
+#     -sqlbody          TCL script to run with malloc failure simulation.
+#     -cleanup          TCL script to run after the test.
+#
+# This command runs a series of tests to verify SQLite's ability
+# to handle an out-of-memory condition gracefully. It is assumed
+# that if this condition occurs a malloc() call will return a
+# NULL pointer. Linux, for example, doesn't do that by default. See
+# the "BUGS" section of malloc(3).
+#
+# Each iteration of a loop, the TCL commands in any argument passed
+# to the -tclbody switch, followed by the SQL commands in any argument
+# passed to the -sqlbody switch are executed. Each iteration the
+# Nth call to sqliteMalloc() is made to fail, where N is increased
+# each time the loop runs starting from 1. When all commands execute
+# successfully, the loop ends.
+#
+proc do_malloc_test {tn args} {
+  array unset ::mallocopts 
+  array set ::mallocopts $args
+
+  set ::go 1
+  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
+    do_test malloc8-$tn.$::n {
+
+      sqlite_malloc_fail 0
+      catch {db close}
+      sqlite3 db test.db
+      set ::DB [sqlite3_connection_pointer db]
+
+      # Execute any -tclprep and -sqlprep scripts.
+      #
+      if {[info exists ::mallocopts(-tclprep)]} {
+        eval $::mallocopts(-tclprep)
+      }
+      if {[info exists ::mallocopts(-sqlprep)]} {
+        execsql $::mallocopts(-sqlprep)
+      }
+
+      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
+      # -sqlbody scripts.
+      #
+      sqlite_malloc_fail $::n
+      set ::mallocbody {}
+      if {[info exists ::mallocopts(-tclbody)]} {
+        append ::mallocbody "$::mallocopts(-tclbody)\n"
+      }
+      if {[info exists ::mallocopts(-sqlbody)]} {
+        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
+      }
+      set v [catch $::mallocbody msg]
+
+      # If the test fails (if $v!=0) and the database connection actually
+      # exists, make sure the failure code is SQLITE_NOMEM.
+      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
+              && [db errorcode]!=7} {
+        set v 999
+      }
+
+      set leftover [lindex [sqlite_malloc_stat] 2]
+      if {$leftover>0} {
+        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
+        set ::go 0
+        if {$v} {
+          puts "\nError message returned: $msg"
+        } else {
+          set v {1 1}
+        }
+      } else {
+        set v2 [expr {$msg=="" || $msg=="out of memory"}]
+        if {!$v2} {puts "\nError message returned: $msg"}
+        lappend v $v2
+      }
+    } {1 1}
+
+    if {[info exists ::mallocopts(-cleanup)]} {
+      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
+    }
+  }
+  unset ::mallocopts
+}
+
+# The setup is a database with UTF-16 encoding that contains a single
+# large string.  We will be running lots of queries against this 
+# database.  Because we will be extracting the string as UTF-8, there
+# is a type conversion that occurs and thus an opportunity for malloc()
+# to fail and for sqlite3_value_text() to return 0 even though
+# sqlite3_value_type() returns SQLITE_TEXT.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+db eval {
+  PRAGMA encoding='UTF-16';
+  CREATE TABLE t1(a);
+  INSERT INTO t1 
+  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+}
+
+
+do_malloc_test 1 -sqlbody {
+  SELECT lower(a), upper(a), quote(a), trim(a), trim('x',a) FROM t1;
+}
+do_malloc_test 2 -sqlbody {
+  SELECT replace(a,'x','y'), replace('x',a,'y'), replace('x','y',a)
+    FROM t1;
+}
+do_malloc_test 3 -sqlbody {
+  SELECT length(a), substr(a, 4, 4) FROM t1;
+}
+do_malloc_test 4 -sqlbody {
+  SELECT julianday(a,a) FROM t1;
+}
+do_malloc_test 5 -sqlbody {
+  SELECT 1 FROM t1 WHERE a LIKE 'hello' ESCAPE NULL;
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-99.X {
+  catch {db close}
+  set sqlite_open_file_count
+} {0}
+
+sqlite_malloc_fail 0
+finish_test