fix: null sqlite3 handle after close to prevent double-free (#2251)#2261
Open
wasim-builds wants to merge 1 commit into
Open
fix: null sqlite3 handle after close to prevent double-free (#2251)#2261wasim-builds wants to merge 1 commit into
wasim-builds wants to merge 1 commit into
Conversation
Set raw pointer to null after sqlite3_close_v2 in disconnect() to prevent use-after-free when the handle is closed twice. This fixes issue tursodatabase#2251 where LibsqlConnection::Drop calls disconnect() and then the inner Connection::Drop also calls disconnect(), causing a double-close of the sqlite3 handle. The fix ensures disconnect() is idempotent by nulling the raw pointer after the first close, so subsequent calls are safe no-ops.
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.
Problem
Closes #2251
The local connection's
sqlite3handle issqlite3_close_v2'd twice on teardown.LibsqlConnection'sDropcallsdisconnect(), then the innerConnection'sDropcalls it again. Both go throughConnection::disconnect(connection.rs:110).disconnect()isn't idempotent — theArc::get_mut(drop_ref)guard only checks unique ownership (true both times) and doesn't record that the handle was already closed.On Windows this is a hard
STATUS_ACCESS_VIOLATION(0xC0000005); on Linux it doesn't fault (glibc keeps the freed page mapped) but the double free is real, as valgrind shows — 800 errors from 1 context at 800 iterations, one per connection.Root Cause
Both calls succeed because
Arc::get_mut(drop_ref)returnsSomeboth times (the first close doesn't mark the handle as closed).Fix
After calling
sqlite3_close_v2, setself.rawtostd::ptr::null_mut(). This makesdisconnect()idempotent — the second call sees a null pointer, and even ifArc::get_mutpasses, callingsqlite3_close_v2(null)is documented as safe (no-op) in SQLite.Changes
libsql/src/local/connection.rs: Nullrawaftersqlite3_close_v2indisconnect()Reproduction (from issue)
With valgrind: 800 errors at 800 iterations. On Windows:
STATUS_ACCESS_VIOLATIONwithin first few hundred iterations.Testing
cargo checkpassesdisconnect()idempotent as suggested in the issueCo-Authored-By: Claude noreply@anthropic.com