You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Shared #range prepared statement makes concurrent scans return each other's rows #2
SqliteStore prepares #range once in the constructor and every values() call iterates that same statement:
// sqliteStore.js:26this.#range =this.#db.prepare('SELECT value FROM key_value WHERE key > ? AND key < ? ORDER BY key')// sqliteStore.js:77async*values(options){for(constrowofthis.#range.iterate(options.gt,options.lt)){
A second iterate() on a statement that is still being walked resets it, so two overlapping scans cross-contaminate. Reproduced — one iterator yields rows belonging to the other and then terminates early:
It was first found with two concurrent outgoingStream() calls, which already matters: two clients reconnecting at the same time means client A receives B's queued packets and silently loses its own.
But it is reachable on a much more ordinary path. Assigning instance.broker = ... starts setup(), which iterates every subscription in the store. Any query issued before ready races that startup scan. Measured on a store with 5 subscriptions written for one client:
query without awaiting ready -> 8 rows (contaminated)
query after awaiting ready -> 5 rows ["plain/t","ÿ/x","Ā/x","主题","😀/x"]
So a broker that starts serving while its own subscription scan is in flight can return wrong data, with no concurrent client activity required at all.
Fix
Prepare the statement per iteration rather than sharing one instance, or otherwise give each values() call its own cursor. It needs a test that runs two scans interleaved — a sequential pair will pass either way.
Found while reviewing #1 and deliberately left out of that PR: it is pre-existing, sits in a different method, and deserves its own fix and test rather than riding along with a security patch.
Activity