summaryrefslogtreecommitdiff
path: root/chromium/third_party/sqlite/patches/0005-Better-corruption-detection-in-fts3.patch
blob: 4eeb96e9ebe472273db8a78ebb16e379dec5e2c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Darwin Huang <huangdarwin@chromium.org>
Date: Tue, 19 Nov 2019 15:17:18 -0800
Subject: [PATCH 05/25] Better % corruption detection in fts3.

Backports https://www.sqlite.org/src/info/1e449687881f4d38

Bug: 1025472
---
 third_party/sqlite/patched/ext/fts3/fts3.c    | 26 +++++++++++++
 third_party/sqlite/patched/ext/fts3/fts3Int.h |  1 +
 .../sqlite/patched/ext/fts3/fts3_snippet.c    | 39 +++++++++++++++----
 .../sqlite/patched/ext/fts3/fts3_write.c      |  2 +-
 third_party/sqlite/patched/test/fts4aa.test   | 38 ++++++++++++++++++
 5 files changed, 98 insertions(+), 8 deletions(-)

diff --git a/third_party/sqlite/patched/ext/fts3/fts3.c b/third_party/sqlite/patched/ext/fts3/fts3.c
index d15809f3198d..98aa1a2c0f56 100644
--- a/third_party/sqlite/patched/ext/fts3/fts3.c
+++ b/third_party/sqlite/patched/ext/fts3/fts3.c
@@ -391,6 +391,32 @@ 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] and
+** not extending past pEnd[-1].
+** Return the number of bytes read, or 0 on error.
+** The value is stored in *v.
+*/
+int sqlite3Fts3GetVarintBounded(
+  const char *pBuf,
+  const char *pEnd,
+  sqlite_int64 *v
+){
+  const unsigned char *p = (const unsigned char*)pBuf;
+  const unsigned char *pStart = p;
+  const unsigned char *pX = (const unsigned char*)pEnd;
+  u64 b = 0;
+  int shift;
+  for(shift=0; shift<=63; shift+=7){
+    u64 c = p<pX ? *p : 0;
+    p++;
+    b += (c&0x7F) << shift;
+    if( (c & 0x80)==0 ) break;
+  }
+  *v = b;
+  return (int)(p - pStart);
+}
+
 /*
 ** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to
 ** a non-negative 32-bit integer before it is returned.
diff --git a/third_party/sqlite/patched/ext/fts3/fts3Int.h b/third_party/sqlite/patched/ext/fts3/fts3Int.h
index edbac06ae7cc..5cafa1fe9b91 100644
--- a/third_party/sqlite/patched/ext/fts3/fts3Int.h
+++ b/third_party/sqlite/patched/ext/fts3/fts3Int.h
@@ -577,6 +577,7 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
 void sqlite3Fts3ErrMsg(char**,const char*,...);
 int sqlite3Fts3PutVarint(char *, sqlite3_int64);
 int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
+int sqlite3Fts3GetVarintBounded(const char*,const char*,sqlite3_int64*);
 int sqlite3Fts3GetVarint32(const char *, int *);
 int sqlite3Fts3VarintLen(sqlite3_uint64);
 void sqlite3Fts3Dequote(char *);
diff --git a/third_party/sqlite/patched/ext/fts3/fts3_snippet.c b/third_party/sqlite/patched/ext/fts3/fts3_snippet.c
index 8a1730f8f1e2..39455dd104c5 100644
--- a/third_party/sqlite/patched/ext/fts3/fts3_snippet.c
+++ b/third_party/sqlite/patched/ext/fts3/fts3_snippet.c
@@ -1038,11 +1038,15 @@ static int fts3MatchinfoSelectDoctotal(
   Fts3Table *pTab,
   sqlite3_stmt **ppStmt,
   sqlite3_int64 *pnDoc,
-  const char **paLen
+  const char **paLen,
+  const char **ppEnd
 ){
   sqlite3_stmt *pStmt;
   const char *a;
+  const char *pEnd;
   sqlite3_int64 nDoc;
+  int n;
+
 
   if( !*ppStmt ){
     int rc = sqlite3Fts3SelectDoctotal(pTab, ppStmt);
@@ -1051,12 +1055,23 @@ static int fts3MatchinfoSelectDoctotal(
   pStmt = *ppStmt;
   assert( sqlite3_data_count(pStmt)==1 );
 
+  n = sqlite3_column_bytes(pStmt, 0);
+  if( n==0 ){
+    return FTS_CORRUPT_VTAB;
+  }
   a = sqlite3_column_blob(pStmt, 0);
-  a += sqlite3Fts3GetVarint(a, &nDoc);
-  if( nDoc==0 ) return FTS_CORRUPT_VTAB;
+  if( a==0 ){
+    return SQLITE_NOMEM;
+  }
+  pEnd = a + n;
+  a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
+  if( nDoc==0 || a>pEnd ){
+    return FTS_CORRUPT_VTAB;
+  }
   *pnDoc = (u32)nDoc;
 
   if( paLen ) *paLen = a;
+  if( ppEnd ) *ppEnd = pEnd;
   return SQLITE_OK;
 }
 
@@ -1237,7 +1252,7 @@ static int fts3MatchinfoValues(
       case FTS3_MATCHINFO_NDOC:
         if( bGlobal ){
           sqlite3_int64 nDoc = 0;
-          rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
+          rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0, 0);
           pInfo->aMatchinfo[0] = (u32)nDoc;
         }
         break;
@@ -1246,14 +1261,19 @@ static int fts3MatchinfoValues(
         if( bGlobal ){
           sqlite3_int64 nDoc;     /* Number of rows in table */
           const char *a;          /* Aggregate column length array */
+          const char *pEnd;       /* First byte past end of length array */
 
-          rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a);
+          rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a, &pEnd);
           if( rc==SQLITE_OK ){
             int iCol;
             for(iCol=0; iCol<pInfo->nCol; iCol++){
               u32 iVal;
               sqlite3_int64 nToken;
               a += sqlite3Fts3GetVarint(a, &nToken);
+              if( a>pEnd ){
+                rc = SQLITE_CORRUPT_VTAB;
+                break;
+              }
               iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
               pInfo->aMatchinfo[iCol] = iVal;
             }
@@ -1267,9 +1287,14 @@ static int fts3MatchinfoValues(
         if( rc==SQLITE_OK ){
           int iCol;
           const char *a = sqlite3_column_blob(pSelectDocsize, 0);
+          const char *pEnd = a + sqlite3_column_bytes(pSelectDocsize, 0);
           for(iCol=0; iCol<pInfo->nCol; iCol++){
             sqlite3_int64 nToken;
-            a += sqlite3Fts3GetVarint(a, &nToken);
+            a += sqlite3Fts3GetVarintBounded(a, pEnd, &nToken);
+            if( a>pEnd ){
+              rc = SQLITE_CORRUPT_VTAB;
+              break;
+            }
             pInfo->aMatchinfo[iCol] = (u32)nToken;
           }
         }
@@ -1300,7 +1325,7 @@ static int fts3MatchinfoValues(
         if( rc!=SQLITE_OK ) break;
         if( bGlobal ){
           if( pCsr->pDeferred ){
-            rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc, 0);
+            rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
             if( rc!=SQLITE_OK ) break;
           }
           rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
diff --git a/third_party/sqlite/patched/ext/fts3/fts3_write.c b/third_party/sqlite/patched/ext/fts3/fts3_write.c
index 11ac562a17f8..146c507b5d0b 100644
--- a/third_party/sqlite/patched/ext/fts3/fts3_write.c
+++ b/third_party/sqlite/patched/ext/fts3/fts3_write.c
@@ -67,7 +67,7 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
 #endif
 
 /*
-** The two values that may be meaningfully bound to the :1 parameter in
+** The values that may be meaningfully bound to the :1 parameter in
 ** statements SQL_REPLACE_STAT and SQL_SELECT_STAT.
 */
 #define FTS_STAT_DOCTOTAL      0
diff --git a/third_party/sqlite/patched/test/fts4aa.test b/third_party/sqlite/patched/test/fts4aa.test
index 095c522026f6..c516c34d564a 100644
--- a/third_party/sqlite/patched/test/fts4aa.test
+++ b/third_party/sqlite/patched/test/fts4aa.test
@@ -191,4 +191,42 @@ foreach {q r} [array get fts4aa_res] {
   } $r
 }
 
+# 2019-11-16 https://bugs.chromium.org/p/chromium/issues/detail?id=1025472
+#
+db close
+sqlite3 db :memory:
+do_execsql_test fts4aa-5.10 {
+  CREATE VIRTUAL TABLE t1 USING fts4(a, b, c, d, e,f,g,h,i,j,k,l,m,n,o,p,q,r);
+  INSERT INTO t1 VALUES('X Y', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+                        'a','b','c','d','e','f','g','h');
+  UPDATE t1_docsize SET size=x'88' WHERE docid=1;
+} {}
+do_catchsql_test fts4aa-5.20 {
+  SELECT quote(matchinfo(t1, 'l')) FROM t1 WHERE t1 MATCH 'X Y';
+} {1 {database disk image is malformed}}
+do_execsql_test fts4aa-5.30 {
+  DROP TABLE t1;
+  CREATE VIRTUAL TABLE t1 USING fts4(a,b,c,d);
+  INSERT INTO t1 VALUES('one two','three four','five six','seven eight');
+} {}
+do_catchsql_test fts4aa-5.40 {
+  UPDATE t1_stat SET value=x'01010101' WHERE id=0;
+  SELECT quote(matchinfo(t1,'a')) FROM t1 WHERE t1 MATCH 'one two';
+} {1 {database disk image is malformed}}
+do_catchsql_test fts4aa-5.50 {
+  UPDATE t1_stat SET value=x'010101' WHERE id=0;
+  SELECT quote(matchinfo(t1,'a')) FROM t1 WHERE t1 MATCH 'one two';
+} {1 {database disk image is malformed}}
+do_catchsql_test fts4aa-5.60 {
+  UPDATE t1_stat SET value=x'01' WHERE id=0;
+  SELECT quote(matchinfo(t1,'a')) FROM t1 WHERE t1 MATCH 'one two';
+} {1 {database disk image is malformed}}
+do_catchsql_test fts4aa-5.70 {
+  UPDATE t1_stat SET value=x'' WHERE id=0;
+  SELECT quote(matchinfo(t1,'a')) FROM t1 WHERE t1 MATCH 'one two';
+} {1 {database disk image is malformed}}
+
+
+
+
 finish_test
-- 
2.25.0.rc1.283.g88dfdc4193-goog