| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Darwin Huang <huangdarwin@chromium.org> |
| Date: Mon, 16 Dec 2019 16:01:06 -0800 |
| Subject: [PATCH 15/25] Fix fts3 integer overflows |
| |
| Backports https://www.sqlite.org/src/info/3b873029ef1903f7 |
| |
| Bug: 1029002, 1030709 |
| --- |
| third_party/sqlite/patched/ext/fts3/fts3.c | 43 ++++++++----------- |
| third_party/sqlite/patched/ext/fts3/fts3Int.h | 4 ++ |
| .../sqlite/patched/ext/fts3/fts3_write.c | 29 +++++++------ |
| .../sqlite/patched/test/fts3corrupt4.test | 32 +++++++++++++- |
| 4 files changed, 68 insertions(+), 40 deletions(-) |
| |
| diff --git a/third_party/sqlite/patched/ext/fts3/fts3.c b/third_party/sqlite/patched/ext/fts3/fts3.c |
| index 26aee1788429..5cef6aa2370f 100644 |
| --- a/third_party/sqlite/patched/ext/fts3/fts3.c |
| +++ b/third_party/sqlite/patched/ext/fts3/fts3.c |
| @@ -308,18 +308,6 @@ |
| SQLITE_EXTENSION_INIT1 |
| #endif |
| |
| -/* |
| -** The following are copied from sqliteInt.h. |
| -** |
| -** Constants for the largest and smallest possible 64-bit signed integers. |
| -** These macros are designed to work correctly on both 32-bit and 64-bit |
| -** compilers. |
| -*/ |
| -#ifndef SQLITE_AMALGAMATION |
| -# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) |
| -# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) |
| -#endif |
| - |
| static int fts3EvalNext(Fts3Cursor *pCsr); |
| static int fts3EvalStart(Fts3Cursor *pCsr); |
| static int fts3TermSegReaderCursor( |
| @@ -364,12 +352,7 @@ int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){ |
| v = (*ptr++); \ |
| if( (v & mask2)==0 ){ var = v; return ret; } |
| |
| -/* |
| -** Read a 64-bit variable-length integer from memory starting at p[0]. |
| -** Return the number of bytes read, or 0 on error. |
| -** The value is stored in *v. |
| -*/ |
| -int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){ |
| +int sqlite3Fts3GetVarintU(const char *pBuf, sqlite_uint64 *v){ |
| const unsigned char *p = (const unsigned char*)pBuf; |
| const unsigned char *pStart = p; |
| u32 a; |
| @@ -391,6 +374,15 @@ int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){ |
| return (int)(p - pStart); |
| } |
| |
| +/* |
| +** Read a 64-bit variable-length integer from memory starting at p[0]. |
| +** Return the number of bytes read, or 0 on error. |
| +** The value is stored in *v. |
| +*/ |
| +int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){ |
| + return sqlite3Fts3GetVarintU(pBuf, (sqlite3_uint64*)v); |
| +} |
| + |
| /* |
| ** Read a 64-bit variable-length integer from memory starting at p[0] and |
| ** not extending past pEnd[-1]. |
| @@ -2484,12 +2476,12 @@ static void fts3GetDeltaVarint3( |
| if( *pp>=pEnd ){ |
| *pp = 0; |
| }else{ |
| - sqlite3_int64 iVal; |
| - *pp += sqlite3Fts3GetVarint(*pp, &iVal); |
| + u64 iVal; |
| + *pp += sqlite3Fts3GetVarintU(*pp, &iVal); |
| if( bDescIdx ){ |
| - *pVal -= iVal; |
| + *pVal = (i64)((u64)*pVal - iVal); |
| }else{ |
| - *pVal += iVal; |
| + *pVal = (i64)((u64)*pVal + iVal); |
| } |
| } |
| } |
| @@ -2518,9 +2510,9 @@ static void fts3PutDeltaVarint3( |
| ){ |
| sqlite3_int64 iWrite; |
| if( bDescIdx==0 || *pbFirst==0 ){ |
| - iWrite = iVal - *piPrev; |
| + iWrite = (u64)iVal - (u64)*piPrev; |
| }else{ |
| - iWrite = *piPrev - iVal; |
| + iWrite = (u64)*piPrev - (u64)iVal; |
| } |
| assert( *pbFirst || *piPrev==0 ); |
| assert_fts3_nc( *pbFirst==0 || iWrite>0 ); |
| @@ -2540,7 +2532,8 @@ static void fts3PutDeltaVarint3( |
| ** Using this makes it easier to write code that can merge doclists that are |
| ** sorted in either ascending or descending order. |
| */ |
| -#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1-i2)) |
| +// #define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i64)((u64)i1-i2)) |
| +#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1>i2?1:((i1==i2)?0:-1))) |
| |
| /* |
| ** This function does an "OR" merge of two doclists (output contains all |
| diff --git a/third_party/sqlite/patched/ext/fts3/fts3Int.h b/third_party/sqlite/patched/ext/fts3/fts3Int.h |
| index 5cafa1fe9b91..44bf78ef8f17 100644 |
| --- a/third_party/sqlite/patched/ext/fts3/fts3Int.h |
| +++ b/third_party/sqlite/patched/ext/fts3/fts3Int.h |
| @@ -196,6 +196,9 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */ |
| # define TESTONLY(X) |
| #endif |
| |
| +#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| +#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| + |
| #endif /* SQLITE_AMALGAMATION */ |
| |
| #ifdef SQLITE_DEBUG |
| @@ -577,6 +580,7 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int); |
| void sqlite3Fts3ErrMsg(char**,const char*,...); |
| int sqlite3Fts3PutVarint(char *, sqlite3_int64); |
| int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); |
| +int sqlite3Fts3GetVarintU(const char *, sqlite_uint64 *); |
| int sqlite3Fts3GetVarintBounded(const char*,const char*,sqlite3_int64*); |
| int sqlite3Fts3GetVarint32(const char *, int *); |
| int sqlite3Fts3VarintLen(sqlite3_uint64); |
| diff --git a/third_party/sqlite/patched/ext/fts3/fts3_write.c b/third_party/sqlite/patched/ext/fts3/fts3_write.c |
| index 47692c52c882..8b6b729987c3 100644 |
| --- a/third_party/sqlite/patched/ext/fts3/fts3_write.c |
| +++ b/third_party/sqlite/patched/ext/fts3/fts3_write.c |
| @@ -696,7 +696,7 @@ static int fts3PendingListAppend( |
| assert( !p || p->iLastDocid<=iDocid ); |
| |
| if( !p || p->iLastDocid!=iDocid ){ |
| - sqlite3_int64 iDelta = iDocid - (p ? p->iLastDocid : 0); |
| + u64 iDelta = (u64)iDocid - (u64)(p ? p->iLastDocid : 0); |
| if( p ){ |
| assert( p->nData<p->nSpace ); |
| assert( p->aData[p->nData]==0 ); |
| @@ -1529,18 +1529,18 @@ static int fts3SegReaderNextDocid( |
| }else{ |
| rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX); |
| if( rc==SQLITE_OK ){ |
| - sqlite3_int64 iDelta; |
| - pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); |
| + u64 iDelta; |
| + pReader->pOffsetList = p + sqlite3Fts3GetVarintU(p, &iDelta); |
| if( pTab->bDescIdx ){ |
| - pReader->iDocid -= iDelta; |
| + pReader->iDocid = (i64)((u64)pReader->iDocid - iDelta); |
| }else{ |
| - pReader->iDocid += iDelta; |
| + pReader->iDocid = (i64)((u64)pReader->iDocid + iDelta); |
| } |
| } |
| } |
| } |
| |
| - return SQLITE_OK; |
| + return rc; |
| } |
| |
| |
| @@ -2279,6 +2279,7 @@ static int fts3SegWriterAdd( |
| int rc; |
| |
| /* The current leaf node is full. Write it out to the database. */ |
| + if( pWriter->iFree==LARGEST_INT64 ) return FTS_CORRUPT_VTAB; |
| rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData); |
| if( rc!=SQLITE_OK ) return rc; |
| p->nLeafAdd++; |
| @@ -2975,9 +2976,9 @@ int sqlite3Fts3SegReaderStep( |
| ** doclist. */ |
| sqlite3_int64 iDelta; |
| if( p->bDescIdx && nDoclist>0 ){ |
| - iDelta = iPrev - iDocid; |
| + iDelta = (i64)((u64)iPrev - (u64)iDocid); |
| }else{ |
| - iDelta = iDocid - iPrev; |
| + iDelta = (i64)((u64)iDocid - (u64)iPrev); |
| } |
| if( iDelta<=0 && (nDoclist>0 || iDelta!=iDocid) ){ |
| return FTS_CORRUPT_VTAB; |
| @@ -3264,7 +3265,7 @@ static int fts3SegmentMerge( |
| csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist); |
| } |
| if( rc!=SQLITE_OK ) goto finished; |
| - assert( pWriter || bIgnoreEmpty ); |
| + assert_fts3_nc( pWriter || bIgnoreEmpty ); |
| |
| if( iLevel!=FTS3_SEGCURSOR_PENDING ){ |
| rc = fts3DeleteSegdir( |
| @@ -5175,12 +5176,12 @@ static u64 fts3ChecksumIndex( |
| |
| i64 iDocid = 0; |
| i64 iCol = 0; |
| - i64 iPos = 0; |
| + u64 iPos = 0; |
| |
| pCsr += sqlite3Fts3GetVarint(pCsr, &iDocid); |
| while( pCsr<pEnd ){ |
| - i64 iVal = 0; |
| - pCsr += sqlite3Fts3GetVarint(pCsr, &iVal); |
| + u64 iVal = 0; |
| + pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal); |
| if( pCsr<pEnd ){ |
| if( iVal==0 || iVal==1 ){ |
| iCol = 0; |
| @@ -5188,8 +5189,8 @@ static u64 fts3ChecksumIndex( |
| if( iVal ){ |
| pCsr += sqlite3Fts3GetVarint(pCsr, &iCol); |
| }else{ |
| - pCsr += sqlite3Fts3GetVarint(pCsr, &iVal); |
| - iDocid += iVal; |
| + pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal); |
| + iDocid = (i64)((u64)iDocid + iVal); |
| } |
| }else{ |
| iPos += (iVal - 2); |
| diff --git a/third_party/sqlite/patched/test/fts3corrupt4.test b/third_party/sqlite/patched/test/fts3corrupt4.test |
| index b5d9c138a5ea..45dd52fff29e 100644 |
| --- a/third_party/sqlite/patched/test/fts3corrupt4.test |
| +++ b/third_party/sqlite/patched/test/fts3corrupt4.test |
| @@ -5549,7 +5549,7 @@ do_catchsql_test 30.2 { |
| #------------------------------------------------------------------------- |
| # |
| reset_db |
| -do_catchsql_test 32.0 { |
| +do_catchsql_test 33.0 { |
| CREATE VIRTUAL TABLE f USING fts3(a,b,tokenize=icu); |
| CREATE TABLE 'f_docsize'(docid INTEGER PRIMARY KEY, size BLOB); |
| CREATE TABLE 'f_stat'(id INTEGER PRIMARY KEY, value BLOB); |
| @@ -5558,5 +5558,35 @@ do_catchsql_test 32.0 { |
| INSERT INTO f(f) VALUES ('merge=198,49'); |
| } {1 {database disk image is malformed}} |
| |
| +#------------------------------------------------------------------------- |
| +# |
| +reset_db |
| +do_execsql_test 34.0 { |
| + CREATE VIRTUAL TABLE f USING fts3(a,b); |
| + INSERT INTO f VALUES (1, '1234'); |
| + INSERT INTO f_segdir VALUES (1,255,0,0,'1 255',x'00'); |
| + UPDATE f_segdir SET level = 0 WHERE level IN ( |
| + SELECT level FROM f_segdir LIMIT 1 OFFSET 1 |
| + ); |
| + INSERT INTO f_segdir VALUES (255,249,0,121,'0 0',x'00'); |
| + INSERT INTO f_content VALUES (255,0,x'ff'); |
| + INSERT INTO f_segdir VALUES (1,255,16,0,'1 255',x'00'); |
| +} |
| + |
| +do_catchsql_test 34.1 { |
| + UPDATE f SET b = x'00' WHERE b IN (SELECT b FROM f LIMIT 1 OFFSET 0); |
| +} {1 {database disk image is malformed}} |
| + |
| +#------------------------------------------------------------------------- |
| +# |
| +reset_db |
| +do_execsql_test 35.0 { |
| + CREATE VIRTUAL TABLE f USING fts3(a,b); |
| + INSERT INTO f_segdir VALUES (1,255,0,0,'1 255',x'0001ff000001ff000001ff000001ff000001ff00c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5bec5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5'); |
| +} |
| + |
| +do_catchsql_test 35.1 { |
| + INSERT INTO f(f) VALUES ('integrity-check'); |
| +} {1 {database disk image is malformed}} |
| |
| finish_test |
| -- |
| 2.25.0.rc1.283.g88dfdc4193-goog |
| |