aboutsummaryrefslogtreecommitdiff
path: root/src/dbstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dbstat.c')
-rw-r--r--src/dbstat.c88
1 files changed, 70 insertions, 18 deletions
diff --git a/src/dbstat.c b/src/dbstat.c
index 78173c397..c70d80637 100644
--- a/src/dbstat.c
+++ b/src/dbstat.c
@@ -26,6 +26,15 @@
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
+** The pager and btree modules arrange objects in memory so that there are
+** always approximately 200 bytes of addressable memory following each page
+** buffer. This way small buffer overreads caused by corrupt database pages
+** do not cause undefined behaviour. This module pads each page buffer
+** by the following number of bytes for the same purpose.
+*/
+#define DBSTAT_PAGE_PADDING_BYTES 256
+
+/*
** Page paths:
**
** The value of the 'path' column describes the path taken from the
@@ -92,9 +101,8 @@ struct StatCell {
/* Size information for a single btree page */
struct StatPage {
u32 iPgno; /* Page number */
- DbPage *pPg; /* Page content */
+ u8 *aPg; /* Page buffer from sqlite3_malloc() */
int iCell; /* Current cell */
-
char *zPath; /* Path to this page */
/* Variables populated by statDecodePage(): */
@@ -155,6 +163,7 @@ static int statConnect(
StatTable *pTab = 0;
int rc = SQLITE_OK;
int iDb;
+ (void)pAux;
if( argc>=4 ){
Token nm;
@@ -208,6 +217,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iSchema = -1;
int iName = -1;
int iAgg = -1;
+ (void)tab;
/* Look for a valid schema=? constraint. If found, change the idxNum to
** 1 and request the value of that constraint be sent to xFilter. And
@@ -306,18 +316,25 @@ static void statClearCells(StatPage *p){
}
static void statClearPage(StatPage *p){
+ u8 *aPg = p->aPg;
statClearCells(p);
- sqlite3PagerUnref(p->pPg);
sqlite3_free(p->zPath);
memset(p, 0, sizeof(StatPage));
+ p->aPg = aPg;
}
static void statResetCsr(StatCursor *pCsr){
int i;
- sqlite3_reset(pCsr->pStmt);
+ /* In some circumstances, specifically if an OOM has occurred, the call
+ ** to sqlite3_reset() may cause the pager to be reset (emptied). It is
+ ** important that statClearPage() is called to free any page refs before
+ ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */
for(i=0; i<ArraySize(pCsr->aPage); i++){
statClearPage(&pCsr->aPage[i]);
+ sqlite3_free(pCsr->aPage[i].aPg);
+ pCsr->aPage[i].aPg = 0;
}
+ sqlite3_reset(pCsr->pStmt);
pCsr->iPage = 0;
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
@@ -382,7 +399,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
int isLeaf;
int szPage;
- u8 *aData = sqlite3PagerGetData(p->pPg);
+ u8 *aData = p->aPg;
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
p->flags = aHdr[0];
@@ -453,7 +470,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
if( nPayload>(u32)nLocal ){
int j;
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
- if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){
+ if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){
goto statPageIsCorrupt;
}
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
@@ -513,6 +530,38 @@ static void statSizeAndOffset(StatCursor *pCsr){
}
/*
+** Load a copy of the page data for page iPg into the buffer belonging
+** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK
+** if successful, or an SQLite error code otherwise.
+*/
+static int statGetPage(
+ Btree *pBt, /* Load page from this b-tree */
+ u32 iPg, /* Page number to load */
+ StatPage *pPg /* Load page into this object */
+){
+ int pgsz = sqlite3BtreeGetPageSize(pBt);
+ DbPage *pDbPage = 0;
+ int rc;
+
+ if( pPg->aPg==0 ){
+ pPg->aPg = (u8*)sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES);
+ if( pPg->aPg==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES);
+ }
+
+ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0);
+ if( rc==SQLITE_OK ){
+ const u8 *a = sqlite3PagerGetData(pDbPage);
+ memcpy(pPg->aPg, a, pgsz);
+ sqlite3PagerUnref(pDbPage);
+ }
+
+ return rc;
+}
+
+/*
** Move a DBSTAT cursor to the next entry. Normally, the next
** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0),
** the next entry is the next btree.
@@ -530,7 +579,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
pCsr->zPath = 0;
statNextRestart:
- if( pCsr->aPage[0].pPg==0 ){
+ if( pCsr->iPage<0 ){
/* Start measuring space on the next btree */
statResetCounts(pCsr);
rc = sqlite3_step(pCsr->pStmt);
@@ -542,7 +591,7 @@ statNextRestart:
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
}
- rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0);
+ rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]);
pCsr->aPage[0].iPgno = iRoot;
pCsr->aPage[0].iCell = 0;
if( !pCsr->isAgg ){
@@ -593,9 +642,8 @@ statNextRestart:
if( !p->iRightChildPg || p->iCell>p->nCell ){
statClearPage(p);
- if( pCsr->iPage>0 ){
- pCsr->iPage--;
- }else if( pCsr->isAgg ){
+ pCsr->iPage--;
+ if( pCsr->isAgg && pCsr->iPage<0 ){
/* label-statNext-done: When computing aggregate space usage over
** an entire btree, this is the exit point from this function */
return SQLITE_OK;
@@ -614,7 +662,7 @@ statNextRestart:
}else{
p[1].iPgno = p->aCell[p->iCell].iChildPg;
}
- rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0);
+ rc = statGetPage(pBt, p[1].iPgno, &p[1]);
pCsr->nPage++;
p[1].iCell = 0;
if( !pCsr->isAgg ){
@@ -695,6 +743,8 @@ static int statFilter(
int iArg = 0; /* Count of argv[] parameters used so far */
int rc = SQLITE_OK; /* Result of this operation */
const char *zName = 0; /* Only provide analysis of this table */
+ (void)argc;
+ (void)idxStr;
statResetCsr(pCsr);
sqlite3_finalize(pCsr->pStmt);
@@ -744,6 +794,7 @@ static int statFilter(
}
if( rc==SQLITE_OK ){
+ pCsr->iPage = -1;
rc = statNext(pCursor);
}
return rc;
@@ -777,16 +828,16 @@ static int statColumn(
}
break;
case 4: /* ncell */
- sqlite3_result_int(ctx, pCsr->nCell);
+ sqlite3_result_int64(ctx, pCsr->nCell);
break;
case 5: /* payload */
- sqlite3_result_int(ctx, pCsr->nPayload);
+ sqlite3_result_int64(ctx, pCsr->nPayload);
break;
case 6: /* unused */
- sqlite3_result_int(ctx, pCsr->nUnused);
+ sqlite3_result_int64(ctx, pCsr->nUnused);
break;
case 7: /* mx_payload */
- sqlite3_result_int(ctx, pCsr->nMxPayload);
+ sqlite3_result_int64(ctx, pCsr->nMxPayload);
break;
case 8: /* pgoffset */
if( !pCsr->isAgg ){
@@ -794,7 +845,7 @@ static int statColumn(
}
break;
case 9: /* pgsize */
- sqlite3_result_int(ctx, pCsr->szPage);
+ sqlite3_result_int64(ctx, pCsr->szPage);
break;
case 10: { /* schema */
sqlite3 *db = sqlite3_context_db_handle(ctx);
@@ -844,7 +895,8 @@ int sqlite3DbstatRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}