From: dan Date: Tue, 23 Jun 2026 20:28:51 +0000 (+0000) Subject: Merge the fixes for the use-after-free problem caused by executing an ATTACH when... X-Git-Tag: release~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b9e0f62c3a65fc32cbcb0e933e7669444b131943;p=thirdparty%2Fsqlite.git Merge the fixes for the use-after-free problem caused by executing an ATTACH when a backup operation was active, and for the OOB read in the unused spellfix1 extension. FossilOrigin-Name: 121a749d0f3d54eef7f35d527f0eed9a2531ac741452ed85d4c49556df8955ea --- diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c index a132742a2c..f9d9e529ed 100644 --- a/ext/misc/spellfix.c +++ b/ext/misc/spellfix.c @@ -2604,7 +2604,7 @@ static int spellfix1FilterForMatch( goto filter_exit; } nPattern = (int)strlen(zPattern); - if( zPattern[nPattern-1]=='*' ) nPattern--; + if( nPattern>0 && zPattern[nPattern-1]=='*' ) nPattern--; zSql = sqlite3_mprintf( "SELECT id, word, rank, coalesce(k1,word)" " FROM \"%w\".\"%w_vocab\"" diff --git a/manifest b/manifest index 75239d1713..9510765f83 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\suse\sshared\slocks\son\sSHM\sfiles\swhen\sthe\sfilename\sis\sa\slong\sDOS-device\npath.\s\sOnly\sused\sshared\slocks\sfor\sUNC\spaths.\s\sDo\snot\sconfuse\sthe\nlong-DOS-device\ssyntax\swith\sUNC\spaths.\n[https://bugzilla.mozilla.org/show_bug.cgi?id=2016438|Bugzilla\s2016438]. -D 2026-06-23T15:16:15.963 +C Merge\sthe\sfixes\sfor\sthe\suse-after-free\sproblem\scaused\sby\sexecuting\san\sATTACH\swhen\sa\sbackup\soperation\swas\sactive,\sand\sfor\sthe\sOOB\sread\sin\sthe\sunused\sspellfix1\sextension. +D 2026-06-23T20:28:51.931 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -397,7 +397,7 @@ F ext/misc/series.c 8612317330587e4cfeb0ae7ffe2f06fe86e66f94a0f5bbb80d8fb46f89e9 F ext/misc/sha1.c 8bf60344c11a525384c2efd1ae77f160b06be336db679effaadf292d4b41451c F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e389826993df F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 -F ext/misc/spellfix.c e9e951f9712b6c302e4ee84f5db5a7b18daab87aa229867c66f34684d2dfbb40 +F ext/misc/spellfix.c a262267b16f04d30a2896cc1ba644570cbdb55446951f97d026f4e062bd20291 F ext/misc/sqlar.c 97c100b010159c08a7a9acd8eb1ea510a5522e64741aaafcd7b6c629de682edc F ext/misc/sqlite3_stdio.c b43a0f530c6f0fb3d41d9af8c0b40f3f71198a1db55ab8ffffbef5c8cc329d22 F ext/misc/sqlite3_stdio.h 27a4ecea47e61bc9574ccdf2806f468afe23af2f95028c9b689bfa08ab1ce99f @@ -672,11 +672,11 @@ F src/alter.c d7cbe4332a7a2c3a21ab58ee2609efade493f6ae78254613ada0759898629ee3 F src/analyze.c 42ed904f4a81d0e4f32491e10ff8ea3cdf2f98a29b04a015f0a29046b32b4e0f F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338 F src/auth.c ebec42df26b34a62b6750d30d9c2c03554a1c522020182476f7729a439fef04f -F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523 +F src/backup.c 2f5f350c714407a04fadc9c812b8fcbb47d472c543c492f9fc03043c94255406 F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399 F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea F src/btree.c 377b98fbe170ae38298a3fcea5355ec88404f63238f80741992cda765ac91aeb -F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0 +F src/btree.h db9d500f049addf08ab5c7df1b5e7b54513c614def9f0b129085d1bc7b692e33 F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886 F src/build.c 8581de0af3b6c448f5d64e2d18a91ac1e7057b3bcb8b8827e1240f80d87486a4 F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad @@ -801,7 +801,7 @@ F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf F src/upsert.c dd9f0fcccbfb4f20e1026a21a7254ba3f2c08e9cfa92affaff5b5ec3b00ea549 F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165 F src/util.c 2053344769bc602b2c23ce193f4d24f89777ab67fa768b80fa197d3876dc3017 -F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82 +F src/vacuum.c 4eda73a75a15f939b45b26e189f5b25757b39d7cb6d5e972a7f754e75d019189 F src/vdbe.c 6c57525d7db0232d52687d30da1093db0c152f14206c2ef1adf0c19a09d863e3 F src/vdbe.h 70e862ac8a11b590f8c1eaac17a0078429d42bc4ea3f757a9af0f451dd966a71 F src/vdbeInt.h c31ba4dc8d280c2b1dc89c6fcee68f2555e3813ab34279552c20b964c0e338b1 @@ -897,7 +897,7 @@ F test/backcompat.test f2431465ed668f09fc3f6998e56e893a1506ccea6e8b6f409f085f759 F test/backup.test 3b08fd4af69f0fa786931103a31f4542b184aba16e239e5f22b18c3c2476697f F test/backup2.test b153553ee5667b0748b43346b0725fbf80ce1f5544613bf087d669778b60ec56 F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32 -F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4 +F test/backup5.test 3e16065dc45c40030378702aac3e92f4d6e7d8853f0328f0390c9e7a71d8c712 F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135 F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f @@ -1669,7 +1669,7 @@ F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d F test/speedtest.md ea0c85ebe0ecff8b45ba6cdb26e694871f469009a5a29dcfe634b055f05ab241 F test/speedtest.tcl b06f6321ef90bb68f18f7b0e430e25203d9da79b80f8926986a0d5f21ac485fb x F test/speedtest1.c 6c01252e66f46de0b6b8d5316e03521e2151782104f3608c10262aa5dce85721 -F test/spellfix.test 7f7770ee8bc738fb0dab182d9bb4ee998e3982e534bdffd1a176c08bafec50f1 +F test/spellfix.test 8d674c2d4776792cdd982c5a8bbbfd433c23bc98f00127cae83437f5d693e018 F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 F test/spellfix4.test 7c56a09de68b95051ef0627fe235e514efbba2aa816dc2a9c5c670d98877e632 @@ -2200,9 +2200,9 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 7e8de3ba28224a293fd1c220c3bf7ac5f721a01125debc5dce39f16745c43c17 -Q +a4691489f21bb1645bb8de649268dd457af53c5f613f2a7fd2e5569f029efa87 -R f32f630554fffe9714d0c0898e8775a7 -U drh -Z 617e301cc83bca882542cff9ec92bc6c +P 246f46614fbdbc2609ee1c6fffe446abd6c0121c3e626a1776bf7e09759bc973 +Q +e458ed9cc9d46dfdade3b4e99eb209d8aefd40a245e002d68df1813f9f99e2d7 +R e83eeb82efe8ea8d6502efa2498da6db +U dan +Z b6c0ff2c6c6614bdfc01f0186668d841 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2f4644c3ee..f6b707fffa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -246f46614fbdbc2609ee1c6fffe446abd6c0121c3e626a1776bf7e09759bc973 +121a749d0f3d54eef7f35d527f0eed9a2531ac741452ed85d4c49556df8955ea diff --git a/src/backup.c b/src/backup.c index 22615d1499..0a20ae4cf9 100644 --- a/src/backup.c +++ b/src/backup.c @@ -20,13 +20,13 @@ */ struct sqlite3_backup { sqlite3* pDestDb; /* Destination database handle */ - Btree *pDest; /* Destination b-tree file */ + int iDest; /* 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 */ + int iSrc; /* Source db file */ int rc; /* Backup process error code */ @@ -40,6 +40,20 @@ struct sqlite3_backup { sqlite3_backup *pNext; /* Next backup associated with source pager */ }; +/* +** Return the source (Btree*) used by backup object b. +*/ +#define getSourceBtree(b) ((b)->pSrcDb->aDb[(b)->iSrc].pBt) + +/* +** Return the destination (Btree*) used by backup object b. +*/ +#define getDestBtree(b) ( (b)->pDestDb ? \ + (b)->pDestDb->aDb[(b)->iDest].pBt : \ + (b)->pSrcDb->aDb[(b)->iDest].pBt \ +) + + /* ** THREAD SAFETY NOTES: ** @@ -71,15 +85,15 @@ struct sqlite3_backup { */ /* -** Return a pointer corresponding to database zDb (i.e. "main", "temp") +** Return an integer corresponding to database zDb (i.e. "main", "temp") ** in connection handle pDb. If such a database cannot be found, return -** a NULL pointer and write an error message to pErrorDb. +** a negative value and write an error message to pErrorDb. ** ** If the "temp" database is requested, it may need to be opened by this -** function. If an error occurs while doing so, return 0 and write an +** function. If an error occurs while doing so, return -1 and write an ** error message to pErrorDb. */ -static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ +static int findDatabase(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ int i = sqlite3FindDbName(pDb, zDb); if( i==1 ){ @@ -93,16 +107,15 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ sqlite3DbFree(pErrorDb, sParse.zErrMsg); sqlite3ParseObjectReset(&sParse); if( rc ){ - return 0; + return -1; } } if( i<0 ){ sqlite3ErrorWithMsg(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); - return 0; } - return pDb->aDb[i].pBt; + return i; } /* @@ -110,9 +123,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(getDestBtree(p), + sqlite3BtreeGetPageSize(getSourceBtree(p)), 0, 0 + ); } /* @@ -181,15 +194,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->iDest = findDatabase(pDestDb, pDestDb, zDestDb); + p->iSrc = findDatabase(pDestDb, pSrcDb, zSrcDb); 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 + if( p->iSrc<0 || p->iDest<0 + || checkReadTransaction(pDestDb, getDestBtree(p))!=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 +214,7 @@ sqlite3_backup *sqlite3_backup_init( } } if( p ){ - p->pSrc->nBackup++; + getSourceBtree(p)->nBackup++; } sqlite3_mutex_leave(pDestDb->mutex); @@ -229,18 +242,19 @@ 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); + Btree *pDestBt = getDestBtree(p); + Pager * const pDestPager = sqlite3BtreePager(pDestBt); + const int nSrcPgsz = sqlite3BtreeGetPageSize(getSourceBtree(p)); + int nDestPgsz = sqlite3BtreeGetPageSize(pDestBt); 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(getSourceBtree(p))>=0 ); assert( p->bDestLocked ); assert( !isFatalError(p->rc) ); - assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); + assert( iSrcPg!=PENDING_BYTE_PAGE(getSourceBtree(p)->pBt) ); assert( zSrcData ); assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); @@ -251,7 +265,7 @@ static int backupOnePage( for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOffpDest->pBt) ) continue; + if( iDest==PENDING_BYTE_PAGE(pDestBt->pBt) ) continue; if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0)) && SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg)) ){ @@ -269,7 +283,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(getSourceBtree(p))); } } sqlite3PagerUnref(pDestPg); @@ -301,8 +315,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(getSourceBtree(p)) ); + pp = sqlite3PagerBackupPtr(sqlite3BtreePager(getSourceBtree(p))); p->pNext = *pp; *pp = p; p->isAttached = 1; @@ -316,20 +330,26 @@ 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; + Btree *pSrc; #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ) return SQLITE_MISUSE_BKPT; #endif + assert( p->iDest>=0 ); + assert( p->iSrc>=0 ); + pDest = getDestBtree(p); + pSrc = getSourceBtree(p); 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 +357,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 +367,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 +384,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 +392,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 +405,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 || iiiNext<=(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 +436,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 +464,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( pgszSrcpDest->pBt) ){ + if( nDestTruncate==(int)PENDING_BYTE_PAGE(pDest->pBt) ){ nDestTruncate--; } }else{ @@ -477,7 +497,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 +509,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 +553,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 +567,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 +580,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; } @@ -572,22 +592,24 @@ int sqlite3_backup_finish(sqlite3_backup *p){ sqlite3_backup **pp; /* Ptr to head of pagers backup list */ sqlite3 *pSrcDb; /* Source database connection */ int rc; /* Value to return */ + Btree *pSrcBt = 0; /* Enter the mutexes */ if( p==0 ) return SQLITE_OK; + pSrcBt = getSourceBtree(p); pSrcDb = p->pSrcDb; sqlite3_mutex_enter(pSrcDb->mutex); - sqlite3BtreeEnter(p->pSrc); + sqlite3BtreeEnter(pSrcBt); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } /* Detach this backup from the source pager. */ if( p->pDestDb ){ - p->pSrc->nBackup--; + pSrcBt->nBackup--; } if( p->isAttached ){ - pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); + pp = sqlite3PagerBackupPtr(sqlite3BtreePager(pSrcBt)); assert( pp!=0 ); while( *pp!=p ){ pp = &(*pp)->pNext; @@ -597,7 +619,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(getDestBtree(p), SQLITE_OK, 0); /* Set the error code of the destination database handle. */ rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; @@ -607,7 +629,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){ /* Exit the mutexes and free the backup context structure. */ sqlite3LeaveMutexAndCloseZombie(p->pDestDb); } - sqlite3BtreeLeave(p->pSrc); + sqlite3BtreeLeave(pSrcBt); 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 +687,7 @@ static SQLITE_NOINLINE void backupUpdate( ){ assert( p!=0 ); do{ - assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); + assert( sqlite3_mutex_held(getSourceBtree(p)->pBt->mutex) ); if( !isFatalError(p->rc) && iPageiNext ){ /* 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 +723,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(getSourceBtree(p)->pBt->mutex) ); p->iNext = 1; } } @@ -715,10 +737,12 @@ void sqlite3BackupRestart(sqlite3_backup *pBackup){ ** goes wrong, the transaction on pTo is rolled back. If successful, the ** transaction is committed before returning. */ -int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ +int sqlite3BtreeCopyFile(sqlite3 *db, int iTo, int iFrom){ int rc; sqlite3_file *pFd; /* File descriptor for database pTo */ sqlite3_backup b; + Btree *pFrom = db->aDb[iFrom].pBt; + Btree *pTo = db->aDb[iTo].pBt; sqlite3BtreeEnter(pTo); sqlite3BtreeEnter(pFrom); @@ -737,9 +761,9 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ ** from this function, not directly by the user. */ memset(&b, 0, sizeof(b)); - b.pSrcDb = pFrom->db; - b.pSrc = pFrom; - b.pDest = pTo; + b.pSrcDb = db; + b.iSrc = iFrom; + b.iDest = iTo; b.iNext = 1; /* 0x7FFFFFFF is the hard limit for the number of pages in a database @@ -755,7 +779,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 ); diff --git a/src/btree.h b/src/btree.h index 96f4c4c607..e8a2bea716 100644 --- a/src/btree.h +++ b/src/btree.h @@ -105,7 +105,7 @@ int sqlite3BtreeSavepoint(Btree *, int, int); const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); -int sqlite3BtreeCopyFile(Btree *, Btree *); +int sqlite3BtreeCopyFile(sqlite3*, int, int); int sqlite3BtreeIncrVacuum(Btree *); diff --git a/src/vacuum.c b/src/vacuum.c index 70e62e1ef1..94de364de7 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -376,7 +376,9 @@ SQLITE_NOINLINE int sqlite3RunVacuum( } if( pOut==0 ){ - rc = sqlite3BtreeCopyFile(pMain, pTemp); + assert( pMain==db->aDb[iDb].pBt ); + assert( pTemp==db->aDb[nDb].pBt ); + rc = sqlite3BtreeCopyFile(db, iDb, nDb); } if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pTemp); diff --git a/test/backup5.test b/test/backup5.test index c789adfa69..e68a258c3d 100644 --- a/test/backup5.test +++ b/test/backup5.test @@ -62,4 +62,40 @@ do_test 1.7 { sqlite3_errmsg db2 } {no such table: t2} +#------------------------------------------------------------------------- +reset_db + +forcedelete test2.db + +do_execsql_test 2.0 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + INSERT INTO t2 VALUES(1, 1); + INSERT INTO t2 VALUES(2, 2); + INSERT INTO t2 VALUES(3, 3); + CREATE INDEX i1 ON t1(a); + CREATE INDEX i2 ON t2(a); + ATTACH 'test.db2' AS aux2; +} + +sqlite3 db2 test2.db + +do_test 2.1 { + sqlite3_backup B db2 main db main + B step 1 + + execsql { + ATTACH 'test.db3' AS aux3; + ATTACH 'test.db4' AS aux4; + ATTACH 'test.db5' AS aux5; + ATTACH 'test.db6' AS aux6; + ATTACH 'test.db7' AS aux7; + ATTACH 'test.db8' AS aux8; + } + + B step 200 + B finish +} {SQLITE_OK} + + finish_test diff --git a/test/spellfix.test b/test/spellfix.test index 82c578239d..dfcee1ac9a 100644 --- a/test/spellfix.test +++ b/test/spellfix.test @@ -415,4 +415,14 @@ do_execsql_test 8.1 { 'substr(printf(''%.*c'',2000000,''A''),1,if(abs(random())%2=0,1000001,1))')))>0 FROM cnt; } {1} + +# Bug 2026-06-23T18:48:40Z +# +do_execsql_test 8.2 { + DROP TABLE IF EXISTS t1; + CREATE VIRTUAL TABLE t1 USING spellfix1; + INSERT INTO t1(word) VALUES('hello'),('world'); + SELECT word FROM t1 WHERE word MATCH ''; +} {hello world} + finish_test