]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a crash that can occur following an OOM fault.
authordan <dan@noemail.net>
Thu, 15 Aug 2013 18:43:21 +0000 (18:43 +0000)
committerdan <dan@noemail.net>
Thu, 15 Aug 2013 18:43:21 +0000 (18:43 +0000)
FossilOrigin-Name: 9f80b2687012ab7c4d6d654fe19f40878bd78bd8

manifest
manifest.uuid
src/vdbemem.c
test/analyze9.test
test/mallocA.test

index 4f54fd9f5d70309976fc443da18073a0fcb0640b..a2529d2cb281b46ddc155bada9a81dd3bc0c3129 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\ssome\sassert()\sstatements\sin\svdbe.c\sto\sensure\sthat\sa\smemory\scell\sused\sto\sstore\sa\sVdbeCursor\sobject\sis\snot\salso\sused\sfor\ssome\sother\spurpose.
-D 2013-08-15T16:18:39.664
+C Fix\sa\scrash\sthat\scan\soccur\sfollowing\san\sOOM\sfault.
+D 2013-08-15T18:43:21.395
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -283,7 +283,7 @@ F src/vdbeInt.h e9b7c6b165a31a4715c5aa97223d20d265515231
 F src/vdbeapi.c 4d13580bd058b39623e8fcfc233b7df4b8191e8b
 F src/vdbeaux.c a6ea36a9dc714e1128a0173249a0532ddcab0489
 F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69
-F src/vdbemem.c 7ec9a78d6d4b2d4ebb4d3fd9b706065146616019
+F src/vdbemem.c c08dd81009fd6708dbb961d8ec7f82549765d680
 F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017
 F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc
 F src/vtab.c 2e8b489db47e20ae36cd247932dc671c9ded0624
@@ -308,7 +308,7 @@ F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
 F test/analyze6.test 19151da2c4e918905d2081b74ac5c4d47fc850ab
 F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
 F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88
-F test/analyze9.test b018c837164ada65f4a80dadbbcdc89cb1fff362
+F test/analyze9.test 83e74db42a49bb185e05f3a44a5d65323aba8a40
 F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944
 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
 F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
@@ -651,7 +651,7 @@ F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
 F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
 F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
 F test/malloc9.test 2307c6ee3703b0a21391f3ea92388b4b73f9105e
-F test/mallocA.test 47006c8d70f29b030652e251cb9d35ba60289198
+F test/mallocA.test 71e4b57e640c017cf2833e51fe6e8e43e8575b73
 F test/mallocAll.test 98f1be74bc9f49a858bc4f361fc58e26486798be
 F test/mallocB.test bc475ab850cda896142ab935bbfbc74c24e51ed6
 F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4
@@ -1107,7 +1107,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P 46fec9b1a1c4616df5a634dbf9235bd13408d3a9
-R e1ce93ce8d017990abe28f4c9f5b58f9
+P 71070c9fce86103f174220e07771df99b2e01405
+R fd0e4baaf9a9ffd0fe23644f24c090cf
 U dan
-Z 9abc3408e2534a3305d7999552dece52
+Z 1c4d6e6edcc7281c470c7559e77faff9
index e48df0d90069e0f4fe91636ea0658ced28ad5ecc..f0c5e4a7e1ba93e3ca4f40c2eb0071cdeef4f664 100644 (file)
@@ -1 +1 @@
-71070c9fce86103f174220e07771df99b2e01405
\ No newline at end of file
+9f80b2687012ab7c4d6d654fe19f40878bd78bd8
\ No newline at end of file
index db3e6b262eeae58ca4ffdec1b2afcff6ddc3240f..b75e86b0354d2557d536c0c424156ce9e12bc56f 100644 (file)
@@ -1006,29 +1006,85 @@ sqlite3_value *sqlite3ValueNew(sqlite3 *db){
 }
 
 /*
-** Argument pCtx is actually a pointer to a database handle. Allocate and
-** return an sqlite3_value object associated with this database handle.
+** Context object passed by sqlite3Stat4ProbeSetValue() through to 
+** valueNew(). See comments above valueNew() for details.
+*/
+struct ValueNewStat4Ctx {
+  Parse *pParse;
+  Index *pIdx;
+  UnpackedRecord **ppRec;
+  int iVal;
+};
+
+/*
+** Allocate and return a pointer to a new sqlite3_value object. If
+** the second argument to this function is NULL, the object is allocated
+** by calling sqlite3ValueNew().
 **
-** This function used as the xAlloc callback for valueFromExpr() when
-** it is called by sqlite3ValueFromExpr().
+** Otherwise, if the second argument is non-zero, then this function is 
+** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
+** already been allocated, allocate the UnpackedRecord structure that 
+** that function will return to its caller here. Then return a pointer 
+** an sqlite3_value within the UnpackedRecord.a[] array.
 */
-static sqlite3_value *valueNew(void *pCtx){
-  return sqlite3ValueNew((sqlite3*)pCtx);
+static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
+#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
+  if( p ){
+    UnpackedRecord *pRec = p->ppRec[0];
+
+    if( pRec==0 ){
+      Index *pIdx = p->pIdx;      /* Index being probed */
+      int nByte;                  /* Bytes of space to allocate */
+      int i;                      /* Counter variable */
+      int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */
+  
+      nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord);
+      pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
+      if( pRec ){
+        pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx);
+        if( pRec->pKeyInfo ){
+          assert( pRec->pKeyInfo->nField+1==nCol );
+          pRec->pKeyInfo->enc = ENC(db);
+          pRec->flags = UNPACKED_PREFIX_MATCH;
+          pRec->aMem = (Mem *)&pRec[1];
+          for(i=0; i<nCol; i++){
+            pRec->aMem[i].flags = MEM_Null;
+            pRec->aMem[i].type = SQLITE_NULL;
+            pRec->aMem[i].db = db;
+          }
+        }else{
+          sqlite3DbFree(db, pRec);
+          pRec = 0;
+        }
+      }
+      if( pRec==0 ) return 0;
+      p->ppRec[0] = pRec;
+    }
+  
+    pRec->nField = p->iVal+1;
+    return &pRec->aMem[p->iVal];
+  }
+#endif
+  return sqlite3ValueNew(db);
 }
 
 /*
-** This function is the same as sqlite3ValueFromExpr(), except that instead
-** of allocating any required sqlite3_value object by calling 
-** sqlite3ValueNew(), it does so by calling the supplied xAlloc hook. 
+** Extract a value from the supplied expression in the manner described
+** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
+** using valueNew().
+**
+** If pCtx is NULL and an error occurs after the sqlite3_value object
+** has been allocated, it is freed before returning. Or, if pCtx is not
+** NULL, it is assumed that the caller will free any allocated object
+** in all cases.
 */
 int valueFromExpr(
-  sqlite3 *db,                         /* The database connection */
-  Expr *pExpr,                         /* The expression to evaluate */
-  u8 enc,                              /* Encoding to use */
-  u8 affinity,                         /* Affinity to use */
-  sqlite3_value **ppVal,               /* Write the new value here */
-  sqlite3_value *(*xAlloc)(void*),     /* Used to allocate new sqlite3_value */
-  void *pAlloc                         /* Argument passed to xAlloc */
+  sqlite3 *db,                    /* The database connection */
+  Expr *pExpr,                    /* The expression to evaluate */
+  u8 enc,                         /* Encoding to use */
+  u8 affinity,                    /* Affinity to use */
+  sqlite3_value **ppVal,          /* Write the new value here */
+  struct ValueNewStat4Ctx *pCtx   /* Second argument for valueNew() */
 ){
   int op;
   char *zVal = 0;
@@ -1064,7 +1120,7 @@ int valueFromExpr(
   }
 
   if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
-    pVal = xAlloc(pAlloc);
+    pVal = valueNew(db, pCtx);
     if( pVal==0 ) goto no_mem;
     if( ExprHasProperty(pExpr, EP_IntValue) ){
       sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
@@ -1100,7 +1156,7 @@ int valueFromExpr(
       sqlite3ValueApplyAffinity(pVal, affinity, enc);
     }
   }else if( op==TK_NULL ){
-    pVal = xAlloc(pAlloc);
+    pVal = valueNew(db, pCtx);
     if( pVal==0 ) goto no_mem;
   }
 #ifndef SQLITE_OMIT_BLOB_LITERAL
@@ -1108,7 +1164,7 @@ int valueFromExpr(
     int nVal;
     assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
     assert( pExpr->u.zToken[1]=='\'' );
-    pVal = xAlloc(pAlloc);
+    pVal = valueNew(db, pCtx);
     if( !pVal ) goto no_mem;
     zVal = &pExpr->u.zToken[2];
     nVal = sqlite3Strlen30(zVal)-1;
@@ -1127,8 +1183,8 @@ int valueFromExpr(
 no_mem:
   db->mallocFailed = 1;
   sqlite3DbFree(db, zVal);
-  if( *ppVal==0 ) sqlite3ValueFree(pVal);
-  *ppVal = 0;
+  assert( *ppVal==0 );
+  if( pCtx==0 ) sqlite3ValueFree(pVal);
   return SQLITE_NOMEM;
 }
 
@@ -1149,7 +1205,7 @@ int sqlite3ValueFromExpr(
   u8 affinity,              /* Affinity to use */
   sqlite3_value **ppVal     /* Write the new value here */
 ){
-  return valueFromExpr(db, pExpr, enc, affinity, ppVal, valueNew, (void*)db);
+  return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0);
 }
 
 #if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
@@ -1207,64 +1263,6 @@ void sqlite3AnalyzeFunctions(void){
   }
 }
 
-/*
-** A pointer to an instance of this object is passed as the context 
-** pointer to valueNewStat4() (see below.
-*/
-struct ValueNewStat4Ctx {
-  Parse *pParse;
-  Index *pIdx;
-  UnpackedRecord **ppRec;
-  int iVal;
-};
-
-/*
-** This function is used as the xAlloc function with valueFromExpr() when
-** it is called by sqlite3Stat4ProbeSetValue(). The argument points to
-** an object of type ValueNewStat4Ctx (see above).
-**
-** If it has not already been allocated, this function allocates an 
-** UnpackedRecord structure and space for up to N values, where N is the
-** number of columns in the index being probed.
-*/
-static sqlite3_value *valueNewStat4(void *pCtx){
-  struct ValueNewStat4Ctx *p = (struct ValueNewStat4Ctx*)pCtx;
-  UnpackedRecord *pRec = p->ppRec[0];
-
-  if( pRec==0 ){
-    sqlite3 *db = p->pParse->db;  /* Database handle */
-    Index *pIdx = p->pIdx;        /* Index being probed */
-    int nByte;                    /* Bytes of space to allocate */
-    int i;                        /* Counter variable */
-    int nCol = pIdx->nColumn+1;   /* Number of index columns including rowid */
-
-    nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord);
-    pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
-    if( pRec ){
-      pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx);
-      if( pRec->pKeyInfo ){
-        assert( pRec->pKeyInfo->nField+1==nCol );
-        pRec->pKeyInfo->enc = ENC(db);
-        pRec->flags = UNPACKED_PREFIX_MATCH;
-        pRec->aMem = (Mem *)&pRec[1];
-        for(i=0; i<nCol; i++){
-          pRec->aMem[i].flags = MEM_Null;
-          pRec->aMem[i].type = SQLITE_NULL;
-          pRec->aMem[i].db = db;
-        }
-      }else{
-        sqlite3DbFree(db, pRec);
-        pRec = 0;
-      }
-    }
-    if( pRec==0 ) return 0;
-    p->ppRec[0] = pRec;
-  }
-
-  pRec->nField = p->iVal+1;
-  return &pRec->aMem[p->iVal];
-}
-
 /*
 ** This function is used to allocate and populate UnpackedRecord 
 ** structures intended to be compared against sample index keys stored 
@@ -1313,12 +1311,8 @@ int sqlite3Stat4ProbeSetValue(
   alloc.ppRec = ppRec;
   alloc.iVal = iVal;
 
-#if 0
-  if( iVal>0 ){ *pbOk = 0; return SQLITE_OK; }
-#endif
-
   if( !pExpr ){
-    pVal = valueNewStat4((void*)&alloc);
+    pVal = valueNew(pParse->db, &alloc);
     if( pVal ){
       sqlite3VdbeMemSetNull((Mem*)pVal);
       *pbOk = 1;
@@ -1330,7 +1324,7 @@ int sqlite3Stat4ProbeSetValue(
     int iVar = pExpr->iColumn;
     sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
     if( (v = pParse->pReprepare) ){
-      pVal = valueNewStat4((void*)&alloc);
+      pVal = valueNew(pParse->db, &alloc);
       if( pVal ){
         rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iVal-1]);
         if( rc==SQLITE_OK ){
@@ -1345,9 +1339,7 @@ int sqlite3Stat4ProbeSetValue(
     }
   }else{
     sqlite3 *db = pParse->db;
-    rc = valueFromExpr(
-        db, pExpr, ENC(db), affinity, &pVal, valueNewStat4, (void*)&alloc
-    );
+    rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc);
     *pbOk = (pVal!=0);
   }
 
index 08e8126b24a47e5d3840720935fc4006955afdcc..21b354792b0a7e0f6a1b676e01a6af8cca1fd036 100644 (file)
@@ -256,6 +256,30 @@ do_execsql_test 4.6 {
     ('34', '68', '102', '136', '170', '204', '238', '272')
 } {8}
 
+reset_db
+do_test 4.7 {
+  execsql { 
+    BEGIN;
+    CREATE TABLE t1(o,t INTEGER PRIMARY KEY);
+    CREATE INDEX i1 ON t1(o);
+  }
+  for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} {
+    execsql { INSERT INTO t1 VALUES('x', $i) }
+  }
+  execsql {
+    COMMIT;
+    ANALYZE;
+    SELECT count(*) FROM sqlite_stat4;
+  }
+} {8}
+do_execsql_test 4.8 {
+  SELECT test_decode(sample) FROM sqlite_stat4;
+} {
+  {x 211} {x 423} {x 635} {x 847} 
+  {x 1590} {x 3710} {x 5830} {x 7950}
+}
+
+
 #-------------------------------------------------------------------------
 # The following would cause a crash at one point.
 #
index 89951276f81ebd660e88166c2585c913404684d4..1e05723e139c30f397e2106309fed4b690e86205 100644 (file)
@@ -15,6 +15,7 @@
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
 source $testdir/malloc_common.tcl
+set testprefix mallocA
 
 # Only run these tests if memory debugging is turned on.
 #
@@ -40,7 +41,6 @@ db eval {
 db close
 copy_file test.db test.db.bu
 
-
 do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody {
   ANALYZE
 }
@@ -53,6 +53,7 @@ do_malloc_test mallocA-1.2 -testdb test.db.bu -sqlbody {
 do_malloc_test mallocA-1.3 -testdb test.db.bu -sqlbody {
   ANALYZE main.t1
 }
+
 ifcapable reindex {
   do_malloc_test mallocA-2 -testdb test.db.bu -sqlbody {
     REINDEX;
@@ -68,6 +69,35 @@ ifcapable reindex {
   }
 }
 
+reset_db
+sqlite3_db_config_lookaside db 0 0 0
+do_execsql_test 6-prep {
+  CREATE TABLE t1(a, b);
+  CREATE INDEX i1 ON t1(a, b);
+  INSERT INTO t1 VALUES('abc', 'w'); -- rowid=1
+  INSERT INTO t1 VALUES('abc', 'x'); -- rowid=2
+  INSERT INTO t1 VALUES('abc', 'y'); -- rowid=3
+  INSERT INTO t1 VALUES('abc', 'z'); -- rowid=4
+
+  INSERT INTO t1 VALUES('def', 'w'); -- rowid=5
+  INSERT INTO t1 VALUES('def', 'x'); -- rowid=6
+  INSERT INTO t1 VALUES('def', 'y'); -- rowid=7
+  INSERT INTO t1 VALUES('def', 'z'); -- rowid=8
+
+  ANALYZE;
+}
+
+do_faultsim_test 6.1 -faults oom* -body {
+  execsql { SELECT rowid FROM t1 WHERE a='abc' AND b='x' }
+} -test {
+  faultsim_test_result [list 0 2]
+}
+do_faultsim_test 6.2 -faults oom* -body {
+  execsql { SELECT rowid FROM t1 WHERE a='abc' AND b<'y' }
+} -test {
+  faultsim_test_result [list 0 {1 2}]
+}
+
 # Ensure that no file descriptors were leaked.
 do_test malloc-99.X {
   catch {db close}