/ Check-in [a0d47f25]
Login

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

Overview
Comment:Harden the dbstat extension against corrupt database files.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a0d47f25ae7bdf98f5b853f23776b3bf86bea7c0dda386664c1e3b1c363c518f
User & Date: drh 2018-10-29 16:07:10
Context
2018-10-29
17:08
In the sessions module, avoid collecting rebase data if the user has not requested it. check-in: de72a773 user: dan tags: trunk
16:07
Harden the dbstat extension against corrupt database files. check-in: a0d47f25 user: drh tags: trunk
2018-10-27
21:06
Improvements to the dbfuzz2.c test module. check-in: d60eff49 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/dbstat.c.

   320    320     int isLeaf;
   321    321     int szPage;
   322    322   
   323    323     u8 *aData = sqlite3PagerGetData(p->pPg);
   324    324     u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
   325    325   
   326    326     p->flags = aHdr[0];
          327  +  if( p->flags==0x0A || p->flags==0x0D ){
          328  +    isLeaf = 1;
          329  +    nHdr = 8;
          330  +  }else if( p->flags==0x05 || p->flags==0x02 ){
          331  +    isLeaf = 0;
          332  +    nHdr = 12;
          333  +  }else{
          334  +    goto statPageIsCorrupt;
          335  +  }
          336  +  if( p->iPgno==1 ) nHdr += 100;
   327    337     p->nCell = get2byte(&aHdr[3]);
   328    338     p->nMxPayload = 0;
   329         -
   330         -  isLeaf = (p->flags==0x0A || p->flags==0x0D);
   331         -  nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100;
          339  +  szPage = sqlite3BtreeGetPageSize(pBt);
   332    340   
   333    341     nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell;
   334    342     nUnused += (int)aHdr[7];
   335    343     iOff = get2byte(&aHdr[1]);
   336    344     while( iOff ){
          345  +    int iNext;
          346  +    if( iOff>=szPage ) goto statPageIsCorrupt;
   337    347       nUnused += get2byte(&aData[iOff+2]);
   338         -    iOff = get2byte(&aData[iOff]);
          348  +    iNext = get2byte(&aData[iOff]);
          349  +    if( iNext<iOff+4 && iNext>0 ) goto statPageIsCorrupt;
          350  +    iOff = iNext;
   339    351     }
   340    352     p->nUnused = nUnused;
   341    353     p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
   342         -  szPage = sqlite3BtreeGetPageSize(pBt);
   343    354   
   344    355     if( p->nCell ){
   345    356       int i;                        /* Used to iterate through cells */
   346    357       int nUsable;                  /* Usable bytes per page */
   347    358   
   348    359       sqlite3BtreeEnter(pBt);
   349    360       nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt);
................................................................................
   352    363       if( p->aCell==0 ) return SQLITE_NOMEM_BKPT;
   353    364       memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
   354    365   
   355    366       for(i=0; i<p->nCell; i++){
   356    367         StatCell *pCell = &p->aCell[i];
   357    368   
   358    369         iOff = get2byte(&aData[nHdr+i*2]);
          370  +      if( iOff<nHdr || iOff>=szPage ) goto statPageIsCorrupt;
   359    371         if( !isLeaf ){
   360    372           pCell->iChildPg = sqlite3Get4byte(&aData[iOff]);
   361    373           iOff += 4;
   362    374         }
   363    375         if( p->flags==0x05 ){
   364    376           /* A table interior node. nPayload==0. */
   365    377         }else{
................................................................................
   368    380           iOff += getVarint32(&aData[iOff], nPayload);
   369    381           if( p->flags==0x0D ){
   370    382             u64 dummy;
   371    383             iOff += sqlite3GetVarint(&aData[iOff], &dummy);
   372    384           }
   373    385           if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
   374    386           getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
          387  +        if( nLocal<0 ) goto statPageIsCorrupt;
   375    388           pCell->nLocal = nLocal;
   376         -        assert( nLocal>=0 );
   377    389           assert( nPayload>=(u32)nLocal );
   378    390           assert( nLocal<=(nUsable-35) );
   379    391           if( nPayload>(u32)nLocal ){
   380    392             int j;
   381    393             int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
   382    394             pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
   383    395             pCell->nOvfl = nOvfl;
................................................................................
   398    410             }
   399    411           }
   400    412         }
   401    413       }
   402    414     }
   403    415   
   404    416     return SQLITE_OK;
          417  +
          418  +statPageIsCorrupt:
          419  +  p->flags = 0;
          420  +  p->nCell = 0;
          421  +  return SQLITE_OK;
   405    422   }
   406    423   
   407    424   /*
   408    425   ** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on
   409    426   ** the current value of pCsr->iPageno.
   410    427   */
   411    428   static void statSizeAndOffset(StatCursor *pCsr){