SQLite

Check-in [395bb3e677]
Login

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

Overview
Comment:Prevent a possible infinite loop when trying to DROP a table from a corrupt database.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 395bb3e677a6551b06ba96fc58c393132b93d1e8
User & Date: drh 2015-03-30 23:43:56.191
References
2015-04-06
22:05
Detect and suppress an endless loops in clearDatabasePage() that might result from a corrupt database file. This is an edited cherry-pick from [30011ad2f55c] and [395bb3e677a]. (Leaf check-in: e5f5ef008d user: drh tags: apple-osx-385)
Context
2015-03-31
11:42
Ensure that comparison operators do not mess up the MEM_Dyn flag on registers when reverting affinity changes. (check-in: 02e3c88fbf user: drh tags: trunk)
00:10
Merge all recent trunk enhancements and fixes into the sessions branch. (check-in: df94e61f93 user: drh tags: sessions)
2015-03-30
23:52
Merge the latest trunk enhancements, and especially the fix for the infinite loop on a DROP TABLE in a corrupt database, into the apple-osx branch. (check-in: 4d42bf5e18 user: drh tags: apple-osx)
23:43
Prevent a possible infinite loop when trying to DROP a table from a corrupt database. (check-in: 395bb3e677 user: drh tags: trunk)
2015-03-27
18:20
Enhancements to OSTRACE usage in the Win32 VFS. (check-in: ab5800291e user: mistachkin tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
7974
7975
7976
7977
7978
7979
7980

7981
7982
7983
7984
7985
7986
7987
7988


7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
){
  MemPage *pPage;
  int rc;
  unsigned char *pCell;
  int i;
  int hdr;
  u16 szCell;


  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pgno>btreePagecount(pBt) ){
    return SQLITE_CORRUPT_BKPT;
  }

  rc = getAndInitPage(pBt, pgno, &pPage, 0);
  if( rc ) return rc;


  hdr = pPage->hdrOffset;
  for(i=0; i<pPage->nCell; i++){
    pCell = findCell(pPage, i);
    if( !pPage->leaf ){
      rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
      if( rc ) goto cleardatabasepage_out;
    }
    rc = clearCell(pPage, pCell, &szCell);
    if( rc ) goto cleardatabasepage_out;
  }
  if( !pPage->leaf ){
    rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
    if( rc ) goto cleardatabasepage_out;
  }else if( pnChange ){
    assert( pPage->intKey );
    *pnChange += pPage->nCell;
  }
  if( freePageFlag ){







>








>
>



|






|







7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
8007
8008
8009
){
  MemPage *pPage;
  int rc;
  unsigned char *pCell;
  int i;
  int hdr;
  u16 szCell;
  u8 hasChildren;

  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pgno>btreePagecount(pBt) ){
    return SQLITE_CORRUPT_BKPT;
  }

  rc = getAndInitPage(pBt, pgno, &pPage, 0);
  if( rc ) return rc;
  hasChildren = !pPage->leaf;
  pPage->leaf = 1;  /* Block looping if the database is corrupt */
  hdr = pPage->hdrOffset;
  for(i=0; i<pPage->nCell; i++){
    pCell = findCell(pPage, i);
    if( hasChildren ){
      rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
      if( rc ) goto cleardatabasepage_out;
    }
    rc = clearCell(pPage, pCell, &szCell);
    if( rc ) goto cleardatabasepage_out;
  }
  if( hasChildren ){
    rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
    if( rc ) goto cleardatabasepage_out;
  }else if( pnChange ){
    assert( pPage->intKey );
    *pnChange += pPage->nCell;
  }
  if( freePageFlag ){
Added test/corruptJ.test.
































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# 2015-03-30
#
# 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.
#
#***********************************************************************
#
# Corruption consisting of a database page that thinks it is a child
# of itself.
#

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

if {[permutation]=="mmap"} {
  finish_test
  return
}

# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec
database_may_be_corrupt

# Initialize the database.
#
do_execsql_test 1.1 {
  PRAGMA page_size=1024;
  PRAGMA auto_vacuum=0;
  CREATE TABLE t1(a,b);
  WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10)
    INSERT INTO t1(a,b) SELECT i, zeroblob(700) FROM c;
} {}
db close

# Corrupt the root page of the t1 table such that the left-child pointer
# for the very first cell points back to the root.  Then try to DROP the
# table.  The clearDatabasePage() routine should not loop.
#
do_test 1.2 {
  hexio_write test.db [expr {2*1024-2}] 02
  sqlite3 db test.db
  catchsql { DROP TABLE t1 }
} {1 {database disk image is malformed}}

# Similar test using a WITHOUT ROWID table
#
do_test 2.1 {
  db close
  forcedelete test.db
  sqlite3 db test.db
  db eval {
    PRAGMA page_size=1024;
    PRAGMA auto_vacuum=0;
    CREATE TABLE t1(a,b,PRIMARY KEY(a,b)) WITHOUT ROWID;
    WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<100)
      INSERT INTO t1(a,b) SELECT i, zeroblob(200) FROM c;
  }
} {}

# The table is three levels deep.  Corrupt the left child of an intermediate
# page so that it points back to the root page.
#
do_test 2.2 {
  db close
  hexio_read test.db [expr {9*1024+391}] 8
} {00000008814D0401}
do_test 2.2b {
  hexio_write test.db [expr {9*1024+391}] 00000002
  sqlite3 db test.db
  catchsql { DROP TABLE t1 }
} {0 {}}

finish_test