Overflowing DbPage refcount
(1) By Roger Binns (rogerbinns) on 2023-03-19 19:46:57 [source]
I discovered this in 3.41 (and still in current fossil) by getting an assertion failure when stressing my statement cache. If 32,768 statements (SELECT x FROM table)
have been stepped once (ie all sitting at SQLITE_ROW
) then this assertion fails in a call to sqlite3_step
:
sqlite3PagerGetData: Assertion `pPg->nRef>0 || pPg->pPager->memDb'
The reason is because pPg->nRef
which is defined as i16
has wrapped around to -32,768.
To repeat in English:
- Create a table with one row of data
- Prepare
SELECT * FROM table
- Run
sqlite3_step
once - Go to 2
To repeat in code:
#include <stdio.h>
#include <assert.h>
/* avoid syscall on linux */
#define HAVE_MREMAP 0
/* turn sqlite assert on */
#define SQLITE_DEBUG 1
/* include the .c file so we can be certain which is used */
#include "sqlite3.c"
int main(void)
{
int i, rc;
sqlite3 *pdb;
sqlite3_stmt *pstmt;
rc = sqlite3_open("", &pdb);
assert(rc == SQLITE_OK);
rc = sqlite3_exec(pdb, "create table foo(x); insert into foo values(1)", NULL, NULL, NULL);
assert(rc == SQLITE_OK);
for (i = 0; i < 33000; i++)
{
fprintf(stderr, "%d\n", i);
rc = sqlite3_prepare_v3(pdb, "select x from foo", -1, 0, &pstmt, NULL);
assert(rc == SQLITE_OK);
rc = sqlite3_step(pstmt);
assert(rc == SQLITE_ROW);
}
return 0;
}
Tail end of running it:
32760
32761
32762
32763
32764
32765
32766
32767
t: sqlite3/sqlite3.c:62698: sqlite3PagerGetData: Assertion `pPg->nRef>0 || pPg->pPager->memDb' failed.
Program received signal SIGABRT, Aborted.