pCursor->iChild = 0;
/* A child for each cell, plus one in the header. */
- /* TODO(shess): Sanity-check the count? Page header plus per-cell
- * cost of 16-bit offset, 32-bit page number, and one varint
- * (minimum 1 byte).
- */
pCursor->nChildren = decodeUnsigned16(PageHeader(pPage) +
kiPageCellCountOffset) + 1;
+
+ /* Each child requires a 16-bit offset from an array after the header,
+ * and each child contains a 32-bit page number and at least a varint
+ * (min size of one byte). The final child page is in the header. So
+ * the maximum value for nChildren is:
+ * (nPageSize - kiPageInteriorHeaderBytes) /
+ * (sizeof(uint16) + sizeof(uint32) + 1) + 1
+ */
+ /* TODO(shess): This count is very unlikely to be corrupted in
+ * isolation, so seeing this could signal to skip the page. OTOH, I
+ * can't offhand think of how to get here unless this or the page-type
+ * byte is corrupted. Could be an overflow page, but it would require
+ * a very large database.
+ */
+ const unsigned knMinCellLength = 2 + 4 + 1;
+ unsigned nMaxChildren =
+ (pCursor->nPageSize - kiPageInteriorHeaderBytes) / knMinCellLength + 1;
+ if (pCursor->nChildren > nMaxChildren) {
+ pCursor->nChildren = nMaxChildren;
+ }
}
static int interiorCursorCreate(RecoverInteriorCursor *pParent,
/* Setup the cursor for reading the information from cell iCell. */
static int leafCursorCellDecode(RecoverLeafCursor *pCursor){
const unsigned char *pPageHeader; /* Header of current page. */
+ const unsigned char *pPageEnd; /* Byte after end of current page. */
const unsigned char *pCellOffsets; /* Pointer to page's cell offsets. */
unsigned iCellOffset; /* Offset of current cell (iCell). */
const unsigned char *pCell; /* Pointer to data at iCellOffset. */
/* Find the offset to the row. */
pPageHeader = PageHeader(pCursor->pPage);
pCellOffsets = pPageHeader + knPageLeafHeaderBytes;
+ pPageEnd = PageData(pCursor->pPage, pCursor->nPageSize);
+ if( pCellOffsets + pCursor->iCell*2 + 2 > pPageEnd ){
+ return ValidateError();
+ }
iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iCell*2);
if( iCellOffset>=pCursor->nPageSize ){
return ValidateError();
/* Check that no other cell starts within this cell. */
iEndOffset = pCursor->iRecordOffset + pCursor->nLocalRecordBytes;
- for( i=0; i<pCursor->nCells; ++i ){
+ for( i=0; i<pCursor->nCells && pCellOffsets + i*2 + 2 <= pPageEnd; ++i ){
const unsigned iOtherOffset = decodeUnsigned16(pCellOffsets + i*2);
if( iOtherOffset>iCellOffset && iOtherOffset<iEndOffset ){
return ValidateError();
pCursor->pLeafCursor = pLeafCursor;
pCursor->iEncoding = iEncoding;
+ /* If no leaf pages were found, empty result set. */
+ /* TODO(shess): leafCursorNextValidCell() would return SQLITE_ROW or
+ * SQLITE_DONE to indicate whether there is further data to consider.
+ */
+ pCursor->bEOF = (pLeafCursor->pPage==NULL);
+
*ppCursor = (sqlite3_vtab_cursor*)pCursor;
return SQLITE_OK;
}
FNENTRY();
- /* Load the first cell, and iterate forward if it's not valid. */
- /* TODO(shess): What happens if no cells at all are valid? */
+ /* There were no valid leaf pages in the table. */
+ if( pCursor->bEOF ){
+ return SQLITE_OK;
+ }
+
+ /* Load the first cell, and iterate forward if it's not valid. If no cells at
+ * all are valid, recoverNext() sets bEOF and returns appropriately.
+ */
rc = leafCursorCellDecode(pCursor->pLeafCursor);
if( rc!=SQLITE_OK || recoverValidateLeafCell(pRecover, pCursor)!=SQLITE_OK ){
return recoverNext(pVtabCursor);