diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 39a3dc35c1..0b567ae45f 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -1611,6 +1611,10 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ int iDocidLe = -1; /* Index of docid<=x constraint, if present */ int iIdx; + if( p->bLock ){ + return SQLITE_ERROR; + } + /* By default use a full table scan. This is an expensive option, ** so search through the constraints to see if a more efficient ** strategy is possible. @@ -1809,7 +1813,11 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){ }else{ zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); if( !zSql ) return SQLITE_NOMEM; - rc = sqlite3_prepare_v3(p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0); + p->bLock++; + rc = sqlite3_prepare_v3( + p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 + ); + p->bLock--; sqlite3_free(zSql); } if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1; @@ -1827,11 +1835,15 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ if( pCsr->isRequireSeek ){ rc = fts3CursorSeekStmt(pCsr); if( rc==SQLITE_OK ){ + Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab; + pTab->bLock++; sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); pCsr->isRequireSeek = 0; if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ + pTab->bLock--; return SQLITE_OK; }else{ + pTab->bLock--; rc = sqlite3_reset(pCsr->pStmt); if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){ /* If no row was found and no error has occurred, then the %_content @@ -3218,6 +3230,8 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ int rc; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){ + Fts3Table *pTab = (Fts3Table*)pCursor->pVtab; + pTab->bLock++; if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ pCsr->isEof = 1; rc = sqlite3_reset(pCsr->pStmt); @@ -3225,6 +3239,7 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0); rc = SQLITE_OK; } + pTab->bLock--; }else{ rc = fts3EvalNext((Fts3Cursor *)pCursor); } @@ -3285,6 +3300,10 @@ static int fts3FilterMethod( UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); + if( p->bLock ){ + return SQLITE_ERROR; + } + eSearch = (idxNum & 0x0000FFFF); assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( p->pSegments==0 ); @@ -3356,7 +3375,11 @@ static int fts3FilterMethod( ); } if( zSql ){ - rc = sqlite3_prepare_v3(p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0); + p->bLock++; + rc = sqlite3_prepare_v3( + p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 + ); + p->bLock--; sqlite3_free(zSql); }else{ rc = SQLITE_NOMEM; diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 3833e067e4..26b23b66f2 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -239,6 +239,7 @@ struct Fts3Table { char *zLanguageid; /* languageid=xxx option, or NULL */ int nAutoincrmerge; /* Value configured by 'automerge' */ u32 nLeafAdd; /* Number of leaf blocks added this trans */ + int bLock; /* Used to prevent recursive content= tbls */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index a0450a6e4f..d0cfcbe413 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -895,15 +895,24 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ break; } - default: + default: { + Fts5Config *pConfig = ((Fts5Table*)pCursor->pVtab)->pConfig; + pConfig->bLock++; rc = sqlite3_step(pCsr->pStmt); + pConfig->bLock--; if( rc!=SQLITE_ROW ){ CsrFlagSet(pCsr, FTS5CSR_EOF); rc = sqlite3_reset(pCsr->pStmt); + if( rc!=SQLITE_OK ){ + pCursor->pVtab->zErrMsg = sqlite3_mprintf( + "%s", sqlite3_errmsg(pConfig->db) + ); + } }else{ rc = SQLITE_OK; } break; + } } } @@ -1188,6 +1197,13 @@ static int fts5FilterMethod( int iIdxStr = 0; Fts5Expr *pExpr = 0; + if( pConfig->bLock ){ + pTab->p.base.zErrMsg = sqlite3_mprintf( + "recursively defined fts5 content table" + ); + return SQLITE_ERROR; + } + if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); @@ -1408,10 +1424,13 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ } if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){ + Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); assert( pCsr->pExpr ); sqlite3_reset(pCsr->pStmt); sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr)); + pTab->pConfig->bLock++; rc = sqlite3_step(pCsr->pStmt); + pTab->pConfig->bLock--; if( rc==SQLITE_ROW ){ rc = SQLITE_OK; CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT); @@ -1419,6 +1438,10 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ rc = sqlite3_reset(pCsr->pStmt); if( rc==SQLITE_OK ){ rc = FTS5_CORRUPT; + }else{ + pTab->base.zErrMsg = sqlite3_mprintf( + "%s", sqlite3_errmsg(pTab->pConfig->db) + ); } } } diff --git a/ext/fts5/test/fts5content.test b/ext/fts5/test/fts5content.test index bd510a552d..74a74e2ad0 100644 --- a/ext/fts5/test/fts5content.test +++ b/ext/fts5/test/fts5content.test @@ -257,16 +257,41 @@ do_execsql_test 6.2 { # Check that an fts5 table cannot be its own content table. # reset_db -do_execsql_test 7.1 { +do_execsql_test 7.1.1 { CREATE VIRTUAL TABLE t1 USING fts5(a, c=t1 ); INSERT INTO t1( a ) VALUES('abc'); } -do_catchsql_test 7.2 { +do_catchsql_test 7.1.2 { SELECT * FROM t1; } {1 {recursively defined fts5 content table}} -do_catchsql_test 7.3 { +do_catchsql_test 7.1.3 { SELECT * FROM t1('abc'); } {1 {recursively defined fts5 content table}} +do_catchsql_test 7.1.4 { + SELECT count(*) FROM t1; +} {1 {recursively defined fts5 content table}} +do_catchsql_test 7.1.5 { + SELECT * FROM t1('abc') ORDER BY rank; +} {1 {recursively defined fts5 content table}} + +reset_db +do_execsql_test 7.2.1 { + CREATE VIRTUAL TABLE t1 USING fts5(a, c=t2 ); + CREATE VIRTUAL TABLE t2 USING fts5(a, c=t1 ); + INSERT INTO t1( a ) VALUES('abc'); +} +do_catchsql_test 7.2.2 { + SELECT * FROM t1; +} {1 {recursively defined fts5 content table}} +do_catchsql_test 7.2.3 { + SELECT * FROM t1('abc'); +} {1 {recursively defined fts5 content table}} +do_catchsql_test 7.2.4 { + SELECT count(*) FROM t1; +} {1 {recursively defined fts5 content table}} +do_catchsql_test 7.2.5 { + SELECT * FROM t1('abc') ORDER BY rank; +} {1 {recursively defined fts5 content table}} finish_test diff --git a/manifest b/manifest index 0e0e65a3e2..d2b8a71850 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\suse-after-free\sproblem\sin\sthe\stest\ssuite.\sNo\schanges\sto\sproduction\scode. -D 2019-11-20T16:10:40.296 +C Prevent\sdirect\sand\sindirect\srecursive\scontent=\soptions\sin\sfts3/4\sand\sfts5. +D 2019-11-21T14:20:59.705 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -81,9 +81,9 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f39fa93a2e422d F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 9c2ac0443f4207e523e0b26046553b28c3e1234d867a19ac477504f37f3875be +F ext/fts3/fts3.c f2e6743c765488b727b6186dcefd6dcfa2da72a28cb67152849285a7b32ade78 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h 7201d419716896951044f980350f0ccb376fdc15932ff1a81f0d43f5e7051a45 +F ext/fts3/fts3Int.h ba4e5445a0daf4a251345d4704259268fd98a3e0282fedfcaedb9fc6da9f250c F ext/fts3/fts3_aux.c 96708c8b3a7d9b8ca1b68ea2b7e503e283f20e95f145becadedfad096dbd0f34 F ext/fts3/fts3_expr.c b132af223e90e35b9f9efa9fe63d6ae737d34153a3b6066736086df8abc78a1f F ext/fts3/fts3_hash.c 8b6e31bfb0844c27dc6092c2620bdb1fca17ed613072db057d96952c6bdb48b7 @@ -117,7 +117,7 @@ F ext/fts5/fts5_config.c aab6117f8f85933e051c66f227414fdcaf7f2313688e16276b895f9 F ext/fts5/fts5_expr.c 5661fe64f4f5a499710df9561075de84b743f01e808af46df4130a9ec343a0fd F ext/fts5/fts5_hash.c 1cc0095646f5f3b46721aa112fb4f9bf29ae175cb5338f89dcec66ed97acfe75 F ext/fts5/fts5_index.c 99b77ae1f503978ca76985bcfff7345c822aed8bbaa8edb3747f804f614685b5 -F ext/fts5/fts5_main.c 1b2d41fd7cc2e8277f60e4156826f41fe5d6b1ccc2e54d70450883ab2ca697d2 +F ext/fts5/fts5_main.c dad126d1dede87df748c65b389ec67edcc803d939980b55a0280386b54fd7e7b F ext/fts5/fts5_storage.c 167e3d8f8052a71032d498e32a2f2ed5ffe489e5d4d47e298adfa02ed55c7882 F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee @@ -154,7 +154,7 @@ F ext/fts5/test/fts5columnsize.test 45459ce4dd9fd853b6044cdc9674921bff89e3d840f3 F ext/fts5/test/fts5config.test 60094712debc59286c59aef0e6cf511c37d866802776a825ce437d26afe0817f F ext/fts5/test/fts5conflict.test 655925678e630d3cdf145d18725a558971806416f453ac8410ca8c04d934238d F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c073e19b3ae9126b2f4 -F ext/fts5/test/fts5content.test 9517cc527a8e8a33949652d5c7b5e251f8c3d5ae3f23f01d4320e30f29a0336b +F ext/fts5/test/fts5content.test 213506436fb2c87567b8e31f6d43ab30aab99354cec74ed679f22aad0cdbf283 F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519deb43581cb17a57ebe F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f F ext/fts5/test/fts5corrupt3.test 4100ac398eefbf033ebdf5a5a9399b37b4f8e1285fa35ab9d0119d4c7f21dc0c @@ -975,7 +975,7 @@ F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d F test/fts3varint.test 0b84a3fd4eba8a39f3687523804d18f3b322e6d4539a55bf342079c3614f2ada F test/fts4aa.test 4338ea7a67f7e19269bf6e6fb4a291352aa32296e7daed87f9823d57016a1ef7 F test/fts4check.test 6259f856604445d7b684c9b306b2efb6346834c3f50e8fc4a59a2ca6d5319ad0 -F test/fts4content.test 1518195a9f92b711d94419f76409a31cc78755854fb0abb1da2b74b9e0cf843e +F test/fts4content.test 73bbb123420d2c46ef2fb3b24761e9acdb78b0877179d3a5d7d57aada08066f6 F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01 F test/fts4growth.test 289833c34ad45a5e6e6133b53b6a71647231fb89d36ddcb8d9c87211b6721d7f F test/fts4growth2.test 13ad4e76451af6e6906c95cdc725d01b00044269 @@ -1850,7 +1850,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a0f6d526baecd061a5e2bec5eb698fb5dfb10122ac79c853d7b3f4a48bc9f49b -R f8d336959087d4a7ae5775d40c8495c8 +P 0d1055a5da8274a59f35170b1f9469597ce4c5a4c5e851a1b81d3be2ccd32871 +R 200f7549382b29efd6fcb95f943bb744 U dan -Z a7988e13d0d9940f3811a9b074dcc031 +Z 37f61c8ae9fc951a55a1a29cfd01745c diff --git a/manifest.uuid b/manifest.uuid index ef54236abb..abafc575bf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0d1055a5da8274a59f35170b1f9469597ce4c5a4c5e851a1b81d3be2ccd32871 \ No newline at end of file +2eb997327c2c369c10b7835be80112d77cb2a7cfa9fe4d564042984c21d94698 \ No newline at end of file diff --git a/test/fts4content.test b/test/fts4content.test index 2da51d1516..980586ea3a 100644 --- a/test/fts4content.test +++ b/test/fts4content.test @@ -634,5 +634,41 @@ do_catchsql_test 11.1 { CREATE VIRTUAL TABLE x1 USING fts4(content=x1); } {1 {vtable constructor called recursively: x1}} +#--------------------------------------------------------------------------- +# Check that an fts4 table cannot be its own content table. +# +reset_db +breakpoint +do_execsql_test 12.1.1 { + CREATE VIRTUAL TABLE t1 USING fts4(a, content=t1 ); + INSERT INTO t1(rowid, a) VALUES(1, 'abc'); +} +do_catchsql_test 12.1.2 { + SELECT * FROM t1; +} {1 {SQL logic error}} +do_catchsql_test 12.1.3 { + SELECT * FROM t1('abc'); +} {1 {SQL logic error}} +do_catchsql_test 12.1.4 { + SELECT count(*) FROM t1; +} {1 {SQL logic error}} + +reset_db +do_execsql_test 12.2.1 { + CREATE VIRTUAL TABLE t1 USING fts4(a, content=t2 ); + CREATE VIRTUAL TABLE t2 USING fts4(a, content=t1 ); + INSERT INTO t1(rowid, a) VALUES(1, 'abc'); +} +do_catchsql_test 12.2.2 { + SELECT * FROM t1; +} {1 {SQL logic error}} +do_catchsql_test 12.2.3 { + SELECT * FROM t1('abc'); +} {1 {SQL logic error}} +do_catchsql_test 12.2.4 { + SELECT count(*) FROM t1; +} {1 {SQL logic error}} + + finish_test