/ Check-in [251a7590]
Login

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

Overview
Comment:Smaller and faster PRAGMA integrity_check that also does a better job of detecting errors. Some output text describing discovered file corruption has changed for clarity.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 251a7590ff4f65f59a1c871892533e4e2c544515
User & Date: drh 2015-07-02 16:17:30
Context
2015-07-02
16:29
Fix a (harmless) shadowed local variable definition in the integrity_check logic. check-in: 3a26a919 user: drh tags: trunk
16:17
Smaller and faster PRAGMA integrity_check that also does a better job of detecting errors. Some output text describing discovered file corruption has changed for clarity. check-in: 251a7590 user: drh tags: trunk
15:52
Remove "#ifdef SQLITE_ENABLE_FTS5" from individual fts5 source files. Add a single "#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5)" to fts5.c. check-in: 7819002e user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945












8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
....
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977


8978
8979
8980
8981
8982



































8983
8984
8985
8986
8987
8988
8989

8990









8991
8992
8993
8994
8995







8996
8997
8998
8999

9000
9001
9002
9003
9004


9005
9006
9007



9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020

9021
9022
9023
9024
9025
9026
9027

9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067


9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082
9083
9084
9085
9086
9087





9088
9089
9090
9091
9092
9093
9094
9095
9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115


9116
9117
9118

9119
9120
9121
9122
9123
9124
9125
....
9130
9131
9132
9133
9134
9135
9136



9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
9167
....
9188
9189
9190
9191
9192
9193
9194
9195
9196
9197

9198

9199
9200
9201
9202
9203
9204
9205
....
9235
9236
9237
9238
9239
9240
9241


9242

9243
9244
9245
9246
9247
9248
9249
9250

9251
9252
9253
9254
9255
9256
9257
**      3.  Check the integrity of overflow pages.
**      4.  Recursively call checkTreePage on all children.
**      5.  Verify that the depth of all children is the same.
*/
static int checkTreePage(
  IntegrityCk *pCheck,  /* Context for the sanity check */
  int iPage,            /* Page number of the page to check */
  i64 *pnParentMinKey, 
  i64 *pnParentMaxKey
){
  MemPage *pPage = 0;
  int i, rc, depth, d2, pgno, cnt;
  int hdr, cellStart;
  int nCell;
  u8 *data;
  BtShared *pBt;
  int usableSize;
  u32 *heap = 0;












  u32 x, prev = 0;
  i64 nMinKey = 0;
  i64 nMaxKey = 0;
  const char *saved_zPfx = pCheck->zPfx;
  int saved_v1 = pCheck->v1;
  int saved_v2 = pCheck->v2;

  /* Check that the page exists
  */
  pBt = pCheck->pBt;
................................................................................
  if( iPage==0 ) return 0;
  if( checkRef(pCheck, iPage) ) return 0;
  pCheck->zPfx = "Page %d: ";
  pCheck->v1 = iPage;
  if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
    checkAppendMsg(pCheck,
       "unable to get the page. error code=%d", rc);
    depth = -1;
    goto end_of_check;
  }

  /* Clear MemPage.isInit to make sure the corruption detection code in
  ** btreeInitPage() is executed.  */
  pPage->isInit = 0;
  if( (rc = btreeInitPage(pPage))!=0 ){
    assert( rc==SQLITE_CORRUPT );  /* The only possible error from InitPage */
    checkAppendMsg(pCheck,
                   "btreeInitPage() returns error code %d", rc);
    depth = -1;
    goto end_of_check;
  }



  /* Check out all the cells.
  */
  depth = 0;
  pCheck->zPfx = "On tree page %d cell %d: ";



































  for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
    u8 *pCell;
    u32 sz;
    CellInfo info;

    /* Check payload overflow pages
    */

    pCheck->v2 = i;









    pCell = findCell(pPage,i);
    pPage->xParseCell(pPage, pCell, &info);
    sz = info.nPayload;
    /* For intKey pages, check that the keys are in order.
    */







    if( pPage->intKey ){
      if( i==0 ){
        nMinKey = nMaxKey = info.nKey;
      }else if( info.nKey <= nMaxKey ){

        checkAppendMsg(pCheck,
           "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
      }
      nMaxKey = info.nKey;
    }


    if( (sz>info.nLocal) 
     && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
    ){



      int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
      Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
#ifndef SQLITE_OMIT_AUTOVACUUM
      if( pBt->autoVacuum ){
        checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
      }
#endif
      checkList(pCheck, 0, pgnoOvfl, nPage);
    }

    /* Check sanity of left child page.
    */
    if( !pPage->leaf ){

      pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
      if( pBt->autoVacuum ){
        checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
      }
#endif
      d2 = checkTreePage(pCheck, pgno, &nMinKey, i==0?NULL:&nMaxKey);

      if( i>0 && d2!=depth ){
        checkAppendMsg(pCheck, "Child page depth differs");
      }
      depth = d2;
    }
  }

  if( !pPage->leaf ){
    pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
    pCheck->zPfx = "On page %d at right child: ";
#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pBt->autoVacuum ){
      checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
    }
#endif
    d2 = checkTreePage(pCheck, pgno, NULL, !pPage->nCell?NULL:&nMaxKey);
    if( d2!=depth && iPage!=1 ){
      checkAppendMsg(pCheck, "Child page depth differs");
    }
  }
 
  /* For intKey leaf pages, check that the min/max keys are in order
  ** with any left/parent/right pages.
  */
  pCheck->zPfx = "Page %d: ";
  if( pPage->leaf && pPage->intKey ){
    /* if we are a left child page */
    if( pnParentMinKey ){
      /* if we are the left most child page */
      if( !pnParentMaxKey ){
        if( nMaxKey > *pnParentMinKey ){
          checkAppendMsg(pCheck,
              "Rowid %lld out of order (max larger than parent min of %lld)",
              nMaxKey, *pnParentMinKey);
        }
      }else{
        if( nMinKey <= *pnParentMinKey ){
          checkAppendMsg(pCheck,
              "Rowid %lld out of order (min less than parent min of %lld)",
              nMinKey, *pnParentMinKey);


        }
        if( nMaxKey > *pnParentMaxKey ){
          checkAppendMsg(pCheck,
              "Rowid %lld out of order (max larger than parent max of %lld)",
              nMaxKey, *pnParentMaxKey);
        }
        *pnParentMinKey = nMaxKey;
      }
    /* else if we're a right child page */
    } else if( pnParentMaxKey ){
      if( nMinKey <= *pnParentMaxKey ){
        checkAppendMsg(pCheck,
            "Rowid %lld out of order (min less than parent max of %lld)",
            nMinKey, *pnParentMaxKey);
      }
    }
  }

  /* Check for complete coverage of the page
  */





  data = pPage->aData;
  hdr = pPage->hdrOffset;
  heap = pCheck->heap;
  heap[0] = 0;
  pCheck->zPfx = 0;
  {
    int contentOffset = get2byteNotZero(&data[hdr+5]);
    assert( contentOffset<=usableSize );  /* Enforced by btreeInitPage() */
    btreeHeapInsert(heap, contentOffset-1);
    /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
    ** number of cells on the page. */
    nCell = get2byte(&data[hdr+3]);
    /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
    ** immediately follows the b-tree page header. */
    cellStart = hdr + 12 - 4*pPage->leaf;
    /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
    ** integer offsets to the cell contents. */
    for(i=nCell-1; i>=0; i--){
      u32 pc = get2byteAligned(&data[cellStart+i*2]);
      u32 size = pPage->xCellSize(pPage, &data[pc]);
      if( (int)(pc+size-1)>=usableSize ){
        pCheck->zPfx = 0;
        checkAppendMsg(pCheck,
            "Corruption detected in cell %d on page %d",i,iPage);
      }else{
        btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
      }
    }


    /* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
    ** is the offset of the first freeblock, or zero if there are no
    ** freeblocks on the page. */

    i = get2byte(&data[hdr+1]);
    while( i>0 ){
      int size, j;
      assert( i<=usableSize-4 );     /* Enforced by btreeInitPage() */
      size = get2byte(&data[i+2]);
      assert( i+size<=usableSize );  /* Enforced by btreeInitPage() */
      btreeHeapInsert(heap, (i<<16)|(i+size-1));
................................................................................
      j = get2byte(&data[i]);
      /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
      ** increasing offset. */
      assert( j==0 || j>i+size );  /* Enforced by btreeInitPage() */
      assert( j<=usableSize-4 );   /* Enforced by btreeInitPage() */
      i = j;
    }



    cnt = 0;
    assert( heap[0]>0 );
    assert( (heap[1]>>16)==0 );
    btreeHeapPull(heap,&prev);
    while( btreeHeapPull(heap,&x) ){
      if( (prev&0xffff)+1>(x>>16) ){
        checkAppendMsg(pCheck,
          "Multiple uses for byte %u of page %d", x>>16, iPage);
        break;
      }else{
        cnt += (x>>16) - (prev&0xffff) - 1;
        prev = x;
      }
    }
    cnt += usableSize - (prev&0xffff) - 1;
    /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
    ** is stored in the fifth field of the b-tree page header.
    ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
    ** number of fragmented free bytes within the cell content area.
    */
    if( heap[0]==0 && cnt!=data[hdr+7] ){
      checkAppendMsg(pCheck,
          "Fragmentation of %d bytes reported as %d on page %d",
          cnt, data[hdr+7], iPage);
    }
  }

end_of_check:
  releasePage(pPage);
  pCheck->zPfx = saved_zPfx;
  pCheck->v1 = saved_v1;
................................................................................
  Btree *p,     /* The btree to be checked */
  int *aRoot,   /* An array of root pages numbers for individual trees */
  int nRoot,    /* Number of entries in aRoot[] */
  int mxErr,    /* Stop reporting errors after this many */
  int *pnErr    /* Write number of errors seen to this variable */
){
  Pgno i;
  VVA_ONLY( int nRef );
  IntegrityCk sCheck;
  BtShared *pBt = p->pBt;

  char zErr[100];


  sqlite3BtreeEnter(p);
  assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
  assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 );
  sCheck.pBt = pBt;
  sCheck.pPager = pBt->pPager;
  sCheck.nPage = btreePagecount(sCheck.pBt);
................................................................................
  sCheck.zPfx = "Main freelist: ";
  checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
            get4byte(&pBt->pPage1->aData[36]));
  sCheck.zPfx = 0;

  /* Check all the tables.
  */


  for(i=0; (int)i<nRoot && sCheck.mxErr; i++){

    if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pBt->autoVacuum && aRoot[i]>1 ){
      checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
    }
#endif
    checkTreePage(&sCheck, aRoot[i], NULL, NULL);
  }


  /* Make sure every page in the file is referenced
  */
  for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
    if( getPageReferenced(&sCheck, i)==0 ){
      checkAppendMsg(&sCheck, "Page %d is never used", i);







|
|

|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>

<
<







 







<










<


>
>

<
|
<

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
<


<
<
>

>
>
>
>
>
>
>
>
>
|

<
<
<
>
>
>
>
>
>
>

<
<
<
>
|
<

|

>
>
|
<
<
>
>
>
|
|








<
<

>






|
>
|

<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
>
>
|
<
<
<
<
|
|
|
<
<
<
<
<
<
<
<
<
<


>
>
>
>
>
|
<
|
|
<
<
<
<
|
<
<
<
<
<
<
<
<
|
|
|
<
<
<
<
<



>
>
|

|
>







 







>
>
>
|









|



|





|


|







 







<


>

>







 







>
>

>






|

>







8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958


8959
8960
8961
8962
8963
8964
8965
....
8967
8968
8969
8970
8971
8972
8973

8974
8975
8976
8977
8978
8979
8980
8981
8982
8983

8984
8985
8986
8987
8988

8989

8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026


9027
9028


9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041



9042
9043
9044
9045
9046
9047
9048
9049



9050
9051

9052
9053
9054
9055
9056
9057


9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
9069
9070


9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082

9083
9084






























9085




9086
9087
9088




9089
9090
9091










9092
9093
9094
9095
9096
9097
9098
9099

9100
9101




9102








9103
9104
9105





9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
....
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
....
9187
9188
9189
9190
9191
9192
9193

9194
9195
9196
9197
9198
9199
9200
9201
9202
9203
9204
9205
....
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
9249
9250
9251
9252
9253
9254
9255
9256
9257
9258
9259
9260
9261
**      3.  Check the integrity of overflow pages.
**      4.  Recursively call checkTreePage on all children.
**      5.  Verify that the depth of all children is the same.
*/
static int checkTreePage(
  IntegrityCk *pCheck,  /* Context for the sanity check */
  int iPage,            /* Page number of the page to check */
  i64 *piMinKey,        /* Write minimum integer primary key here */
  i64 maxKey            /* Error if integer primary key greater than this */
){
  MemPage *pPage = 0;      /* The page being analyzed */
  int i;                   /* Loop counter */
  int rc;                  /* Result code from subroutine call */
  int depth = -1, d2;      /* Depth of a subtree */
  int pgno;                /* Page number */
  int nFrag;               /* Number of fragmented bytes on the page */
  int hdr;                 /* Offset to the page header */
  int cellStart;           /* Offset to the start of the cell pointer array */
  int nCell;               /* Number of cells */
  int doCoverageCheck = 1; /* True if cell coverage checking should be done */
  int keyCanBeEqual = 1;   /* True if IPK can be equal to maxKey
                           ** False if IPK must be strictly less than maxKey */
  u8 *data;                /* Page content */
  u8 *pCell;               /* Cell content */
  u8 *pCellIdx;            /* Next element of the cell pointer array */
  BtShared *pBt;           /* The BtShared object that owns pPage */
  u32 pc;                  /* Address of a cell */
  u32 usableSize;          /* Usable size of the page */
  u32 contentOffset;       /* Offset to the start of the cell content area */
  u32 *heap = 0;           /* Min-heap used for checking cell coverage */
  u32 x, prev = 0;


  const char *saved_zPfx = pCheck->zPfx;
  int saved_v1 = pCheck->v1;
  int saved_v2 = pCheck->v2;

  /* Check that the page exists
  */
  pBt = pCheck->pBt;
................................................................................
  if( iPage==0 ) return 0;
  if( checkRef(pCheck, iPage) ) return 0;
  pCheck->zPfx = "Page %d: ";
  pCheck->v1 = iPage;
  if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
    checkAppendMsg(pCheck,
       "unable to get the page. error code=%d", rc);

    goto end_of_check;
  }

  /* Clear MemPage.isInit to make sure the corruption detection code in
  ** btreeInitPage() is executed.  */
  pPage->isInit = 0;
  if( (rc = btreeInitPage(pPage))!=0 ){
    assert( rc==SQLITE_CORRUPT );  /* The only possible error from InitPage */
    checkAppendMsg(pCheck,
                   "btreeInitPage() returns error code %d", rc);

    goto end_of_check;
  }
  data = pPage->aData;
  hdr = pPage->hdrOffset;


  /* Set up for cell analysis */

  pCheck->zPfx = "On tree page %d cell %d: ";
  contentOffset = get2byteNotZero(&data[hdr+5]);
  assert( contentOffset<=usableSize );  /* Enforced by btreeInitPage() */

  /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
  ** number of cells on the page. */
  nCell = get2byte(&data[hdr+3]);
  assert( pPage->nCell==nCell );

  /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
  ** immediately follows the b-tree page header. */
  cellStart = hdr + 12 - 4*pPage->leaf;
  assert( pPage->aCellIdx==&data[cellStart] );
  pCellIdx = &data[cellStart + 2*(nCell-1)];

  if( !pPage->leaf ){
    /* Analyze the right-child page of internal pages */
    pgno = get4byte(&data[hdr+8]);
#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pBt->autoVacuum ){
      pCheck->zPfx = "On page %d at right child: ";
      checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
    }
#endif
    depth = checkTreePage(pCheck, pgno, &maxKey, maxKey);
    keyCanBeEqual = 0;
  }else{
    /* For leaf pages, the coverage check will occur in the same loop
    ** as the other cell checks, so initialize the heap.  */
    heap = pCheck->heap;
    heap[0] = 0;
    btreeHeapInsert(heap, contentOffset-1);
  }

  /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
  ** integer offsets to the cell contents. */
  for(i=nCell-1; i>=0 && pCheck->mxErr; i--){


    CellInfo info;



    /* Check cell size */
    pCheck->v2 = i;
    assert( pCellIdx==&data[cellStart + i*2] );
    pc = get2byteAligned(pCellIdx);
    pCellIdx -= 2;
    if( pc<contentOffset || pc>usableSize-4 ){
      checkAppendMsg(pCheck, "Offset %d out of range %d..%d",
                             pc, contentOffset, usableSize-4);
      doCoverageCheck = 0;
      continue;
    }
    pCell = &data[pc];
    pPage->xParseCell(pPage, pCell, &info);



    if( pc+info.nSize>usableSize ){
      checkAppendMsg(pCheck, "Extends off end of page");
      doCoverageCheck = 0;
      continue;
    }

    /* Check for integer primary key out of range */
    if( pPage->intKey ){



      if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){
        checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey);

      }
      maxKey = info.nKey;
    }

    /* Check the content overflow list */
    if( info.nPayload>info.nLocal ){


      int nPage;       /* Number of pages on the overflow chain */
      Pgno pgnoOvfl;   /* First page of the overflow chain */
      assert( pc + info.iOverflow <= usableSize );
      nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
      pgnoOvfl = get4byte(&pCell[info.iOverflow]);
#ifndef SQLITE_OMIT_AUTOVACUUM
      if( pBt->autoVacuum ){
        checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
      }
#endif
      checkList(pCheck, 0, pgnoOvfl, nPage);
    }



    if( !pPage->leaf ){
      /* Check sanity of left child page for internal pages */
      pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
      if( pBt->autoVacuum ){
        checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
      }
#endif
      d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey);
      keyCanBeEqual = 0;
      if( d2!=depth ){
        checkAppendMsg(pCheck, "Child page depth differs");

        depth = d2;
      }






























    }else{




      /* Populate the coverage-checking heap for leaf pages */
      btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1));
    }




  }
  *piMinKey = maxKey;











  /* Check for complete coverage of the page
  */
  pCheck->zPfx = 0;
  if( doCoverageCheck && pCheck->mxErr>0 ){
    /* For leaf pages, the min-heap has already been initialized and the
    ** cells have already been inserted.  But for internal pages, that has
    ** not yet been done, so do it now */
    if( !pPage->leaf ){

      heap = pCheck->heap;
      heap[0] = 0;




      btreeHeapInsert(heap, contentOffset-1);








      for(i=nCell-1; i>=0; i--){
        u32 pc = get2byteAligned(&data[cellStart+i*2]);
        u32 size = pPage->xCellSize(pPage, &data[pc]);





        btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
      }
    }
    /* Add the freeblocks to the min-heap
    **
    ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
    ** is the offset of the first freeblock, or zero if there are no
    ** freeblocks on the page. 
    */
    i = get2byte(&data[hdr+1]);
    while( i>0 ){
      int size, j;
      assert( i<=usableSize-4 );     /* Enforced by btreeInitPage() */
      size = get2byte(&data[i+2]);
      assert( i+size<=usableSize );  /* Enforced by btreeInitPage() */
      btreeHeapInsert(heap, (i<<16)|(i+size-1));
................................................................................
      j = get2byte(&data[i]);
      /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
      ** increasing offset. */
      assert( j==0 || j>i+size );  /* Enforced by btreeInitPage() */
      assert( j<=usableSize-4 );   /* Enforced by btreeInitPage() */
      i = j;
    }
    /* Analyze the min-heap looking for overlap between cells and/or 
    ** freeblocks, and counting the number of untracked bytes in nFrag.
    */
    nFrag = 0;
    assert( heap[0]>0 );
    assert( (heap[1]>>16)==0 );
    btreeHeapPull(heap,&prev);
    while( btreeHeapPull(heap,&x) ){
      if( (prev&0xffff)+1>(x>>16) ){
        checkAppendMsg(pCheck,
          "Multiple uses for byte %u of page %d", x>>16, iPage);
        break;
      }else{
        nFrag += (x>>16) - (prev&0xffff) - 1;
        prev = x;
      }
    }
    nFrag += usableSize - (prev&0xffff) - 1;
    /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
    ** is stored in the fifth field of the b-tree page header.
    ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
    ** number of fragmented free bytes within the cell content area.
    */
    if( heap[0]==0 && nFrag!=data[hdr+7] ){
      checkAppendMsg(pCheck,
          "Fragmentation of %d bytes reported as %d on page %d",
          nFrag, data[hdr+7], iPage);
    }
  }

end_of_check:
  releasePage(pPage);
  pCheck->zPfx = saved_zPfx;
  pCheck->v1 = saved_v1;
................................................................................
  Btree *p,     /* The btree to be checked */
  int *aRoot,   /* An array of root pages numbers for individual trees */
  int nRoot,    /* Number of entries in aRoot[] */
  int mxErr,    /* Stop reporting errors after this many */
  int *pnErr    /* Write number of errors seen to this variable */
){
  Pgno i;

  IntegrityCk sCheck;
  BtShared *pBt = p->pBt;
  int savedDbFlags = pBt->db->flags;
  char zErr[100];
  VVA_ONLY( int nRef );

  sqlite3BtreeEnter(p);
  assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
  assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 );
  sCheck.pBt = pBt;
  sCheck.pPager = pBt->pPager;
  sCheck.nPage = btreePagecount(sCheck.pBt);
................................................................................
  sCheck.zPfx = "Main freelist: ";
  checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
            get4byte(&pBt->pPage1->aData[36]));
  sCheck.zPfx = 0;

  /* Check all the tables.
  */
  testcase( pBt->db->flags & SQLITE_CellSizeCk );
  pBt->db->flags &= ~SQLITE_CellSizeCk;
  for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
    i64 notUsed;
    if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pBt->autoVacuum && aRoot[i]>1 ){
      checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
    }
#endif
    checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
  }
  pBt->db->flags = savedDbFlags;

  /* Make sure every page in the file is referenced
  */
  for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
    if( getPageReferenced(&sCheck, i)==0 ){
      checkAppendMsg(&sCheck, "Page %d is never used", i);

Changes to test/corrupt2.test.

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  db2 eval {SELECT rowid FROM t1} {
    set result [db2 eval {pragma integrity_check}]
    break
  }
  set result
} {{*** in database main ***
On tree page 2 cell 0: 2nd reference to page 10
On tree page 2 cell 1: Child page depth differs
Page 4 is never used}}

db2 close

proc corruption_test {args} {
  set A(-corrupt) {}
  set A(-sqlprep) {}







<







245
246
247
248
249
250
251

252
253
254
255
256
257
258
  db2 eval {SELECT rowid FROM t1} {
    set result [db2 eval {pragma integrity_check}]
    break
  }
  set result
} {{*** in database main ***
On tree page 2 cell 0: 2nd reference to page 10

Page 4 is never used}}

db2 close

proc corruption_test {args} {
  set A(-corrupt) {}
  set A(-sqlprep) {}

Changes to test/corrupt7.test.

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
  hexio_get_int [hexio_read test.db 20 1]
} 0      ;# Unused bytes per page is 0

integrity_check corrupt7-1.4

# Deliberately corrupt some of the cell offsets in the btree page
# on page 2 of the database.
#
# The error message is different depending on whether or not the
# SQLITE_ENABLE_OVERSIZE_CELL_CHECK compile-time option is engaged.
#
ifcapable oversize_cell_check {
  do_test corrupt7-2.1 {
    db close
    hexio_write test.db 1062 FF
    sqlite3 db test.db
    db eval {PRAGMA integrity_check(1)}
  } {{*** in database main ***
Page 2: btreeInitPage() returns error code 11}}

  do_test corrupt7-2.2 {
    db close
    hexio_write test.db 1062 04
    sqlite3 db test.db
    db eval {PRAGMA integrity_check(1)}
  } {{*** in database main ***
Page 2: btreeInitPage() returns error code 11}}
} else {
  do_test corrupt7-2.1 {
    db close
    hexio_write test.db 1062 FF
    sqlite3 db test.db
    db eval {PRAGMA integrity_check(1)}
  } {{*** in database main ***
Corruption detected in cell 15 on page 2}}
  do_test corrupt7-2.2 {
    db close
    hexio_write test.db 1062 04
    sqlite3 db test.db
    db eval {PRAGMA integrity_check(1)}
  } {{*** in database main ***
On tree page 2 cell 15: Rowid 0 out of order (previous was 15)}}
}
  
# The code path that was causing the buffer overrun that this test
# case was checking for was removed.
#
#do_test corrupt7-3.1 {
#  execsql {
#    DROP TABLE t1;







<
<
<
<
<
|
|
|
|
|
|
<
>
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<







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
  hexio_get_int [hexio_read test.db 20 1]
} 0      ;# Unused bytes per page is 0

integrity_check corrupt7-1.4

# Deliberately corrupt some of the cell offsets in the btree page
# on page 2 of the database.





do_test corrupt7-2.1 {
  db close
  hexio_write test.db 1062 FF
  sqlite3 db test.db
  db eval {PRAGMA integrity_check(1)}
} {{*** in database main ***

On tree page 2 cell 15: Offset 65457 out of range 945..1020}}
do_test corrupt7-2.2 {
  db close
  hexio_write test.db 1062 04
  sqlite3 db test.db
  db eval {PRAGMA integrity_check(1)}
} {{*** in database main ***















On tree page 2 cell 15: Offset 1201 out of range 945..1020}}

  
# The code path that was causing the buffer overrun that this test
# case was checking for was removed.
#
#do_test corrupt7-3.1 {
#  execsql {
#    DROP TABLE t1;

Changes to test/corruptE.test.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
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
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.  It specifcally
# focuses on rowid order corruption.
#
# $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $

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

# 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).
#
................................................................................
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 2041 [format %02x 0x2e]

  sqlite3 db test.db

  set res [ catchsql {PRAGMA integrity_check} ]
  set ans [lindex $res 1]

  list [regexp {out of order.*previous was} $ans] \
       [regexp {out of order.*max larger than parent max} $ans]
} {1 1}

do_test corruptE-2.2 {
  db close
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 2047 [format %02x 0x84]

  sqlite3 db test.db

  set res [ catchsql {PRAGMA integrity_check} ]
  set ans [lindex $res 1]

  list [regexp {out of order.*previous was} $ans] \
       [regexp {out of order.*min less than parent min} $ans]
} {1 1}

do_test corruptE-2.3 {
  db close
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 7420 [format %02x 0xa8]
  hexio_write test.db 10459 [format %02x 0x8d]

  sqlite3 db test.db

  set res [ catchsql {PRAGMA integrity_check} ]
  set ans [lindex $res 1]

  list [regexp {out of order.*max larger than parent min} $ans]
} {1}

do_test corruptE-2.4 {
  db close
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 10233 [format %02x 0xd0]

  sqlite3 db test.db

  set res [ catchsql {PRAGMA integrity_check} ]
  set ans [lindex $res 1]

  list [regexp {out of order.*min less than parent max} $ans]
} {1}


set tests [list {10233 0xd0} \
                {941 0x42} \
                {1028 0x53} \
                {2041 0xd0} \
                {2042 0x1f} \
                {2047 0xaa} \
                {2263 0x29} \
                {2274 0x75} \
                {3267 0xf2} \
                {4104 0x2c} \
                {5113 0x36} \
                {10233 0x84} \
                {10234 0x74} \
                {10239 0x41} \
                {10453 0x11} \
                {11273 0x28} \
                {11455 0x11} \
                {11461 0xe6} \
                {12281 0x99} \
                {12296 0x9e} \
                {12297 0xd7} \
                {13303 0x53} ]

set tc 1
foreach test $tests {
  do_test corruptE-3.$tc {
    db close
................................................................................
    forcecopy test.bu test.db

    # insert corrupt byte(s)
    hexio_write test.db [lindex $test 0] [format %02x [lindex $test 1]]

    sqlite3 db test.db

    set res [ catchsql {PRAGMA integrity_check} ]
    set ans [lindex $res 1]

    list [regexp {out of order} $ans]
  } {1}
  incr tc 1
}

finish_test







<







 







|
|
<
<
<
<










|
|
<
<
<
<











|
|
<
<
<










|
|
<
<
<




<


<
<


<




<

<

<
<







 







|
<
<
|
<




10
11
12
13
14
15
16

17
18
19
20
21
22
23
..
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
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file.  It specifcally
# focuses on rowid order corruption.
#


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

# 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).
#
................................................................................
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 2041 [format %02x 0x2e]

  sqlite3 db test.db

  catchsql {PRAGMA integrity_check}
} {/ out of order/}





do_test corruptE-2.2 {
  db close
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 2047 [format %02x 0x84]

  sqlite3 db test.db

  catchsql {PRAGMA integrity_check}
} {/ Extends off end of page/}





do_test corruptE-2.3 {
  db close
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 7420 [format %02x 0xa8]
  hexio_write test.db 10459 [format %02x 0x8d]

  sqlite3 db test.db

  catchsql {PRAGMA integrity_check}
} {/out of order/}




do_test corruptE-2.4 {
  db close
  forcecopy test.bu test.db

  # insert corrupt byte(s)
  hexio_write test.db 10233 [format %02x 0xd0]

  sqlite3 db test.db

  catchsql {PRAGMA integrity_check}
} {/out of order/}





set tests [list {10233 0xd0} \
                {941 0x42} \

                {2041 0xd0} \
                {2042 0x1f} \


                {2274 0x75} \
                {3267 0xf2} \

                {5113 0x36} \
                {10233 0x84} \
                {10234 0x74} \
                {10239 0x41} \

                {11273 0x28} \

                {11461 0xe6} \


                {12297 0xd7} \
                {13303 0x53} ]

set tc 1
foreach test $tests {
  do_test corruptE-3.$tc {
    db close
................................................................................
    forcecopy test.bu test.db

    # insert corrupt byte(s)
    hexio_write test.db [lindex $test 0] [format %02x [lindex $test 1]]

    sqlite3 db test.db

    catchsql {PRAGMA integrity_check}


  } {/out of order/}

  incr tc 1
}

finish_test