Skip to content

Commit 52e2764

Browse files
committed
Add extra defenses against strategically corrupt databases to fts3/4.
1 parent 1c823a6 commit 52e2764

File tree

4 files changed

+169
-12
lines changed

4 files changed

+169
-12
lines changed

ext/fts3/fts3.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,7 +1821,7 @@ static int fts3ScanInteriorNode(
18211821
const char *zCsr = zNode; /* Cursor to iterate through node */
18221822
const char *zEnd = &zCsr[nNode];/* End of interior node buffer */
18231823
char *zBuffer = 0; /* Buffer to load terms into */
1824-
int nAlloc = 0; /* Size of allocated buffer */
1824+
i64 nAlloc = 0; /* Size of allocated buffer */
18251825
int isFirstTerm = 1; /* True when processing first term on page */
18261826
sqlite3_int64 iChild; /* Block id of child node to descend to */
18271827

@@ -1859,14 +1859,14 @@ static int fts3ScanInteriorNode(
18591859
zCsr += fts3GetVarint32(zCsr, &nSuffix);
18601860

18611861
assert( nPrefix>=0 && nSuffix>=0 );
1862-
if( &zCsr[nSuffix]>zEnd ){
1862+
if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){
18631863
rc = FTS_CORRUPT_VTAB;
18641864
goto finish_scan;
18651865
}
1866-
if( nPrefix+nSuffix>nAlloc ){
1866+
if( (i64)nPrefix+nSuffix>nAlloc ){
18671867
char *zNew;
1868-
nAlloc = (nPrefix+nSuffix) * 2;
1869-
zNew = (char *)sqlite3_realloc(zBuffer, nAlloc);
1868+
nAlloc = ((i64)nPrefix+nSuffix) * 2;
1869+
zNew = (char *)sqlite3_realloc64(zBuffer, nAlloc);
18701870
if( !zNew ){
18711871
rc = SQLITE_NOMEM;
18721872
goto finish_scan;

ext/fts3/fts3_write.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,15 +1374,19 @@ static int fts3SegReaderNext(
13741374
** safe (no risk of overread) even if the node data is corrupted. */
13751375
pNext += fts3GetVarint32(pNext, &nPrefix);
13761376
pNext += fts3GetVarint32(pNext, &nSuffix);
1377-
if( nPrefix<0 || nSuffix<=0
1378-
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
1377+
if( nSuffix<=0
1378+
|| (&pReader->aNode[pReader->nNode] - pNext)<nSuffix
1379+
|| nPrefix>pReader->nTermAlloc
13791380
){
13801381
return FTS_CORRUPT_VTAB;
13811382
}
13821383

1383-
if( nPrefix+nSuffix>pReader->nTermAlloc ){
1384-
int nNew = (nPrefix+nSuffix)*2;
1385-
char *zNew = sqlite3_realloc(pReader->zTerm, nNew);
1384+
/* Both nPrefix and nSuffix were read by fts3GetVarint32() and so are
1385+
** between 0 and 0x7FFFFFFF. But the sum of the two may cause integer
1386+
** overflow - hence the (i64) casts. */
1387+
if( (i64)nPrefix+nSuffix>(i64)pReader->nTermAlloc ){
1388+
i64 nNew = ((i64)nPrefix+nSuffix)*2;
1389+
char *zNew = sqlite3_realloc64(pReader->zTerm, nNew);
13861390
if( !zNew ){
13871391
return SQLITE_NOMEM;
13881392
}
@@ -1404,7 +1408,7 @@ static int fts3SegReaderNext(
14041408
** b-tree node. And that the final byte of the doclist is 0x00. If either
14051409
** of these statements is untrue, then the data structure is corrupt.
14061410
*/
1407-
if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
1411+
if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)<pReader->nDoclist
14081412
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
14091413
){
14101414
return FTS_CORRUPT_VTAB;
@@ -3730,21 +3734,26 @@ static int nodeReaderNext(NodeReader *p){
37303734
}
37313735
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
37323736

3737+
if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){
3738+
return SQLITE_CORRUPT_VTAB;
3739+
}
37333740
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
37343741
if( rc==SQLITE_OK ){
37353742
memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix);
37363743
p->term.n = nPrefix+nSuffix;
37373744
p->iOff += nSuffix;
37383745
if( p->iChild==0 ){
37393746
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
3747+
if( (p->nNode-p->iOff)<p->nDoclist ){
3748+
return SQLITE_CORRUPT_VTAB;
3749+
}
37403750
p->aDoclist = &p->aNode[p->iOff];
37413751
p->iOff += p->nDoclist;
37423752
}
37433753
}
37443754
}
37453755

37463756
assert( p->iOff<=p->nNode );
3747-
37483757
return rc;
37493758
}
37503759

test/fts3corrupt4.test

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# 2006 September 9
2+
#
3+
# The author disclaims copyright to this source code. In place of
4+
# a legal notice, here is a blessing:
5+
#
6+
# May you do good and not evil.
7+
# May you find forgiveness for yourself and forgive others.
8+
# May you share freely, never taking more than you give.
9+
#
10+
#*************************************************************************
11+
# This file implements regression tests for SQLite library. The
12+
# focus of this script is testing the FTS3 module.
13+
#
14+
# $Id: fts3aa.test,v 1.1 2007/08/20 17:38:42 shess Exp $
15+
#
16+
17+
set testdir [file dirname $argv0]
18+
source $testdir/tester.tcl
19+
set testprefix fts3corrupt4
20+
21+
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
22+
ifcapable !fts3 {
23+
finish_test
24+
return
25+
}
26+
27+
do_execsql_test 1.0 {
28+
BEGIN;
29+
CREATE VIRTUAL TABLE ft USING fts3;
30+
INSERT INTO ft VALUES('aback');
31+
INSERT INTO ft VALUES('abaft');
32+
INSERT INTO ft VALUES('abandon');
33+
COMMIT;
34+
}
35+
36+
proc blob {a} { binary decode hex $a }
37+
db func blob blob
38+
39+
do_execsql_test 1.1 {
40+
SELECT quote(root) FROM ft_segdir;
41+
} {X'0005616261636B03010200030266740302020003046E646F6E03030200'}
42+
43+
do_execsql_test 1.2 {
44+
UPDATE ft_segdir SET root = blob(
45+
'0005616261636B03010200 FFFFFFFF0702 66740302020003046E646F6E03030200'
46+
);
47+
}
48+
49+
do_catchsql_test 1.3 {
50+
SELECT * FROM ft WHERE ft MATCH 'abandon';
51+
} {1 {database disk image is malformed}}
52+
53+
#-------------------------------------------------------------------------
54+
reset_db
55+
do_execsql_test 2.0.0 {
56+
CREATE VIRTUAL TABLE ft USING fts3;
57+
INSERT INTO ft(ft) VALUES('nodesize=32');
58+
}
59+
do_test 2.0.1 {
60+
for {set i 0} {$i < 12} {incr i} {
61+
execsql {
62+
BEGIN;
63+
INSERT INTO ft VALUES('abc' || $i);
64+
INSERT INTO ft VALUES('abc' || $i || 'x' );
65+
INSERT INTO ft VALUES('abc' || $i || 'xx' );
66+
COMMIT
67+
}
68+
}
69+
execsql {
70+
SELECT count(*) FROM ft_segdir;
71+
SELECT count(*) FROM ft_segments;
72+
}
73+
} {12 0}
74+
75+
do_execsql_test 2.1 {
76+
INSERT INTO ft(ft) VALUES('merge=1,4');
77+
SELECT count(*) FROM ft_segdir;
78+
SELECT count(*) FROM ft_segments;
79+
} {12 3}
80+
81+
do_execsql_test 2.2 {
82+
SELECT quote(block) FROM ft_segments WHERE blockid=2
83+
} {X'00056162633130031F0200'}
84+
85+
db func blob blob
86+
do_execsql_test 2.3.1 {
87+
UPDATE ft_segments SET block =
88+
blob('00056162633130031F0200 FFFFFFFF07FF55 66740302020003046E646F6E03030200')
89+
WHERE blockid=2;
90+
} {}
91+
do_catchsql_test 2.3.2 {
92+
INSERT INTO ft(ft) VALUES('merge=1,4');
93+
} {1 {database disk image is malformed}}
94+
95+
do_execsql_test 2.4.1 {
96+
UPDATE ft_segments SET block =
97+
blob('00056162633130031F0200 02FFFFFFFF07 66740302020003046E646F6E03030200')
98+
WHERE blockid=2;
99+
} {}
100+
do_catchsql_test 2.4.2 {
101+
INSERT INTO ft(ft) VALUES('merge=1,4');
102+
} {1 {database disk image is malformed}}
103+
104+
do_execsql_test 2.5.1 {
105+
UPDATE ft_segments SET block =
106+
blob('00056162633130031F0200 0202 6674 FFFFFF070302020003046E646F6E030200')
107+
WHERE blockid=2;
108+
} {}
109+
do_catchsql_test 2.5.2 {
110+
INSERT INTO ft(ft) VALUES('merge=1,4');
111+
} {1 {database disk image is malformed}}
112+
113+
#-------------------------------------------------------------------------
114+
reset_db
115+
do_execsql_test 3.0.0 {
116+
CREATE VIRTUAL TABLE ft USING fts3;
117+
INSERT INTO ft(ft) VALUES('nodesize=32');
118+
}
119+
do_test 3.0.1 {
120+
execsql BEGIN
121+
for {set i 0} {$i < 20} {incr i} {
122+
execsql { INSERT INTO ft VALUES('abc' || $i) }
123+
}
124+
execsql {
125+
COMMIT;
126+
SELECT count(*) FROM ft_segdir;
127+
SELECT count(*) FROM ft_segments;
128+
}
129+
} {1 5}
130+
131+
do_execsql_test 3.1 {
132+
SELECT quote(root) FROM ft_segdir
133+
} {X'0101056162633132040136030132030136'}
134+
135+
db func blob blob
136+
do_execsql_test 3.2 {
137+
UPDATE ft_segdir
138+
SET root = blob('0101056162633132FFFFFFFF070236030132030136');
139+
}
140+
141+
do_catchsql_test 3.1 {
142+
SELECT * FROM ft WHERE ft MATCH 'abc20'
143+
} {1 {database disk image is malformed}}
144+
145+
finish_test
146+
147+

test/permutations.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ test_suite "fts3" -prefix "" -description {
262262
fts3am.test fts3an.test fts3ao.test fts3atoken.test
263263
fts3auto.test fts3aux1.test fts3aux2.test fts3b.test
264264
fts3comp1.test fts3conf.test fts3corrupt2.test fts3corrupt.test
265+
fts3corrupt4.test
265266
fts3cov.test fts3c.test fts3defer2.test fts3defer3.test
266267
fts3defer.test fts3drop.test fts3d.test fts3e.test
267268
fts3expr2.test fts3expr3.test fts3expr4.test fts3expr5.test

0 commit comments

Comments
 (0)