Skip to content

Return an error instead of crashing when a connection is used after Close - #9

Merged
laodouya merged 4 commits into
developfrom
fix/conn-use-after-close
Nov 6, 2026
Merged

laodouya merged 4 commits into
developfrom
fix/conn-use-after-close

Conversation

@laodouya

@laodouya laodouya commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Calling connection methods after Close can pass a nil handle to SQLite and crash the process. Return a closed-connection error, ignore callback registrations, or return the documented sentinel value instead.

Backports mattn#1462 (a5779f8), with one adjustment: the new guards inspect the handle without acquiring c.mu. Close holds that mutex while SQLite runs rollback and close callbacks, so acquiring it again from a callback would deadlock. Concurrent method calls and Close remain unsupported; the existing AutoCommit callback deadlock is outside this change.

Merged current develop (b38e872) and resolved the RegisterAuthorizer conflict by keeping both the closed-handle guard and #6's callback-handle ownership logic. The final diff changes only sqlite3.go and sqlite3_test.go. Squash merge is appropriate because an intermediate commit's test was corrected later.

Validation of head 2f10790 on Linux amd64, Go 1.27.3:

  • Closed-connection, close-callback and authorizer-release regressions passed 20 repetitions with race detection, both with default tags and with sqlite_trace sqlite_app_armor.
  • Full race suites passed with default, libsqlite3, the workflow's full and vacuum tags, and CovenantSQL's regular and miner tags.
  • Vet passed for all these configurations except the full tag set. Its nine testing.T.Fatal diagnostics in sqlite3_opt_unlock_notify_test.go reproduce identically on base develop b38e872.
  • All 12 current GitHub checks passed for this head (Go 1.26/1.27 on Linux/macOS; Windows build checks).

Earlier validation details remain available in the PR edit history. Open-connection behavior is unchanged; this PR does not claim safety when another goroutine closes the same connection concurrently.

mattn and others added 3 commits November 6, 2026 09:00
…lose

SQLite dereferences the connection handle without checking it, so every
*SQLiteConn method that passes c.db into the C API crashed the process
once Close had released the handle and set it to nil. Ordinary code
reaches this: a connection captured in a ConnectHook outlives its
*sql.DB.

The affected methods now check the handle first with dbConnOpen, which
reads it under c.mu and does not hold c.mu across the C call. Methods
that return an error report errConnClosed, the hook and authorizer
registrations, which have no error to report, do nothing, and
AutoCommit, GetFilename, GetLimit and SetLimit return a documented
value.

Affected: RegisterCollation, RegisterCommitHook, RegisterRollbackHook,
RegisterUpdateHook, RegisterAuthorizer, RegisterFunc,
RegisterAggregator, AutoCommit, GetFilename, GetLimit, SetLimit,
SetFileControlInt and SetFileControlInt64.

This is go-sqlite3 commit a5779f8 (mattn#1462) with its
test. Upstream applies it after 068ac77, which rewrites the body of
RegisterAuthorizer; here the check is added to the current body.
The guards of the previous commit call dbConnOpen, which takes c.mu.
Close holds c.mu while sqlite3_close_v2 runs, and SQLite runs callbacks
from there: closing a connection with an open transaction rolls it back
and calls the rollback hook, and with sqlite_trace the trace callback
gets a close event. A callback that calls GetFilename, GetLimit or
another guarded method then waits for c.mu forever and Close never
returns; before the guards the call worked.

The methods already read c.db without c.mu to pass it to SQLite, so
they now check it the same way: they still report a closed connection,
and take no lock. AutoCommit took c.mu before the guards and is
unchanged.

go-sqlite3 has the same deadlock since a5779f8.
With sqlite_app_armor, which the full tag set of the workflow includes,
SQLite refuses calls on a connection it is closing, so GetFilename
returned an empty name in the rollback hook and TestConnMethodsDuringClose
failed. The test is about Close returning; it no longer checks what the
method returns.
@laodouya
laodouya merged commit ef6a6f5 into develop Nov 6, 2026
12 checks passed
@laodouya
laodouya deleted the fix/conn-use-after-close branch November 6, 2026 02:29
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