/ Check-in [471cf0d8]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Make sure a corrupt index does not cause a buffer overread in sqlite3VdbeRecordCompare().
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 471cf0d8e7857110e525e029c2d535cb518dba6a
User & Date: drh 2011-11-11 00:27:15
Context
2011-11-11
23:51
Catch and report errors from sqlite3OsFullPathname(). check-in: 77119785 user: drh tags: trunk
14:12
Pull over all the latest changes from trunk. check-in: 1bbbf857 user: drh tags: experimental-pcache
00:27
Make sure a corrupt index does not cause a buffer overread in sqlite3VdbeRecordCompare(). check-in: 471cf0d8 user: drh tags: trunk
2011-11-10
21:45
Expand passing of a last error argument to the getLastErrorMsg function. Also, remove unused SQLITE_W32_THREADS define. check-in: 8f287979 user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

   855    855   ** Given a btree page and a cell index (0 means the first cell on
   856    856   ** the page, 1 means the second cell, and so forth) return a pointer
   857    857   ** to the cell content.
   858    858   **
   859    859   ** This routine works only for pages that do not contain overflow cells.
   860    860   */
   861    861   #define findCell(P,I) \
   862         -  ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
          862  +  ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)])))
   863    863   #define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
   864    864   
   865    865   
   866    866   /*
   867    867   ** This a more complex version of findCell() that works for
   868    868   ** pages that do contain overflow cells.
   869    869   */
................................................................................
  1405   1405       data = pPage->aData;
  1406   1406       if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
  1407   1407       assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
  1408   1408       pPage->maskPage = (u16)(pBt->pageSize - 1);
  1409   1409       pPage->nOverflow = 0;
  1410   1410       usableSize = pBt->usableSize;
  1411   1411       pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
         1412  +    pPage->aDataEnd = &data[usableSize];
         1413  +    pPage->aCellIdx = &data[cellOffset];
  1412   1414       top = get2byteNotZero(&data[hdr+5]);
  1413   1415       pPage->nCell = get2byte(&data[hdr+3]);
  1414   1416       if( pPage->nCell>MX_CELL(pBt) ){
  1415   1417         /* To many cells for a single page.  The page must be corrupt */
  1416   1418         return SQLITE_CORRUPT_BKPT;
  1417   1419       }
  1418   1420       testcase( pPage->nCell==MX_CELL(pBt) );
................................................................................
  1508   1510     memset(&data[hdr+1], 0, 4);
  1509   1511     data[hdr+7] = 0;
  1510   1512     put2byte(&data[hdr+5], pBt->usableSize);
  1511   1513     pPage->nFree = (u16)(pBt->usableSize - first);
  1512   1514     decodeFlags(pPage, flags);
  1513   1515     pPage->hdrOffset = hdr;
  1514   1516     pPage->cellOffset = first;
         1517  +  pPage->aDataEnd = &data[pBt->usableSize];
         1518  +  pPage->aCellIdx = &data[first];
  1515   1519     pPage->nOverflow = 0;
  1516   1520     assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
  1517   1521     pPage->maskPage = (u16)(pBt->pageSize - 1);
  1518   1522     pPage->nCell = 0;
  1519   1523     pPage->isInit = 1;
  1520   1524   }
  1521   1525   
................................................................................
  4546   4550           ** page is less than 16384 bytes and may be stored as a 2-byte
  4547   4551           ** varint. This information is used to attempt to avoid parsing 
  4548   4552           ** the entire cell by checking for the cases where the record is 
  4549   4553           ** stored entirely within the b-tree page by inspecting the first 
  4550   4554           ** 2 bytes of the cell.
  4551   4555           */
  4552   4556           int nCell = pCell[0];
  4553         -        if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){
         4557  +        if( !(nCell & 0x80)
         4558  +         && nCell<=pPage->maxLocal
         4559  +         && (pCell+nCell+1)<=pPage->aDataEnd
         4560  +        ){
  4554   4561             /* This branch runs if the record-size field of the cell is a
  4555   4562             ** single byte varint and the record fits entirely on the main
  4556   4563             ** b-tree page.  */
         4564  +          testcase( pCell+nCell+1==pPage->aDataEnd );
  4557   4565             c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
  4558   4566           }else if( !(pCell[1] & 0x80) 
  4559   4567             && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
         4568  +          && (pCell+nCell+2)<=pPage->aDataEnd
  4560   4569           ){
  4561   4570             /* The record-size field is a 2 byte varint and the record 
  4562   4571             ** fits entirely on the main b-tree page.  */
         4572  +          testcase( pCell+nCell+2==pPage->aDataEnd );
  4563   4573             c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
  4564   4574           }else{
  4565   4575             /* The record flows over onto one or more overflow pages. In
  4566   4576             ** this case the whole cell needs to be parsed, a buffer allocated
  4567   4577             ** and accessPayload() used to retrieve the record into the
  4568   4578             ** buffer before VdbeRecordCompare() can be called. */
  4569   4579             void *pCellKey;
................................................................................
  5450   5460     if( *pRC ) return;
  5451   5461   
  5452   5462     assert( idx>=0 && idx<pPage->nCell );
  5453   5463     assert( sz==cellSize(pPage, idx) );
  5454   5464     assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  5455   5465     assert( sqlite3_mutex_held(pPage->pBt->mutex) );
  5456   5466     data = pPage->aData;
  5457         -  ptr = &data[pPage->cellOffset + 2*idx];
         5467  +  ptr = &pPage->aCellIdx[2*idx];
  5458   5468     pc = get2byte(ptr);
  5459   5469     hdr = pPage->hdrOffset;
  5460   5470     testcase( pc==get2byte(&data[hdr+5]) );
  5461   5471     testcase( pc+sz==pPage->pBt->usableSize );
  5462   5472     if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
  5463   5473       *pRC = SQLITE_CORRUPT_BKPT;
  5464   5474       return;
  5465   5475     }
  5466   5476     rc = freeSpace(pPage, pc, sz);
  5467   5477     if( rc ){
  5468   5478       *pRC = rc;
  5469   5479       return;
  5470   5480     }
  5471         -  endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
         5481  +  endPtr = &pPage->aCellIdx[2*pPage->nCell - 2];
  5472   5482     assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 );  /* ptr is always 2-byte aligned */
  5473   5483     while( ptr<endPtr ){
  5474   5484       *(u16*)ptr = *(u16*)&ptr[2];
  5475   5485       ptr += 2;
  5476   5486     }
  5477   5487     pPage->nCell--;
  5478   5488     put2byte(&data[hdr+3], pPage->nCell);
................................................................................
  5606   5616               && (int)MX_CELL(pPage->pBt)<=10921);
  5607   5617     assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  5608   5618   
  5609   5619     /* Check that the page has just been zeroed by zeroPage() */
  5610   5620     assert( pPage->nCell==0 );
  5611   5621     assert( get2byteNotZero(&data[hdr+5])==nUsable );
  5612   5622   
  5613         -  pCellptr = &data[pPage->cellOffset + nCell*2];
         5623  +  pCellptr = &pPage->aCellIdx[nCell*2];
  5614   5624     cellbody = nUsable;
  5615   5625     for(i=nCell-1; i>=0; i--){
  5616   5626       u16 sz = aSize[i];
  5617   5627       pCellptr -= 2;
  5618   5628       cellbody -= sz;
  5619   5629       put2byte(pCellptr, cellbody);
  5620   5630       memcpy(&data[cellbody], apCell[i], sz);

Changes to src/btreeInt.h.

   285    285     u16 maskPage;        /* Mask for page offset */
   286    286     struct _OvflCell {   /* Cells that will not fit on aData[] */
   287    287       u8 *pCell;          /* Pointers to the body of the overflow cell */
   288    288       u16 idx;            /* Insert this cell before idx-th non-overflow cell */
   289    289     } aOvfl[5];
   290    290     BtShared *pBt;       /* Pointer to BtShared that this page is part of */
   291    291     u8 *aData;           /* Pointer to disk image of the page data */
          292  +  u8 *aDataEnd;        /* One byte past the end of usable data */
          293  +  u8 *aCellIdx;        /* The cell index area */
   292    294     DbPage *pDbPage;     /* Pager page handle */
   293    295     Pgno pgno;           /* Page number for this page */
   294    296   };
   295    297   
   296    298   /*
   297    299   ** The in-memory image of a disk page has the auxiliary information appended
   298    300   ** to the end.  EXTRA_SIZE is the number of bytes of space needed to hold