Skip to content

fix: set iVersion=2 to prevent SEGFAULT from null xFetch#80

Open
russellromney wants to merge 2 commits intoorbitinghail:mainfrom
russellromney:fix/iversion-segfault
Open

fix: set iVersion=2 to prevent SEGFAULT from null xFetch#80
russellromney wants to merge 2 commits intoorbitinghail:mainfrom
russellromney:fix/iversion-segfault

Conversation

@russellromney
Copy link
Copy Markdown

Problem

PR #76 added SHM support (xShmMap, xShmLock, xShmBarrier, xShmUnmap) and bumped iVersion from 1 to 3. But iVersion: 3 declares that xFetch and xUnfetch are implemented and callable. They were left as None.

SQLite trusts iVersion and does not null-check function pointers. Under concurrent WAL load, SQLite's pager calls xFetch during checkpoint page reads, jumping to address 0x0. ~60% SEGFAULT rate with 1 writer + 4 readers.

The SQLite iVersion contract (from sqlite3.h):

  • 1 = basic file I/O (xClose through xDeviceCharacteristics)
  • 2 = + SHM/WAL support (xShmMap through xShmUnmap)
  • 3 = + memory-mapped I/O (xFetch, xUnfetch)

Fix

Set iVersion: 2 (SHM support only, no mmap). SQLite falls back to xRead for all page reads.

One-line change + regression test.

Regression test

Includes tests/concurrent_wal_test.rs: a minimal file-backed VFS (no external dependencies beyond libc) with 1 writer + 4 readers in WAL mode for 3 seconds. On unpatched main, this SEGFAULTs ~60% of the time. With the fix, 30/30 pass.

ASAN output on unpatched main:

==59391==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
(pc 0x000000000000 ...)
    #0 0x000000000000  (<unknown module>)
    #1 ... in sqlite3WalCheckpoint

pc=0x0 confirms a null function pointer call, not a null data pointer.

Future

A separate PR can add fetch()/unfetch() methods to the Vfs trait to legitimately support iVersion: 3. Default implementations would return Ok(None) (decline mmap, safe fallback). VFS implementations that want actual mmap can override.

iVersion was set to 3 in PR orbitinghail#76 when SHM support was added, but
xFetch and xUnfetch were left as None. SQLite interprets iVersion>=3
as "xFetch is callable" and does not null-check before calling.

Under concurrent WAL load (1 writer + 4 readers), SQLite's pager
calls xFetch during checkpoint page reads, jumping to address 0x0.
~60% SEGFAULT rate.

Fix: iVersion=2 (declares SHM support, not mmap support). SQLite
falls back to xRead for all page reads.

Includes regression test with a minimal file-backed VFS that
reproduces the crash without any external VFS implementation.
@carlsverre
Copy link
Copy Markdown
Contributor

carlsverre commented Apr 13, 2026

Thanks for the submission! Please remove the regression test. This is a straightforward API compatibility mistake, and the regression test is more complex than I think it strictly needs to be (afaict no need to have it implement shm)

@russellromney
Copy link
Copy Markdown
Author

Got it, def more complex than it should be. Do you want me to re-pull without it or keep it in the commit history?

@carlsverre
Copy link
Copy Markdown
Contributor

Thanks for updating. I just saw your other PR and I'll probably go with that one as it seems straightforward. But nice to have this one as backup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants