| OLD | NEW |
| (Empty) | |
| 1 From ddbd1f830c4000b17617b917a768ea4a4ad8d660 Mon Sep 17 00:00:00 2001 |
| 2 From: Scott Hess <shess@chromium.org> |
| 3 Date: Mon, 22 Dec 2014 16:57:47 -0800 |
| 4 Subject: [PATCH 22/24] [fts2] Detect and handle certain corruption cases. |
| 5 |
| 6 These were in response to actual corrupt databases found in the wild. |
| 7 |
| 8 Original Gears CL: |
| 9 https://code.google.com/p/gears/source/detail?r=2855&path=/trunk/third_party/sql
ite_google/ext/fts2/fts2.c |
| 10 --- |
| 11 third_party/sqlite/src/ext/fts2/fts2.c | 152 ++++++++++++++++++++++++++++----- |
| 12 1 file changed, 132 insertions(+), 20 deletions(-) |
| 13 |
| 14 diff --git a/third_party/sqlite/src/ext/fts2/fts2.c b/third_party/sqlite/src/ext
/fts2/fts2.c |
| 15 index 7d07137..bdbd747 100644 |
| 16 --- a/third_party/sqlite/src/ext/fts2/fts2.c |
| 17 +++ b/third_party/sqlite/src/ext/fts2/fts2.c |
| 18 @@ -349,6 +349,16 @@ |
| 19 # define TRACE(A) |
| 20 #endif |
| 21 |
| 22 +#if 0 |
| 23 +/* Useful to set breakpoints. See main.c sqlite3Corrupt(). */ |
| 24 +static int fts2Corrupt(void){ |
| 25 + return SQLITE_CORRUPT; |
| 26 +} |
| 27 +# define SQLITE_CORRUPT_BKPT fts2Corrupt() |
| 28 +#else |
| 29 +# define SQLITE_CORRUPT_BKPT SQLITE_CORRUPT |
| 30 +#endif |
| 31 + |
| 32 /* It is not safe to call isspace(), tolower(), or isalnum() on |
| 33 ** hi-bit-set characters. This is the same solution used in the |
| 34 ** tokenizer. |
| 35 @@ -3435,8 +3445,11 @@ static int fulltextNext(sqlite3_vtab_cursor *pCursor){ |
| 36 c->eof = 0; |
| 37 return SQLITE_OK; |
| 38 } |
| 39 - /* an error occurred; abort */ |
| 40 - return rc==SQLITE_DONE ? SQLITE_ERROR : rc; |
| 41 + |
| 42 + /* Corrupt if the index refers to missing document. */ |
| 43 + if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT; |
| 44 + |
| 45 + return rc; |
| 46 } |
| 47 } |
| 48 |
| 49 @@ -5106,6 +5119,9 @@ static void leavesReaderDestroy(LeavesReader *pReader){ |
| 50 ** the leaf data was entirely contained in the root), or from the |
| 51 ** stream of blocks between iStartBlockid and iEndBlockid, inclusive. |
| 52 */ |
| 53 +/* TODO(shess): Figure out a means of indicating how many leaves are |
| 54 +** expected, for purposes of detecting corruption. |
| 55 +*/ |
| 56 static int leavesReaderInit(fulltext_vtab *v, |
| 57 int idx, |
| 58 sqlite_int64 iStartBlockid, |
| 59 @@ -5117,6 +5133,10 @@ static int leavesReaderInit(fulltext_vtab *v, |
| 60 |
| 61 dataBufferInit(&pReader->rootData, 0); |
| 62 if( iStartBlockid==0 ){ |
| 63 + /* Corrupt if this can't be a leaf node. */ |
| 64 + if( pRootData==NULL || nRootData<1 || pRootData[0]!='\0' ){ |
| 65 + return SQLITE_CORRUPT_BKPT; |
| 66 + } |
| 67 /* Entire leaf level fit in root data. */ |
| 68 dataBufferReplace(&pReader->rootData, pRootData, nRootData); |
| 69 leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, |
| 70 @@ -5127,22 +5147,48 @@ static int leavesReaderInit(fulltext_vtab *v, |
| 71 if( rc!=SQLITE_OK ) return rc; |
| 72 |
| 73 rc = sqlite3_bind_int64(s, 1, iStartBlockid); |
| 74 - if( rc!=SQLITE_OK ) return rc; |
| 75 + if( rc!=SQLITE_OK ) goto err; |
| 76 |
| 77 rc = sqlite3_bind_int64(s, 2, iEndBlockid); |
| 78 - if( rc!=SQLITE_OK ) return rc; |
| 79 + if( rc!=SQLITE_OK ) goto err; |
| 80 |
| 81 rc = sqlite3_step(s); |
| 82 + |
| 83 + /* Corrupt if interior node referenced missing leaf node. */ |
| 84 if( rc==SQLITE_DONE ){ |
| 85 - pReader->eof = 1; |
| 86 - return SQLITE_OK; |
| 87 + rc = SQLITE_CORRUPT_BKPT; |
| 88 + goto err; |
| 89 + } |
| 90 + |
| 91 + if( rc!=SQLITE_ROW ) goto err; |
| 92 + rc = SQLITE_OK; |
| 93 + |
| 94 + /* Corrupt if leaf data isn't a blob. */ |
| 95 + if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){ |
| 96 + rc = SQLITE_CORRUPT_BKPT; |
| 97 + }else{ |
| 98 + const char *pLeafData = sqlite3_column_blob(s, 0); |
| 99 + int nLeafData = sqlite3_column_bytes(s, 0); |
| 100 + |
| 101 + /* Corrupt if this can't be a leaf node. */ |
| 102 + if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){ |
| 103 + rc = SQLITE_CORRUPT_BKPT; |
| 104 + }else{ |
| 105 + leafReaderInit(pLeafData, nLeafData, &pReader->leafReader); |
| 106 + } |
| 107 + } |
| 108 + |
| 109 + err: |
| 110 + if( rc!=SQLITE_OK ){ |
| 111 + if( idx==-1 ){ |
| 112 + sqlite3_finalize(s); |
| 113 + }else{ |
| 114 + sqlite3_reset(s); |
| 115 + } |
| 116 + return rc; |
| 117 } |
| 118 - if( rc!=SQLITE_ROW ) return rc; |
| 119 |
| 120 pReader->pStmt = s; |
| 121 - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), |
| 122 - sqlite3_column_bytes(pReader->pStmt, 0), |
| 123 - &pReader->leafReader); |
| 124 } |
| 125 return SQLITE_OK; |
| 126 } |
| 127 @@ -5165,10 +5211,22 @@ static int leavesReaderStep(fulltext_vtab *v, LeavesRead
er *pReader){ |
| 128 pReader->eof = 1; |
| 129 return rc==SQLITE_DONE ? SQLITE_OK : rc; |
| 130 } |
| 131 - leafReaderDestroy(&pReader->leafReader); |
| 132 - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), |
| 133 - sqlite3_column_bytes(pReader->pStmt, 0), |
| 134 - &pReader->leafReader); |
| 135 + |
| 136 + /* Corrupt if leaf data isn't a blob. */ |
| 137 + if( sqlite3_column_type(pReader->pStmt, 0)!=SQLITE_BLOB ){ |
| 138 + return SQLITE_CORRUPT_BKPT; |
| 139 + }else{ |
| 140 + const char *pLeafData = sqlite3_column_blob(pReader->pStmt, 0); |
| 141 + int nLeafData = sqlite3_column_bytes(pReader->pStmt, 0); |
| 142 + |
| 143 + /* Corrupt if this can't be a leaf node. */ |
| 144 + if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){ |
| 145 + return SQLITE_CORRUPT_BKPT; |
| 146 + } |
| 147 + |
| 148 + leafReaderDestroy(&pReader->leafReader); |
| 149 + leafReaderInit(pLeafData, nLeafData, &pReader->leafReader); |
| 150 + } |
| 151 } |
| 152 return SQLITE_OK; |
| 153 } |
| 154 @@ -5230,6 +5288,14 @@ static int leavesReadersInit(fulltext_vtab *v, int iLevel
, |
| 155 const char *pRootData = sqlite3_column_blob(s, 2); |
| 156 int nRootData = sqlite3_column_bytes(s, 2); |
| 157 |
| 158 + /* Corrupt if we get back different types than we stored. */ |
| 159 + if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
| 160 + sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 161 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 162 + rc = SQLITE_CORRUPT_BKPT; |
| 163 + break; |
| 164 + } |
| 165 + |
| 166 assert( i<MERGE_COUNT ); |
| 167 rc = leavesReaderInit(v, i, iStart, iEnd, pRootData, nRootData, |
| 168 &pReaders[i]); |
| 169 @@ -5241,6 +5307,7 @@ static int leavesReadersInit(fulltext_vtab *v, int iLevel, |
| 170 while( i-->0 ){ |
| 171 leavesReaderDestroy(&pReaders[i]); |
| 172 } |
| 173 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 174 return rc; |
| 175 } |
| 176 |
| 177 @@ -5617,11 +5684,27 @@ static int loadAndGetChildrenContaining( |
| 178 if( rc!=SQLITE_OK ) return rc; |
| 179 |
| 180 rc = sqlite3_step(s); |
| 181 - if( rc==SQLITE_DONE ) return SQLITE_ERROR; |
| 182 + /* Corrupt if interior node references missing child node. */ |
| 183 + if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT; |
| 184 if( rc!=SQLITE_ROW ) return rc; |
| 185 |
| 186 - getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0), |
| 187 - pTerm, nTerm, isPrefix, piStartChild, piEndChild); |
| 188 + /* Corrupt if child node isn't a blob. */ |
| 189 + if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){ |
| 190 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 191 + return SQLITE_CORRUPT_BKPT; |
| 192 + }else{ |
| 193 + const char *pData = sqlite3_column_blob(s, 0); |
| 194 + int nData = sqlite3_column_bytes(s, 0); |
| 195 + |
| 196 + /* Corrupt if child is not a valid interior node. */ |
| 197 + if( pData==NULL || nData<1 || pData[0]=='\0' ){ |
| 198 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 199 + return SQLITE_CORRUPT_BKPT; |
| 200 + } |
| 201 + |
| 202 + getChildrenContaining(pData, nData, pTerm, nTerm, |
| 203 + isPrefix, piStartChild, piEndChild); |
| 204 + } |
| 205 |
| 206 /* We expect only one row. We must execute another sqlite3_step() |
| 207 * to complete the iteration; otherwise the table will remain |
| 208 @@ -5704,7 +5787,8 @@ static int loadSegment(fulltext_vtab *v, const char *pData
, int nData, |
| 209 DataBuffer result; |
| 210 int rc; |
| 211 |
| 212 - assert( nData>1 ); |
| 213 + /* Corrupt if segment root can't be valid. */ |
| 214 + if( pData==NULL || nData<1 ) return SQLITE_CORRUPT_BKPT; |
| 215 |
| 216 /* This code should never be called with buffered updates. */ |
| 217 assert( v->nPendingData<0 ); |
| 218 @@ -5758,6 +5842,14 @@ static int termSelect(fulltext_vtab *v, int iColumn, |
| 219 const char *pData = sqlite3_column_blob(s, 2); |
| 220 const int nData = sqlite3_column_bytes(s, 2); |
| 221 const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); |
| 222 + |
| 223 + /* Corrupt if we get back different types than we stored. */ |
| 224 + if( sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 225 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 226 + rc = SQLITE_CORRUPT_BKPT; |
| 227 + goto err; |
| 228 + } |
| 229 + |
| 230 rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix, |
| 231 &doclist); |
| 232 if( rc!=SQLITE_OK ) goto err; |
| 233 @@ -5777,6 +5869,7 @@ static int termSelect(fulltext_vtab *v, int iColumn, |
| 234 } |
| 235 |
| 236 err: |
| 237 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 238 dataBufferDestroy(&doclist); |
| 239 return rc; |
| 240 } |
| 241 @@ -6269,6 +6362,14 @@ static void optimizeFunc(sqlite3_context *pContext, |
| 242 const char *pRootData = sqlite3_column_blob(s, 2); |
| 243 int nRootData = sqlite3_column_bytes(s, 2); |
| 244 |
| 245 + /* Corrupt if we get back different types than we stored. */ |
| 246 + if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
| 247 + sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 248 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 249 + rc = SQLITE_CORRUPT_BKPT; |
| 250 + break; |
| 251 + } |
| 252 + |
| 253 assert( i<nReaders ); |
| 254 rc = leavesReaderInit(v, -1, iStart, iEnd, pRootData, nRootData, |
| 255 &readers[i].reader); |
| 256 @@ -6282,6 +6383,8 @@ static void optimizeFunc(sqlite3_context *pContext, |
| 257 if( rc==SQLITE_DONE ){ |
| 258 assert( i==nReaders ); |
| 259 rc = optimizeInternal(v, readers, nReaders, &writer); |
| 260 + }else{ |
| 261 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 262 } |
| 263 |
| 264 while( i-- > 0 ){ |
| 265 @@ -6345,9 +6448,18 @@ static int collectSegmentTerms(fulltext_vtab *v, sqlite3_
stmt *s, |
| 266 const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1); |
| 267 const char *pRootData = sqlite3_column_blob(s, 2); |
| 268 const int nRootData = sqlite3_column_bytes(s, 2); |
| 269 + int rc; |
| 270 LeavesReader reader; |
| 271 - int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, |
| 272 - pRootData, nRootData, &reader); |
| 273 + |
| 274 + /* Corrupt if we get back different types than we stored. */ |
| 275 + if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
| 276 + sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 277 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 278 + return SQLITE_CORRUPT_BKPT; |
| 279 + } |
| 280 + |
| 281 + rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, |
| 282 + pRootData, nRootData, &reader); |
| 283 if( rc!=SQLITE_OK ) return rc; |
| 284 |
| 285 while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){ |
| 286 -- |
| 287 2.2.1 |
| 288 |
| OLD | NEW |