]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a crash that could occur if the destination database of a backup is
authordan <Dan Kennedy>
Wed, 29 Apr 2026 19:14:54 +0000 (19:14 +0000)
committerdan <Dan Kennedy>
Wed, 29 Apr 2026 19:14:54 +0000 (19:14 +0000)
replaced using sqlite3_deserialize() between the call to sqlite3_backup_init()
and the first call to sqlite3_backup_step(). Forum post [forum:15d82885e2 | 15d82885e2].

FossilOrigin-Name: 1f940357f7bb160b583ac5b08ff4e32a9fef353255d032c5a18bcb04416c0f0b

manifest
manifest.uuid
src/backup.c
test/memdb1.test

index a04e53a9b57a6b39478bb105ccf93badbbc1f32e..123547996d3decbec2b9d17043bc928691f46823 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Attempt\sto\salign\sinstances\sof\sthe\ssqlite3_mutex\sobject\sat\s128-byte\sboundaries\nto\sprevent\sfalse-sharing\sin\smulti-core\smachines.\sSee\sthe\sdiscussion\sat\sand\naround\s[forum:/forumpost/2026-03-25T23:15:03Z|forum\spost\s2026-03-25T23:15:03Z].
-D 2026-04-28T15:12:40.363
+C Fix\sa\scrash\sthat\scould\soccur\sif\sthe\sdestination\sdatabase\sof\sa\sbackup\sis\nreplaced\susing\ssqlite3_deserialize()\sbetween\sthe\scall\sto\ssqlite3_backup_init()\nand\sthe\sfirst\scall\sto\ssqlite3_backup_step().\sForum\spost\s[forum:15d82885e2\s|\s15d82885e2].
+D 2026-04-29T19:14:54.013
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -672,7 +672,7 @@ F src/alter.c 7d7ddbdc189f0e0c686e32ee170abdddc95c11f2089e40df4ffcee88f5334826
 F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
 F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338
 F src/auth.c ebec42df26b34a62b6750d30d9c2c03554a1c522020182476f7729a439fef04f
-F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
+F src/backup.c 95b97c120676e66b5479cd1d512aaf12ab653f7e68f404ccb79f17bf2d37910d
 F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
 F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
 F src/btree.c 216ffbe197e330118a2999adc7d3f09b0e2eeb163df8746ba9a2b27fed3d4335
@@ -1424,7 +1424,7 @@ F test/malloctraceviewer.tcl 3e3ddf11e30d2b20f53aa16aa6615082fb24a100bea61cca721
 F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
 F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
 F test/memdb.test c1f2a343ad14398d5d6debda6ea33e80d0dafcc7
-F test/memdb1.test 916093ae5ad097660a742890a9986d1f217a620ac95efb64884041ee4c408603
+F test/memdb1.test f89ca9b1c1f2482b1098614259106576dcace8b78ccd6d7cdb24edfa836fc05e
 F test/memdb2.test 4ba1fc09e2f51df80d148a540e4a3fa66d0462e91167b27497084de4d1f6b5b4
 F test/memjournal.test 70f3a00c7f84ee2978ad14e831231caa1e7f23915a2c54b4f775a021d5740c6c
 F test/memjournal2.test dbc2c5cb5f7b38950f4f6dc3e73fcecf0fcbed3fc32c7ce913bba164d288da1e
@@ -2203,8 +2203,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P d64a1dbe0fb2d9286806d833a3146b21d5bf1636a650d5a64cf163c7f2356e98
-R e7f5576deb3afe7b12491989f716c8e2
-U drh
-Z 8f2b29d8666e7ca0a228448a291e99d6
+P 1786fcd5b4ee6cd9b4780f3687dfaec5b90ef0476e0da266a94e069b98e70514
+R 5b43b2d170e39f26e9627eaaeadf5925
+U dan
+Z efb7538193cd694216b53a7535e0ddd9
 # Remove this line to create a well-formed Fossil manifest.
index 33b63b5768bc0eb94215e6bcd58663f692bc19e3..88f3ef29069884d7517bb325ce114fa40ec28497 100644 (file)
@@ -1 +1 @@
-1786fcd5b4ee6cd9b4780f3687dfaec5b90ef0476e0da266a94e069b98e70514
+1f940357f7bb160b583ac5b08ff4e32a9fef353255d032c5a18bcb04416c0f0b
index 22615d1499b54ddf00f4ea8b0f83b804ebbc8ceb..ecd569dbc736f81793c40410674b8fb76451848f 100644 (file)
 */
 struct sqlite3_backup {
   sqlite3* pDestDb;        /* Destination database handle */
-  Btree *pDest;            /* Destination b-tree file */
+  Db *pDest;               /* Destination db file */
   u32 iDestSchema;         /* Original schema cookie in destination */
   int bDestLocked;         /* True once a write-transaction is open on pDest */
 
   Pgno iNext;              /* Page number of the next source page to copy */
   sqlite3* pSrcDb;         /* Source database handle */
-  Btree *pSrc;             /* Source b-tree file */
+  Db *pSrc;                /* Source db file */
 
   int rc;                  /* Backup process error code */
 
@@ -79,7 +79,7 @@ struct sqlite3_backup {
 ** function. If an error occurs while doing so, return 0 and write an 
 ** error message to pErrorDb.
 */
-static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
+static Db *findDatabase(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
   int i = sqlite3FindDbName(pDb, zDb);
 
   if( i==1 ){
@@ -102,7 +102,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
     return 0;
   }
 
-  return pDb->aDb[i].pBt;
+  return &pDb->aDb[i];
 }
 
 /*
@@ -110,9 +110,9 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
 ** of the source.
 */
 static int setDestPgsz(sqlite3_backup *p){
-  int rc;
-  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
-  return rc;
+  return sqlite3BtreeSetPageSize(p->pDest->pBt,
+      sqlite3BtreeGetPageSize(p->pSrc->pBt), 0, 0
+  );
 }
 
 /*
@@ -181,15 +181,15 @@ sqlite3_backup *sqlite3_backup_init(
 
   /* If the allocation succeeded, populate the new object. */
   if( p ){
-    p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
-    p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
+    p->pSrc = findDatabase(pDestDb, pSrcDb, zSrcDb);
+    p->pDest = findDatabase(pDestDb, pDestDb, zDestDb);
     p->pDestDb = pDestDb;
     p->pSrcDb = pSrcDb;
     p->iNext = 1;
     p->isAttached = 0;
 
     if( 0==p->pSrc || 0==p->pDest 
-     || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK 
+     || checkReadTransaction(pDestDb, p->pDest->pBt)!=SQLITE_OK 
      ){
       /* One (or both) of the named databases did not exist or an OOM
       ** error was hit. Or there is a transaction open on the destination
@@ -201,7 +201,7 @@ sqlite3_backup *sqlite3_backup_init(
     }
   }
   if( p ){
-    p->pSrc->nBackup++;
+    p->pSrc->pBt->nBackup++;
   }
 
   sqlite3_mutex_leave(pDestDb->mutex);
@@ -229,18 +229,18 @@ static int backupOnePage(
   const u8 *zSrcData,             /* Source database page data */
   int bUpdate                     /* True for an update, false otherwise */
 ){
-  Pager * const pDestPager = sqlite3BtreePager(p->pDest);
-  const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
-  int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
+  Pager * const pDestPager = sqlite3BtreePager(p->pDest->pBt);
+  const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc->pBt);
+  int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest->pBt);
   const int nCopy = MIN(nSrcPgsz, nDestPgsz);
   const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
   int rc = SQLITE_OK;
   i64 iOff;
 
-  assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
+  assert( sqlite3BtreeGetReserveNoMutex(p->pSrc->pBt)>=0 );
   assert( p->bDestLocked );
   assert( !isFatalError(p->rc) );
-  assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
+  assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt->pBt) );
   assert( zSrcData );
   assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 );
 
@@ -251,7 +251,7 @@ static int backupOnePage(
   for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){
     DbPage *pDestPg = 0;
     Pgno iDest = (Pgno)(iOff/nDestPgsz)+1;
-    if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue;
+    if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt->pBt) ) continue;
     if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0))
      && SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg))
     ){
@@ -269,7 +269,7 @@ static int backupOnePage(
       memcpy(zOut, zIn, nCopy);
       ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
       if( iOff==0 && bUpdate==0 ){
-        sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
+        sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc->pBt));
       }
     }
     sqlite3PagerUnref(pDestPg);
@@ -301,8 +301,8 @@ static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){
 */
 static void attachBackupObject(sqlite3_backup *p){
   sqlite3_backup **pp;
-  assert( sqlite3BtreeHoldsMutex(p->pSrc) );
-  pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
+  assert( sqlite3BtreeHoldsMutex(p->pSrc->pBt) );
+  pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc->pBt));
   p->pNext = *pp;
   *pp = p;
   p->isAttached = 1;
@@ -316,20 +316,22 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
   int destMode;       /* Destination journal mode */
   int pgszSrc = 0;    /* Source page size */
   int pgszDest = 0;   /* Destination page size */
+  Btree *pDest = p->pDest->pBt;
+  Btree *pSrc = p->pSrc->pBt;
 
 #ifdef SQLITE_ENABLE_API_ARMOR
   if( p==0 ) return SQLITE_MISUSE_BKPT;
 #endif
   sqlite3_mutex_enter(p->pSrcDb->mutex);
-  sqlite3BtreeEnter(p->pSrc);
+  sqlite3BtreeEnter(pSrc);
   if( p->pDestDb ){
     sqlite3_mutex_enter(p->pDestDb->mutex);
   }
 
   rc = p->rc;
   if( !isFatalError(rc) ){
-    Pager * const pSrcPager = sqlite3BtreePager(p->pSrc);     /* Source pager */
-    Pager * const pDestPager = sqlite3BtreePager(p->pDest);   /* Dest pager */
+    Pager * const pSrcPager = sqlite3BtreePager(pSrc);     /* Source pager */
+    Pager * const pDestPager = sqlite3BtreePager(pDest);   /* Dest pager */
     int ii;                            /* Iterator variable */
     int nSrcPage = -1;                 /* Size of source db in pages */
     int bCloseTrans = 0;               /* True if src db requires unlocking */
@@ -337,7 +339,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
     /* If the source pager is currently in a write-transaction, return
     ** SQLITE_BUSY immediately.
     */
-    if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
+    if( p->pDestDb && pSrc->pBt->inTransaction==TRANS_WRITE ){
       rc = SQLITE_BUSY;
     }else{
       rc = SQLITE_OK;
@@ -347,8 +349,8 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
     ** one now. If a transaction is opened here, then it will be closed
     ** before this function exits.
     */
-    if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){
-      rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0);
+    if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(pSrc) ){
+      rc = sqlite3BtreeBeginTrans(pSrc, 0, 0);
       bCloseTrans = 1;
     }
 
@@ -364,7 +366,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
 
     /* Lock the destination database, if it is not locked already. */
     if( SQLITE_OK==rc && p->bDestLocked==0
-     && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2,
+     && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(pDest, 2,
                                                 (int*)&p->iDestSchema)) 
     ){
       p->bDestLocked = 1;
@@ -372,9 +374,9 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
 
     /* Do not allow backup if the destination database is in WAL mode
     ** and the page sizes are different between source and destination */
-    pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
-    pgszDest = sqlite3BtreeGetPageSize(p->pDest);
-    destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
+    pgszSrc = sqlite3BtreeGetPageSize(pSrc);
+    pgszDest = sqlite3BtreeGetPageSize(pDest);
+    destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(pDest));
     if( SQLITE_OK==rc 
      && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager))
      && pgszSrc!=pgszDest 
@@ -385,11 +387,11 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
     /* Now that there is a read-lock on the source database, query the
     ** source pager for the number of pages in the database.
     */
-    nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc);
+    nSrcPage = (int)sqlite3BtreeLastPage(pSrc);
     assert( nSrcPage>=0 );
     for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){
       const Pgno iSrcPg = p->iNext;                 /* Source page number */
-      if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
+      if( iSrcPg!=PENDING_BYTE_PAGE(pSrc->pBt) ){
         DbPage *pSrcPg;                             /* Source page object */
         rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY);
         if( rc==SQLITE_OK ){
@@ -416,18 +418,18 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
     */
     if( rc==SQLITE_DONE ){
       if( nSrcPage==0 ){
-        rc = sqlite3BtreeNewDb(p->pDest);
+        rc = sqlite3BtreeNewDb(pDest);
         nSrcPage = 1;
       }
       if( rc==SQLITE_OK || rc==SQLITE_DONE ){
-        rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
+        rc = sqlite3BtreeUpdateMeta(pDest,1,p->iDestSchema+1);
       }
       if( rc==SQLITE_OK ){
         if( p->pDestDb ){
           sqlite3ResetAllSchemasOfConnection(p->pDestDb);
         }
         if( destMode==PAGER_JOURNALMODE_WAL ){
-          rc = sqlite3BtreeSetVersion(p->pDest, 2);
+          rc = sqlite3BtreeSetVersion(pDest, 2);
         }
       }
       if( rc==SQLITE_OK ){
@@ -444,12 +446,12 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
         ** journalled by PagerCommitPhaseOne() before they are destroyed
         ** by the file truncation.
         */
-        assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
-        assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
+        assert( pgszSrc==sqlite3BtreeGetPageSize(pSrc) );
+        assert( pgszDest==sqlite3BtreeGetPageSize(pDest) );
         if( pgszSrc<pgszDest ){
           int ratio = pgszDest/pgszSrc;
           nDestTruncate = (nSrcPage+ratio-1)/ratio;
-          if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
+          if( nDestTruncate==(int)PENDING_BYTE_PAGE(pDest->pBt) ){
             nDestTruncate--;
           }
         }else{
@@ -477,7 +479,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
           assert( pFile );
           assert( nDestTruncate==0 
               || (i64)nDestTruncate*(i64)pgszDest >= iSize || (
-                nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
+                nDestTruncate==(int)(PENDING_BYTE_PAGE(pDest->pBt)-1)
              && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
           ));
 
@@ -489,7 +491,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
           ** journal file.  */
           sqlite3PagerPagecount(pDestPager, &nDstPage);
           for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
-            if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
+            if( iPg!=PENDING_BYTE_PAGE(pDest->pBt) ){
               DbPage *pPg;
               rc = sqlite3PagerGet(pDestPager, iPg, &pPg, 0);
               if( rc==SQLITE_OK ){
@@ -533,7 +535,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
     
         /* Finish committing the transaction to the destination database. */
         if( SQLITE_OK==rc
-         && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
+         && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(pDest, 0))
         ){
           rc = SQLITE_DONE;
         }
@@ -547,8 +549,8 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
     */
     if( bCloseTrans ){
       TESTONLY( int rc2 );
-      TESTONLY( rc2  = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
-      TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
+      TESTONLY( rc2  = ) sqlite3BtreeCommitPhaseOne(pSrc, 0);
+      TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(pSrc, 0);
       assert( rc2==SQLITE_OK );
     }
   
@@ -560,7 +562,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
   if( p->pDestDb ){
     sqlite3_mutex_leave(p->pDestDb->mutex);
   }
-  sqlite3BtreeLeave(p->pSrc);
+  sqlite3BtreeLeave(pSrc);
   sqlite3_mutex_leave(p->pSrcDb->mutex);
   return rc;
 }
@@ -577,17 +579,17 @@ int sqlite3_backup_finish(sqlite3_backup *p){
   if( p==0 ) return SQLITE_OK;
   pSrcDb = p->pSrcDb;
   sqlite3_mutex_enter(pSrcDb->mutex);
-  sqlite3BtreeEnter(p->pSrc);
+  sqlite3BtreeEnter(p->pSrc->pBt);
   if( p->pDestDb ){
     sqlite3_mutex_enter(p->pDestDb->mutex);
   }
 
   /* Detach this backup from the source pager. */
   if( p->pDestDb ){
-    p->pSrc->nBackup--;
+    p->pSrc->pBt->nBackup--;
   }
   if( p->isAttached ){
-    pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
+    pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc->pBt));
     assert( pp!=0 );
     while( *pp!=p ){
       pp = &(*pp)->pNext;
@@ -597,7 +599,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
   }
 
   /* If a transaction is still open on the Btree, roll it back. */
-  sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
+  sqlite3BtreeRollback(p->pDest->pBt, SQLITE_OK, 0);
 
   /* Set the error code of the destination database handle. */
   rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
@@ -607,7 +609,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
     /* Exit the mutexes and free the backup context structure. */
     sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
   }
-  sqlite3BtreeLeave(p->pSrc);
+  sqlite3BtreeLeave(p->pSrc->pBt);
   if( p->pDestDb ){
     /* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
     ** call to sqlite3_backup_init() and is destroyed by a call to
@@ -665,7 +667,7 @@ static SQLITE_NOINLINE void backupUpdate(
 ){
   assert( p!=0 );
   do{
-    assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
+    assert( sqlite3_mutex_held(p->pSrc->pBt->pBt->mutex) );
     if( !isFatalError(p->rc) && iPage<p->iNext ){
       /* The backup process p has already copied page iPage. But now it
       ** has been modified by a transaction on the source pager. Copy
@@ -701,7 +703,7 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
 void sqlite3BackupRestart(sqlite3_backup *pBackup){
   sqlite3_backup *p;                   /* Iterator variable */
   for(p=pBackup; p; p=p->pNext){
-    assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
+    assert( sqlite3_mutex_held(p->pSrc->pBt->pBt->mutex) );
     p->iNext = 1;
   }
 }
@@ -719,6 +721,8 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
   int rc;
   sqlite3_file *pFd;              /* File descriptor for database pTo */
   sqlite3_backup b;
+  Db dbDest;
+  Db dbSrc;
   sqlite3BtreeEnter(pTo);
   sqlite3BtreeEnter(pFrom);
 
@@ -737,9 +741,13 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
   ** from this function, not directly by the user.
   */
   memset(&b, 0, sizeof(b));
+  memset(&dbDest, 0, sizeof(dbDest));
+  memset(&dbSrc, 0, sizeof(dbSrc));
+  dbDest.pBt = pTo;
+  dbSrc.pBt = pFrom;
   b.pSrcDb = pFrom->db;
-  b.pSrc = pFrom;
-  b.pDest = pTo;
+  b.pSrc = &dbSrc;
+  b.pDest = &dbDest;
   b.iNext = 1;
 
   /* 0x7FFFFFFF is the hard limit for the number of pages in a database
@@ -755,7 +763,7 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
   if( rc==SQLITE_OK ){
     pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
   }else{
-    sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
+    sqlite3PagerClearCache(sqlite3BtreePager(pTo));
   }
 
   assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE );
index 0c752ca50a65aea13cec6f946ec6adb8153215c4..e1ae571d81037e7c65b524e40f4a427b8e6cf608 100644 (file)
@@ -317,4 +317,27 @@ do_test 1020 {
   set res
 } {1 {unable to set MEMDB content}}
 
+#-------------------------------------------------------------------------
+# Check that things work if the destination database of a backup is
+# overwritten using sqlite3_deserialize() between sqlite3_backup_init()
+# and the first call to sqlite3_backup_step(). Forum post 15d82885e2.
+#
+reset_db
+do_execsql_test 1100 {
+  CREATE TABLE t(x); 
+  INSERT INTO t VALUES(1),(2);
+}
+
+set blob [db serialize main]
+
+forcedelete test.db2
+sqlite3 db2 test.db2
+do_test 1110 {
+  set seen 0
+  sqlite3_backup B db2 main db main
+  db2 deserialize main $blob
+  B step 2
+  B finish
+} {SQLITE_OK}
+
 finish_test