Skip to content

Commit 71388a7

Browse files
abonanderlink2xtmadadam
authored
sqlite: fix a couple segfaults (#1351)
* sqlite: use Arc instead of Copy-able StatementHandle This guarantees that StatementHandle is never used after calling `sqlite3_finalize`. Now `sqlite3_finalize` is only called when StatementHandle is dropped. (cherry picked from commit 5eebc05) * sqlite: use Weak poiter to StatementHandle in the worker Otherwise some tests fail to close connection. (cherry picked from commit 5461eee) * Fix segfault due to race condition in sqlite (#1300) (cherry picked from commit bb62cf7) * fix(sqlite): run `sqlite3_reset()` in `StatementWorker` this avoids possible race conditions without using a mutex * fix(sqlite): have `StatementWorker` keep a strong ref to `ConnectionHandle` this should prevent the database handle from being finalized before all statement handles have been finalized * fix(sqlite/test): make `concurrent_resets_dont_segfault` runtime-agnostic Co-authored-by: link2xt <[email protected]> Co-authored-by: Adam Cigánek <[email protected]>
1 parent 55c603e commit 71388a7

File tree

11 files changed

+224
-80
lines changed

11 files changed

+224
-80
lines changed

sqlx-core/src/sqlite/connection/describe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub(super) fn describe<'c: 'e, 'q: 'e, 'e>(
6464
// fallback to [column_decltype]
6565
if !stepped && stmt.read_only() {
6666
stepped = true;
67-
let _ = conn.worker.step(*stmt).await;
67+
let _ = conn.worker.step(stmt).await;
6868
}
6969

7070
let mut ty = stmt.column_type_info(col);

sqlx-core/src/sqlite/connection/establish.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub(crate) async fn establish(options: &SqliteConnectOptions) -> Result<SqliteCo
8787
// https://www.sqlite.org/c3ref/extended_result_codes.html
8888
unsafe {
8989
// NOTE: ignore the failure here
90-
sqlite3_extended_result_codes(handle.0.as_ptr(), 1);
90+
sqlite3_extended_result_codes(handle.as_ptr(), 1);
9191
}
9292

9393
// Configure a busy timeout
@@ -99,7 +99,7 @@ pub(crate) async fn establish(options: &SqliteConnectOptions) -> Result<SqliteCo
9999
let ms =
100100
i32::try_from(busy_timeout.as_millis()).expect("Given busy timeout value is too big.");
101101

102-
status = unsafe { sqlite3_busy_timeout(handle.0.as_ptr(), ms) };
102+
status = unsafe { sqlite3_busy_timeout(handle.as_ptr(), ms) };
103103

104104
if status != SQLITE_OK {
105105
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
@@ -109,8 +109,8 @@ pub(crate) async fn establish(options: &SqliteConnectOptions) -> Result<SqliteCo
109109
})?;
110110

111111
Ok(SqliteConnection {
112+
worker: StatementWorker::new(handle.to_ref()),
112113
handle,
113-
worker: StatementWorker::new(),
114114
statements: StatementCache::new(options.statement_cache_capacity),
115115
statement: None,
116116
transaction_depth: 0,

sqlx-core/src/sqlite/connection/executor.rs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::error::Error;
44
use crate::executor::{Execute, Executor};
55
use crate::logger::QueryLogger;
66
use crate::sqlite::connection::describe::describe;
7-
use crate::sqlite::statement::{StatementHandle, VirtualStatement};
7+
use crate::sqlite::statement::{StatementHandle, StatementWorker, VirtualStatement};
88
use crate::sqlite::{
99
Sqlite, SqliteArguments, SqliteConnection, SqliteQueryResult, SqliteRow, SqliteStatement,
1010
SqliteTypeInfo,
@@ -16,7 +16,8 @@ use libsqlite3_sys::sqlite3_last_insert_rowid;
1616
use std::borrow::Cow;
1717
use std::sync::Arc;
1818

19-
fn prepare<'a>(
19+
async fn prepare<'a>(
20+
worker: &mut StatementWorker,
2021
statements: &'a mut StatementCache<VirtualStatement>,
2122
statement: &'a mut Option<VirtualStatement>,
2223
query: &str,
@@ -39,7 +40,7 @@ fn prepare<'a>(
3940
if exists {
4041
// as this statement has been executed before, we reset before continuing
4142
// this also causes any rows that are from the statement to be inflated
42-
statement.reset();
43+
statement.reset(worker).await?;
4344
}
4445

4546
Ok(statement)
@@ -61,19 +62,25 @@ fn bind(
6162

6263
/// A structure holding sqlite statement handle and resetting the
6364
/// statement when it is dropped.
64-
struct StatementResetter {
65-
handle: StatementHandle,
65+
struct StatementResetter<'a> {
66+
handle: Arc<StatementHandle>,
67+
worker: &'a mut StatementWorker,
6668
}
6769

68-
impl StatementResetter {
69-
fn new(handle: StatementHandle) -> Self {
70-
Self { handle }
70+
impl<'a> StatementResetter<'a> {
71+
fn new(worker: &'a mut StatementWorker, handle: &Arc<StatementHandle>) -> Self {
72+
Self {
73+
worker,
74+
handle: Arc::clone(handle),
75+
}
7176
}
7277
}
7378

74-
impl Drop for StatementResetter {
79+
impl Drop for StatementResetter<'_> {
7580
fn drop(&mut self) {
76-
self.handle.reset();
81+
// this method is designed to eagerly send the reset command
82+
// so we don't need to await or spawn it
83+
let _ = self.worker.reset(&self.handle);
7784
}
7885
}
7986

@@ -103,7 +110,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
103110
} = self;
104111

105112
// prepare statement object (or checkout from cache)
106-
let stmt = prepare(statements, statement, sql, persistent)?;
113+
let stmt = prepare(worker, statements, statement, sql, persistent).await?;
107114

108115
// keep track of how many arguments we have bound
109116
let mut num_arguments = 0;
@@ -113,7 +120,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
113120
// is dropped. `StatementResetter` will reliably reset the
114121
// statement even if the stream returned from `fetch_many`
115122
// is dropped early.
116-
let _resetter = StatementResetter::new(*stmt);
123+
let resetter = StatementResetter::new(worker, stmt);
117124

118125
// bind values to the statement
119126
num_arguments += bind(stmt, &arguments, num_arguments)?;
@@ -125,7 +132,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
125132

126133
// invoke [sqlite3_step] on the dedicated worker thread
127134
// this will move us forward one row or finish the statement
128-
let s = worker.step(*stmt).await?;
135+
let s = resetter.worker.step(stmt).await?;
129136

130137
match s {
131138
Either::Left(changes) => {
@@ -145,7 +152,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
145152

146153
Either::Right(()) => {
147154
let (row, weak_values_ref) = SqliteRow::current(
148-
*stmt,
155+
&stmt,
149156
columns,
150157
column_names
151158
);
@@ -188,7 +195,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
188195
} = self;
189196

190197
// prepare statement object (or checkout from cache)
191-
let virtual_stmt = prepare(statements, statement, sql, persistent)?;
198+
let virtual_stmt = prepare(worker, statements, statement, sql, persistent).await?;
192199

193200
// keep track of how many arguments we have bound
194201
let mut num_arguments = 0;
@@ -205,18 +212,18 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
205212

206213
// invoke [sqlite3_step] on the dedicated worker thread
207214
// this will move us forward one row or finish the statement
208-
match worker.step(*stmt).await? {
215+
match worker.step(stmt).await? {
209216
Either::Left(_) => (),
210217

211218
Either::Right(()) => {
212219
let (row, weak_values_ref) =
213-
SqliteRow::current(*stmt, columns, column_names);
220+
SqliteRow::current(stmt, columns, column_names);
214221

215222
*last_row_values = Some(weak_values_ref);
216223

217224
logger.increment_rows();
218225

219-
virtual_stmt.reset();
226+
virtual_stmt.reset(worker).await?;
220227
return Ok(Some(row));
221228
}
222229
}
@@ -238,11 +245,12 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
238245
handle: ref mut conn,
239246
ref mut statements,
240247
ref mut statement,
248+
ref mut worker,
241249
..
242250
} = self;
243251

244252
// prepare statement object (or checkout from cache)
245-
let statement = prepare(statements, statement, sql, true)?;
253+
let statement = prepare(worker, statements, statement, sql, true).await?;
246254

247255
let mut parameters = 0;
248256
let mut columns = None;

sqlx-core/src/sqlite/connection/handle.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,23 @@ use std::ptr::NonNull;
33
use libsqlite3_sys::{sqlite3, sqlite3_close, SQLITE_OK};
44

55
use crate::sqlite::SqliteError;
6+
use std::sync::Arc;
67

78
/// Managed handle to the raw SQLite3 database handle.
8-
/// The database handle will be closed when this is dropped.
9+
/// The database handle will be closed when this is dropped and no `ConnectionHandleRef`s exist.
910
#[derive(Debug)]
10-
pub(crate) struct ConnectionHandle(pub(super) NonNull<sqlite3>);
11+
pub(crate) struct ConnectionHandle(Arc<HandleInner>);
12+
13+
/// A wrapper around `ConnectionHandle` which only exists for a `StatementWorker` to own
14+
/// which prevents the `sqlite3` handle from being finalized while it is running `sqlite3_step()`
15+
/// or `sqlite3_reset()`.
16+
///
17+
/// Note that this does *not* actually give access to the database handle!
18+
pub(crate) struct ConnectionHandleRef(Arc<HandleInner>);
19+
20+
// Wrapper for `*mut sqlite3` which finalizes the handle on-drop.
21+
#[derive(Debug)]
22+
struct HandleInner(NonNull<sqlite3>);
1123

1224
// A SQLite3 handle is safe to send between threads, provided not more than
1325
// one is accessing it at the same time. This is upheld as long as [SQLITE_CONFIG_MULTITHREAD] is
@@ -20,19 +32,32 @@ pub(crate) struct ConnectionHandle(pub(super) NonNull<sqlite3>);
2032

2133
unsafe impl Send for ConnectionHandle {}
2234

35+
// SAFETY: `Arc<T>` normally only implements `Send` where `T: Sync` because it allows
36+
// concurrent access.
37+
//
38+
// However, in this case we're only using `Arc` to prevent the database handle from being
39+
// finalized while the worker still holds a statement handle; `ConnectionHandleRef` thus
40+
// should *not* actually provide access to the database handle.
41+
unsafe impl Send for ConnectionHandleRef {}
42+
2343
impl ConnectionHandle {
2444
#[inline]
2545
pub(super) unsafe fn new(ptr: *mut sqlite3) -> Self {
26-
Self(NonNull::new_unchecked(ptr))
46+
Self(Arc::new(HandleInner(NonNull::new_unchecked(ptr))))
2747
}
2848

2949
#[inline]
3050
pub(crate) fn as_ptr(&self) -> *mut sqlite3 {
31-
self.0.as_ptr()
51+
self.0 .0.as_ptr()
52+
}
53+
54+
#[inline]
55+
pub(crate) fn to_ref(&self) -> ConnectionHandleRef {
56+
ConnectionHandleRef(Arc::clone(&self.0))
3257
}
3358
}
3459

35-
impl Drop for ConnectionHandle {
60+
impl Drop for HandleInner {
3661
fn drop(&mut self) {
3762
unsafe {
3863
// https://sqlite.org/c3ref/close.html

sqlx-core/src/sqlite/connection/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod executor;
1717
mod explain;
1818
mod handle;
1919

20-
pub(crate) use handle::ConnectionHandle;
20+
pub(crate) use handle::{ConnectionHandle, ConnectionHandleRef};
2121

2222
/// A connection to a [Sqlite] database.
2323
pub struct SqliteConnection {

sqlx-core/src/sqlite/row.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub struct SqliteRow {
2323
// IF the user drops the Row before iterating the stream (so
2424
// nearly all of our internal stream iterators), the executor moves on; otherwise,
2525
// it actually inflates this row with a list of owned sqlite3 values.
26-
pub(crate) statement: StatementHandle,
26+
pub(crate) statement: Arc<StatementHandle>,
2727

2828
pub(crate) values: Arc<AtomicPtr<SqliteValue>>,
2929
pub(crate) num_values: usize,
@@ -48,7 +48,7 @@ impl SqliteRow {
4848
// returns a weak reference to an atomic list where the executor should inflate if its going
4949
// to increment the statement with [step]
5050
pub(crate) fn current(
51-
statement: StatementHandle,
51+
statement: &Arc<StatementHandle>,
5252
columns: &Arc<Vec<SqliteColumn>>,
5353
column_names: &Arc<HashMap<UStr, usize>>,
5454
) -> (Self, Weak<AtomicPtr<SqliteValue>>) {
@@ -57,7 +57,7 @@ impl SqliteRow {
5757
let size = statement.column_count();
5858

5959
let row = Self {
60-
statement,
60+
statement: Arc::clone(statement),
6161
values,
6262
num_values: size,
6363
columns: Arc::clone(columns),

sqlx-core/src/sqlite/statement/handle.rs

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::ffi::c_void;
22
use std::ffi::CStr;
3+
34
use std::os::raw::{c_char, c_int};
45
use std::ptr;
56
use std::ptr::NonNull;
@@ -9,21 +10,22 @@ use std::str::{from_utf8, from_utf8_unchecked};
910
use libsqlite3_sys::{
1011
sqlite3, sqlite3_bind_blob64, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
1112
sqlite3_bind_null, sqlite3_bind_parameter_count, sqlite3_bind_parameter_name,
12-
sqlite3_bind_text64, sqlite3_changes, sqlite3_column_blob, sqlite3_column_bytes,
13-
sqlite3_column_count, sqlite3_column_database_name, sqlite3_column_decltype,
14-
sqlite3_column_double, sqlite3_column_int, sqlite3_column_int64, sqlite3_column_name,
15-
sqlite3_column_origin_name, sqlite3_column_table_name, sqlite3_column_type,
16-
sqlite3_column_value, sqlite3_db_handle, sqlite3_reset, sqlite3_sql, sqlite3_stmt,
17-
sqlite3_stmt_readonly, sqlite3_table_column_metadata, sqlite3_value, SQLITE_OK,
18-
SQLITE_TRANSIENT, SQLITE_UTF8,
13+
sqlite3_bind_text64, sqlite3_changes, sqlite3_clear_bindings, sqlite3_column_blob,
14+
sqlite3_column_bytes, sqlite3_column_count, sqlite3_column_database_name,
15+
sqlite3_column_decltype, sqlite3_column_double, sqlite3_column_int, sqlite3_column_int64,
16+
sqlite3_column_name, sqlite3_column_origin_name, sqlite3_column_table_name,
17+
sqlite3_column_type, sqlite3_column_value, sqlite3_db_handle, sqlite3_finalize, sqlite3_reset,
18+
sqlite3_sql, sqlite3_step, sqlite3_stmt, sqlite3_stmt_readonly, sqlite3_table_column_metadata,
19+
sqlite3_value, SQLITE_DONE, SQLITE_MISUSE, SQLITE_OK, SQLITE_ROW, SQLITE_TRANSIENT,
20+
SQLITE_UTF8,
1921
};
2022

2123
use crate::error::{BoxDynError, Error};
2224
use crate::sqlite::type_info::DataType;
2325
use crate::sqlite::{SqliteError, SqliteTypeInfo};
2426

25-
#[derive(Debug, Copy, Clone)]
26-
pub(crate) struct StatementHandle(pub(super) NonNull<sqlite3_stmt>);
27+
#[derive(Debug)]
28+
pub(crate) struct StatementHandle(NonNull<sqlite3_stmt>);
2729

2830
// access to SQLite3 statement handles are safe to send and share between threads
2931
// as long as the `sqlite3_step` call is serialized.
@@ -32,6 +34,14 @@ unsafe impl Send for StatementHandle {}
3234
unsafe impl Sync for StatementHandle {}
3335

3436
impl StatementHandle {
37+
pub(super) fn new(ptr: NonNull<sqlite3_stmt>) -> Self {
38+
Self(ptr)
39+
}
40+
41+
pub(crate) fn as_ptr(&self) -> *mut sqlite3_stmt {
42+
self.0.as_ptr()
43+
}
44+
3545
#[inline]
3646
pub(super) unsafe fn db_handle(&self) -> *mut sqlite3 {
3747
// O(c) access to the connection handle for this statement handle
@@ -280,7 +290,25 @@ impl StatementHandle {
280290
Ok(from_utf8(self.column_blob(index))?)
281291
}
282292

283-
pub(crate) fn reset(&self) {
284-
unsafe { sqlite3_reset(self.0.as_ptr()) };
293+
pub(crate) fn clear_bindings(&self) {
294+
unsafe { sqlite3_clear_bindings(self.0.as_ptr()) };
295+
}
296+
}
297+
impl Drop for StatementHandle {
298+
fn drop(&mut self) {
299+
// SAFETY: we have exclusive access to the `StatementHandle` here
300+
unsafe {
301+
// https://sqlite.org/c3ref/finalize.html
302+
let status = sqlite3_finalize(self.0.as_ptr());
303+
if status == SQLITE_MISUSE {
304+
// Panic in case of detected misuse of SQLite API.
305+
//
306+
// sqlite3_finalize returns it at least in the
307+
// case of detected double free, i.e. calling
308+
// sqlite3_finalize on already finalized
309+
// statement.
310+
panic!("Detected sqlite3_finalize misuse.");
311+
}
312+
}
285313
}
286314
}

0 commit comments

Comments
 (0)