Evict finalized statement pointers to stop SQL aliasing across pointer reuse#4
Open
karans4 wants to merge 1 commit into
Open
Evict finalized statement pointers to stop SQL aliasing across pointer reuse#4karans4 wants to merge 1 commit into
karans4 wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Statement pointers alias across finalize.
sqlite3_finalizereturns the stmt to the allocator, and a later prepare routinely gets the same address back, but neither side ever forgets the dead pointer:sqlByStmtinprobes/sqlite.jskeeps mapping the reused pointer to the dead statement's SQL, so the new statement's executions show up wearing the old statement's text, with the new binds and latencies attached to it.knownLRU in the BPF object still contains the pointer, which suppresses zSql recovery for the new statement on x86-64, so it can never self-correct.This is easy to hit: the first prepare on a fresh connection triggers SQLite's lazy schema load, which nests
sqlite3_exec->sqlite3_prepare_v2inside the outer prepare and clobbers the single per-thread scratch slot (the documented, accepted failure mode insqlite.bpf.c). The header comment says the cost is one statement shown as «unknown» that self-corrects. Without eviction the actual cost is wrong SQL on unrelated statements: in a live run I watched a parameterized SELECT render asCOMMITwith its binds attached (raw event dump confirms the missed outer PREPARE followed by BIND/STEP on a reused pointer).Fix: hook
sqlite3_finalize(entry-only, no pairing, so none of the scratch-slot/uretprobe concerns apply), delete the pointer fromknownin the kernel, and emitEV_FINALIZEso userspace flushes any lingering in-flight execution and drops itssqlByStmtentry. After the patch the same repro shows the missed statement as «unknown» with its own binds, which is exactly the failure mode the header comment promises, and it now truly self-corrects on both architectures.Tested on Debian 13 (arm64, kernel 6.12, yeet master): make builds clean, FINALIZE events flow in the raw dump mode, and the TUI no longer attributes binds to dead statements' SQL. Touches the same region of
probes/sqlite.jsas #2; happy to rebase whichever lands second.