diff --git a/sqlite-3.26.0-fts_corrupt_db.patch b/sqlite-3.26.0-fts_corrupt_db.patch new file mode 100644 index 0000000..aaa968c --- /dev/null +++ b/sqlite-3.26.0-fts_corrupt_db.patch @@ -0,0 +1,292 @@ +Index: ext/fts3/fts3.c +================================================================== +--- ext/fts3/fts3.c ++++ ext/fts3/fts3.c +@@ -1819,11 +1819,11 @@ + ){ + int rc = SQLITE_OK; /* Return code */ + const char *zCsr = zNode; /* Cursor to iterate through node */ + const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ + char *zBuffer = 0; /* Buffer to load terms into */ +- int nAlloc = 0; /* Size of allocated buffer */ ++ i64 nAlloc = 0; /* Size of allocated buffer */ + int isFirstTerm = 1; /* True when processing first term on page */ + sqlite3_int64 iChild; /* Block id of child node to descend to */ + + /* Skip over the 'height' varint that occurs at the start of every + ** interior node. Then load the blockid of the left-child of the b-tree +@@ -1857,18 +1857,18 @@ + } + isFirstTerm = 0; + zCsr += fts3GetVarint32(zCsr, &nSuffix); + + assert( nPrefix>=0 && nSuffix>=0 ); +- if( &zCsr[nSuffix]>zEnd ){ ++ if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){ + rc = FTS_CORRUPT_VTAB; + goto finish_scan; + } +- if( nPrefix+nSuffix>nAlloc ){ ++ if( (i64)nPrefix+nSuffix>nAlloc ){ + char *zNew; +- nAlloc = (nPrefix+nSuffix) * 2; +- zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); ++ nAlloc = ((i64)nPrefix+nSuffix) * 2; ++ zNew = (char *)sqlite3_realloc64(zBuffer, nAlloc); + if( !zNew ){ + rc = SQLITE_NOMEM; + goto finish_scan; + } + zBuffer = zNew; + +Index: ext/fts3/fts3_write.c +================================================================== +--- ext/fts3/fts3_write.c ++++ ext/fts3/fts3_write.c +@@ -1372,19 +1372,23 @@ + + /* Because of the FTS3_NODE_PADDING bytes of padding, the following is + ** safe (no risk of overread) even if the node data is corrupted. */ + pNext += fts3GetVarint32(pNext, &nPrefix); + pNext += fts3GetVarint32(pNext, &nSuffix); +- if( nPrefix<0 || nSuffix<=0 +- || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] ++ if( nSuffix<=0 ++ || (&pReader->aNode[pReader->nNode] - pNext)pReader->nTermAlloc + ){ + return FTS_CORRUPT_VTAB; + } + +- if( nPrefix+nSuffix>pReader->nTermAlloc ){ +- int nNew = (nPrefix+nSuffix)*2; +- char *zNew = sqlite3_realloc(pReader->zTerm, nNew); ++ /* Both nPrefix and nSuffix were read by fts3GetVarint32() and so are ++ ** between 0 and 0x7FFFFFFF. But the sum of the two may cause integer ++ ** overflow - hence the (i64) casts. */ ++ if( (i64)nPrefix+nSuffix>(i64)pReader->nTermAlloc ){ ++ i64 nNew = ((i64)nPrefix+nSuffix)*2; ++ char *zNew = sqlite3_realloc64(pReader->zTerm, nNew); + if( !zNew ){ + return SQLITE_NOMEM; + } + pReader->zTerm = zNew; + pReader->nTermAlloc = nNew; +@@ -1402,11 +1406,11 @@ + + /* Check that the doclist does not appear to extend past the end of the + ** b-tree node. And that the final byte of the doclist is 0x00. If either + ** of these statements is untrue, then the data structure is corrupt. + */ +- if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] ++ if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)nDoclist + || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) + ){ + return FTS_CORRUPT_VTAB; + } + return SQLITE_OK; +@@ -3728,25 +3732,30 @@ + if( bFirst==0 ){ + p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nPrefix); + } + p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); + ++ if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){ ++ return SQLITE_CORRUPT_VTAB; ++ } + blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); + if( rc==SQLITE_OK ){ + memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); + p->term.n = nPrefix+nSuffix; + p->iOff += nSuffix; + if( p->iChild==0 ){ + p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); ++ if( (p->nNode-p->iOff)nDoclist ){ ++ return SQLITE_CORRUPT_VTAB; ++ } + p->aDoclist = &p->aNode[p->iOff]; + p->iOff += p->nDoclist; + } + } + } + + assert( p->iOff<=p->nNode ); +- + return rc; + } + + /* + ** Release all dynamic resources held by node-reader object *p. + +ADDED test/fts3corrupt4.test +Index: test/fts3corrupt4.test +================================================================== +--- test/fts3corrupt4.test ++++ test/fts3corrupt4.test +@@ -0,0 +1,147 @@ ++# 2006 September 9 ++# ++# The author disclaims copyright to this source code. In place of ++# a legal notice, here is a blessing: ++# ++# May you do good and not evil. ++# May you find forgiveness for yourself and forgive others. ++# May you share freely, never taking more than you give. ++# ++#************************************************************************* ++# This file implements regression tests for SQLite library. The ++# focus of this script is testing the FTS3 module. ++# ++# $Id: fts3aa.test,v 1.1 2007/08/20 17:38:42 shess Exp $ ++# ++ ++set testdir [file dirname $argv0] ++source $testdir/tester.tcl ++set testprefix fts3corrupt4 ++ ++# If SQLITE_ENABLE_FTS3 is defined, omit this file. ++ifcapable !fts3 { ++ finish_test ++ return ++} ++ ++do_execsql_test 1.0 { ++ BEGIN; ++ CREATE VIRTUAL TABLE ft USING fts3; ++ INSERT INTO ft VALUES('aback'); ++ INSERT INTO ft VALUES('abaft'); ++ INSERT INTO ft VALUES('abandon'); ++ COMMIT; ++} ++ ++proc blob {a} { binary decode hex $a } ++db func blob blob ++ ++do_execsql_test 1.1 { ++ SELECT quote(root) FROM ft_segdir; ++} {X'0005616261636B03010200030266740302020003046E646F6E03030200'} ++ ++do_execsql_test 1.2 { ++ UPDATE ft_segdir SET root = blob( ++ '0005616261636B03010200 FFFFFFFF0702 66740302020003046E646F6E03030200' ++ ); ++} ++ ++do_catchsql_test 1.3 { ++ SELECT * FROM ft WHERE ft MATCH 'abandon'; ++} {1 {database disk image is malformed}} ++ ++#------------------------------------------------------------------------- ++reset_db ++do_execsql_test 2.0.0 { ++ CREATE VIRTUAL TABLE ft USING fts3; ++ INSERT INTO ft(ft) VALUES('nodesize=32'); ++} ++do_test 2.0.1 { ++ for {set i 0} {$i < 12} {incr i} { ++ execsql { ++ BEGIN; ++ INSERT INTO ft VALUES('abc' || $i); ++ INSERT INTO ft VALUES('abc' || $i || 'x' ); ++ INSERT INTO ft VALUES('abc' || $i || 'xx' ); ++ COMMIT ++ } ++ } ++ execsql { ++ SELECT count(*) FROM ft_segdir; ++ SELECT count(*) FROM ft_segments; ++ } ++} {12 0} ++ ++do_execsql_test 2.1 { ++ INSERT INTO ft(ft) VALUES('merge=1,4'); ++ SELECT count(*) FROM ft_segdir; ++ SELECT count(*) FROM ft_segments; ++} {12 3} ++ ++do_execsql_test 2.2 { ++ SELECT quote(block) FROM ft_segments WHERE blockid=2 ++} {X'00056162633130031F0200'} ++ ++db func blob blob ++do_execsql_test 2.3.1 { ++ UPDATE ft_segments SET block = ++ blob('00056162633130031F0200 FFFFFFFF07FF55 66740302020003046E646F6E03030200') ++ WHERE blockid=2; ++} {} ++do_catchsql_test 2.3.2 { ++ INSERT INTO ft(ft) VALUES('merge=1,4'); ++} {1 {database disk image is malformed}} ++ ++do_execsql_test 2.4.1 { ++ UPDATE ft_segments SET block = ++ blob('00056162633130031F0200 02FFFFFFFF07 66740302020003046E646F6E03030200') ++ WHERE blockid=2; ++} {} ++do_catchsql_test 2.4.2 { ++ INSERT INTO ft(ft) VALUES('merge=1,4'); ++} {1 {database disk image is malformed}} ++ ++do_execsql_test 2.5.1 { ++ UPDATE ft_segments SET block = ++ blob('00056162633130031F0200 0202 6674 FFFFFF070302020003046E646F6E030200') ++ WHERE blockid=2; ++} {} ++do_catchsql_test 2.5.2 { ++ INSERT INTO ft(ft) VALUES('merge=1,4'); ++} {1 {database disk image is malformed}} ++ ++#------------------------------------------------------------------------- ++reset_db ++do_execsql_test 3.0.0 { ++ CREATE VIRTUAL TABLE ft USING fts3; ++ INSERT INTO ft(ft) VALUES('nodesize=32'); ++} ++do_test 3.0.1 { ++ execsql BEGIN ++ for {set i 0} {$i < 20} {incr i} { ++ execsql { INSERT INTO ft VALUES('abc' || $i) } ++ } ++ execsql { ++ COMMIT; ++ SELECT count(*) FROM ft_segdir; ++ SELECT count(*) FROM ft_segments; ++ } ++} {1 5} ++ ++do_execsql_test 3.1 { ++ SELECT quote(root) FROM ft_segdir ++} {X'0101056162633132040136030132030136'} ++ ++db func blob blob ++do_execsql_test 3.2 { ++ UPDATE ft_segdir ++ SET root = blob('0101056162633132FFFFFFFF070236030132030136'); ++} ++ ++do_catchsql_test 3.1 { ++ SELECT * FROM ft WHERE ft MATCH 'abc20' ++} {1 {database disk image is malformed}} ++ ++finish_test ++ ++ + +Index: test/permutations.test +================================================================== +--- test/permutations.test ++++ test/permutations.test +@@ -260,10 +260,11 @@ + fts3ae.test fts3af.test fts3ag.test fts3ah.test + fts3ai.test fts3aj.test fts3ak.test fts3al.test + fts3am.test fts3an.test fts3ao.test fts3atoken.test + fts3auto.test fts3aux1.test fts3aux2.test fts3b.test + fts3comp1.test fts3conf.test fts3corrupt2.test fts3corrupt.test ++ fts3corrupt4.test + fts3cov.test fts3c.test fts3defer2.test fts3defer3.test + fts3defer.test fts3drop.test fts3d.test fts3e.test + fts3expr2.test fts3expr3.test fts3expr4.test fts3expr5.test + fts3expr.test fts3fault2.test fts3fault.test fts3first.test + fts3join.test fts3malloc.test fts3matchinfo.test fts3near.test + + diff --git a/sqlite.spec b/sqlite.spec index 93fc9d6..f95c831 100644 --- a/sqlite.spec +++ b/sqlite.spec @@ -10,7 +10,7 @@ Summary: Library that implements an embeddable SQL database engine Name: sqlite Version: %{rpmver} -Release: 4%{?dist} +Release: 5%{?dist} License: Public Domain Group: Applications/Databases URL: http://www.sqlite.org/ @@ -43,6 +43,8 @@ Patch10: sqlite-3.22.0-fts3rank-big-endian.patch Patch11: sqlite-3.22.0-walro2-filesize.patch # Upstream: https://www.sqlite.org/cgi/src/timeline?r=corrupt-schema Patch12: sqlite-3.22.0-corrupt-schema.patch +# Upstream: https://www.sqlite.org/src/info/d44318f59044162e +Patch13: sqlite-3.26.0-fts_corrupt_db.patch BuildRequires: ncurses-devel readline-devel glibc-devel BuildRequires: autoconf @@ -150,6 +152,7 @@ This package contains the analysis program for %{name}. %patch10 -p0 %patch11 -p0 %patch12 -p0 +%patch13 -p0 autoconf # Rerun with new autoconf to add support for aarm64 @@ -251,6 +254,9 @@ make test %endif %changelog +* Mon Dec 17 2018 Petr Kubat - 3.22.0-5 +- Fixed fts3/4 corrupt database exploit (#1659677) + * Wed Mar 21 2018 Petr Kubat - 3.22.0-4 - Fixed CVE-2018-8740 (#1558809)