Skip to content

Commit 9398c74

Browse files
committed
Remove conn->db NULL guard from multi_step; fix stale NIF from prior run
The conn->db == NULL guard in exqlite_multi_step broke the 'exceeding timeout' integration test. sqlite3_close_v2 uses deferred close: the sqlite3* stays valid (zombie state) until all prepared statements are finalized, so multi_step can legitimately run after a connection close while a query is in-flight. The guard was not needed: conn->db cannot change during the loop because the connection lock is held, and statement->statement NULL is sufficient protection against use-after-release.
1 parent 5acd26b commit 9398c74

2 files changed

Lines changed: 3 additions & 9 deletions

File tree

c_src/sqlite3_nif.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -792,11 +792,6 @@ exqlite_multi_step(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
792792

793793
connection_acquire_lock(conn);
794794

795-
if (conn->db == NULL) {
796-
connection_release_lock(conn);
797-
return make_error_tuple(env, am_connection_closed);
798-
}
799-
800795
if (statement->statement == NULL) {
801796
connection_release_lock(conn);
802797
return make_error_tuple(env, am_invalid_statement);

test/exqlite/sqlite3_test.exs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,10 +1026,9 @@ defmodule Exqlite.Sqlite3Test do
10261026

10271027
# Targets statement use-after-release in exqlite_multi_step.
10281028
# After Sqlite3.release(conn, stmt), statement->statement is NULL.
1029-
# The pre-lock check catches the sequential case, but there
1030-
# is a TOCTOU window between that check and the sqlite3_step() call
1031-
# inside the connection lock. This test verifies the expected error
1032-
# return and documents the race condition.
1029+
# The pre-lock check catches the case where release happened before
1030+
# the call, and the inside-lock check catches the case where release
1031+
# races with the lock acquisition.
10331032
test "multi_step after release does not segfault" do
10341033
for _ <- 1..50 do
10351034
{:ok, conn} = Sqlite3.open(":memory:")

0 commit comments

Comments
 (0)