SQLite

Check-in [89fda074f6]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix some trivial cases where database corruption was causing an error code other than SQLITE_CORRUPT to be returned. (CVS 5690)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 89fda074f6b4959c32f1083badba3c73cffb4995
User & Date: danielk1977 2008-09-10 17:53:36.000
Context
2008-09-11
10:29
Fix a couple of problems with variable initializations picked up by stricter compilers. (CVS 5691) (check-in: f4293d1480 user: danielk1977 tags: trunk)
2008-09-10
17:53
Fix some trivial cases where database corruption was causing an error code other than SQLITE_CORRUPT to be returned. (CVS 5690) (check-in: 89fda074f6 user: danielk1977 tags: trunk)
14:45
Fix for handling database files corrupted in such a was as to make a b-tree page a direct or indirect descendant of itself. (CVS 5689) (check-in: 93545861a7 user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.510 2008/09/10 14:45:58 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.511 2008/09/10 17:53:36 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

1080
1081
1082
1083
1084
1085
1086











1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  pPage->pDbPage = pDbPage;
  pPage->pBt = pBt;
  pPage->pgno = pgno;
  pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;
  *ppPage = pPage;
  return SQLITE_OK;
}












/*
** Get a page from the pager and initialize it.  This routine
** is just a convenience wrapper around separate calls to
** sqlite3BtreeGetPage() and sqlite3BtreeInitPage().
*/
static int getAndInitPage(
  BtShared *pBt,          /* The database file */
  Pgno pgno,           /* Number of the page to get */
  MemPage **ppPage,    /* Write the page pointer here */
  MemPage *pParent     /* Parent of the page */
){
  int rc;
  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( !pParent || pParent->isInit );
  if( pgno==0 ){
    return SQLITE_CORRUPT_BKPT; 
  }
  rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0);
  if( rc==SQLITE_OK ){
    if( (*ppPage)->isInit==0 ){
      rc = sqlite3BtreeInitPage(*ppPage, pParent);
    }else if( pParent && ((*ppPage)==pParent || (*ppPage)->pParent!=pParent) ){







>
>
>
>
>
>
>
>
>
>
>















|







1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
  pPage->pDbPage = pDbPage;
  pPage->pBt = pBt;
  pPage->pgno = pgno;
  pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;
  *ppPage = pPage;
  return SQLITE_OK;
}

/*
** Return the size of the database file in pages.  Or return -1 if
** there is any kind of error.
*/
static int pagerPagecount(Pager *pPager){
  int rc;
  int nPage;
  rc = sqlite3PagerPagecount(pPager, &nPage);
  return (rc==SQLITE_OK?nPage:-1);
}

/*
** Get a page from the pager and initialize it.  This routine
** is just a convenience wrapper around separate calls to
** sqlite3BtreeGetPage() and sqlite3BtreeInitPage().
*/
static int getAndInitPage(
  BtShared *pBt,          /* The database file */
  Pgno pgno,           /* Number of the page to get */
  MemPage **ppPage,    /* Write the page pointer here */
  MemPage *pParent     /* Parent of the page */
){
  int rc;
  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( !pParent || pParent->isInit );
  if( pgno==0 || pgno>pagerPagecount(pBt->pPager) ){
    return SQLITE_CORRUPT_BKPT; 
  }
  rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0);
  if( rc==SQLITE_OK ){
    if( (*ppPage)->isInit==0 ){
      rc = sqlite3BtreeInitPage(*ppPage, pParent);
    }else if( pParent && ((*ppPage)==pParent || (*ppPage)->pParent!=pParent) ){
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049

trans_begun:
  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;
}

/*
** Return the size of the database file in pages.  Or return -1 if
** there is any kind of error.
*/
static int pagerPagecount(Pager *pPager){
  int rc;
  int nPage;
  rc = sqlite3PagerPagecount(pPager, &nPage);
  return (rc==SQLITE_OK?nPage:-1);
}


#ifndef SQLITE_OMIT_AUTOVACUUM

/*
** Set the pointer-map entries for all children of page pPage. Also, if
** pPage contains cells that point to overflow pages, set the pointer
** map entries for the overflow pages as well.







<
<
<
<
<
<
<
<
<
<
<







2036
2037
2038
2039
2040
2041
2042











2043
2044
2045
2046
2047
2048
2049

trans_begun:
  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;
}













#ifndef SQLITE_OMIT_AUTOVACUUM

/*
** Set the pointer-map entries for all children of page pPage. Also, if
** pPage contains cells that point to overflow pages, set the pointer
** map entries for the overflow pages as well.
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
  nKey = (pPage->intKey ? 0 : pCur->info.nKey);

  if( skipKey ){
    offset += nKey;
  }
  if( offset+amt > nKey+pCur->info.nData ){
    /* Trying to read or write past the end of the data is an error */
    return SQLITE_ERROR;
  }

  /* Check if data must be read/written to/from the btree page itself. */
  if( offset<pCur->info.nLocal ){
    int a = amt;
    if( a+offset>pCur->info.nLocal ){
      a = pCur->info.nLocal - offset;







|







3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
  nKey = (pPage->intKey ? 0 : pCur->info.nKey);

  if( skipKey ){
    offset += nKey;
  }
  if( offset+amt > nKey+pCur->info.nData ){
    /* Trying to read or write past the end of the data is an error */
    return SQLITE_CORRUPT_BKPT;
  }

  /* Check if data must be read/written to/from the btree page itself. */
  if( offset<pCur->info.nLocal ){
    int a = amt;
    if( a+offset>pCur->info.nLocal ){
      a = pCur->info.nLocal - offset;
Changes to test/corruptB.test.
12
13
14
15
16
17
18




19
20
21
22
23
24
25
26
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.  It specifically focuses
# on loops in the B-Tree structure. A loop is formed in a B-Tree structure
# when there exists a page that is both an a descendent or ancestor of
# itself.
#




# $Id: corruptB.test,v 1.1 2008/09/10 14:45:58 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl


do_test corruptB-1.1 {
  execsql {







>
>
>
>
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.  It specifically focuses
# on loops in the B-Tree structure. A loop is formed in a B-Tree structure
# when there exists a page that is both an a descendent or ancestor of
# itself.
#
# Also test that an SQLITE_CORRUPT error is returned if a B-Tree page
# contains a (corrupt) reference to a page greater than the configured
# maximum page number.
#
# $Id: corruptB.test,v 1.2 2008/09/10 17:53:36 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl


do_test corruptB-1.1 {
  execsql {
135
136
137
138
139
140
141
142





































143
144
  set cell_offset [hexio_get_int [hexio_read test.db [expr $c_offset+12] 2]]
  hexio_write test.db [expr $c_offset+$cell_offset] [hexio_render_int32 $::root]
} {4}
do_test corruptB-1.9.2 {
  sqlite3 db test.db
  catchsql { SELECT * FROM t1 }
} {1 {database disk image is malformed}}






































finish_test









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


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
  set cell_offset [hexio_get_int [hexio_read test.db [expr $c_offset+12] 2]]
  hexio_write test.db [expr $c_offset+$cell_offset] [hexio_render_int32 $::root]
} {4}
do_test corruptB-1.9.2 {
  sqlite3 db test.db
  catchsql { SELECT * FROM t1 }
} {1 {database disk image is malformed}}

#---------------------------------------------------------------------------

do_test corruptB-2.1.1 {
  db close
  file copy -force bak.db test.db
  hexio_write test.db [expr $offset+8] [hexio_render_int32 0x6FFFFFFF]
} {4}
do_test corruptB-2.1.2 {
  sqlite3 db test.db
  catchsql { SELECT * FROM t1 }
} {1 {database disk image is malformed}}

#---------------------------------------------------------------------------

# Corrupt the header-size field of a database record.
#
do_test corruptB-3.1.1 {
  db close
  file copy -force bak.db test.db
  sqlite3 db test.db
  set v [string repeat abcdefghij 200]
  execsql {
    CREATE TABLE t2(a);
    INSERT INTO t2 VALUES($v);
  }
  set t2_root [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't2'}]
  set iPage [expr ($t2_root-1)*1024]
  set iCellarray [expr $iPage + 8]
  set iRecord [hexio_get_int [hexio_read test.db $iCellarray 2]]
  db close
  hexio_write test.db [expr $iPage+$iRecord+3] FF00
} {2}
do_test corruptB-3.1.2 {
  sqlite3 db test.db
  catchsql { SELECT * FROM t2 }
} {1 {database disk image is malformed}}

finish_test

Changes to test/fuzz3.test.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The focus
# of this file is checking the libraries response to subtly corrupting
# the database file by changing the values of pseudo-randomly selected
# bytes.
#
# $Id: fuzz3.test,v 1.1 2008/09/09 18:28:07 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl


expr srand(123)








|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The focus
# of this file is checking the libraries response to subtly corrupting
# the database file by changing the values of pseudo-randomly selected
# bytes.
#
# $Id: fuzz3.test,v 1.2 2008/09/10 17:53:36 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl


expr srand(123)

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
} {}

set ::cksum [db_checksum]
do_test fuzz3-2 {
  db_checksum
} $::cksum

for {set ii 0} {$ii < 10000} {incr ii} {
  purge_pcache

  # Randomly modify a single byte of the database file somewhere within
  # the first 100KB of the file.
  set iNew [expr int(rand()*5*1024*256)]
  set iOld [modify_database $iNew]

  set iTest 2
  foreach sql {

    {SELECT * FROM t1}                 
    {SELECT * FROM t2}                 
    {SELECT * FROM t1 ORDER BY a}      
    {SELECT * FROM t2 ORDER BY d}      
    {SELECT * FROM t1 WHERE a = (SELECT a FROM t1 WHERE rowid=25)} 
    {SELECT * FROM t2 WHERE d = (SELECT d FROM t2 WHERE rowid=1)}  
    {SELECT * FROM t2 WHERE d = (SELECT d FROM t2 WHERE rowid=50)} 
    {PRAGMA integrity_check}           
  } {
    do_test fuzz3-$ii.$iNew.[incr iTest] {
      foreach {rc msg} [catchsql $sql] {}







|







|

>



<







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
} {}

set ::cksum [db_checksum]
do_test fuzz3-2 {
  db_checksum
} $::cksum

for {set ii 0} {$ii < 5000} {incr ii} {
  purge_pcache

  # Randomly modify a single byte of the database file somewhere within
  # the first 100KB of the file.
  set iNew [expr int(rand()*5*1024*256)]
  set iOld [modify_database $iNew]

  set iTest 0
  foreach sql {
    {SELECT * FROM t2 ORDER BY d}      
    {SELECT * FROM t1}                 
    {SELECT * FROM t2}                 
    {SELECT * FROM t1 ORDER BY a}      

    {SELECT * FROM t1 WHERE a = (SELECT a FROM t1 WHERE rowid=25)} 
    {SELECT * FROM t2 WHERE d = (SELECT d FROM t2 WHERE rowid=1)}  
    {SELECT * FROM t2 WHERE d = (SELECT d FROM t2 WHERE rowid=50)} 
    {PRAGMA integrity_check}           
  } {
    do_test fuzz3-$ii.$iNew.[incr iTest] {
      foreach {rc msg} [catchsql $sql] {}