Skip to content

Commit 70b47dd

Browse files
authored
sqlite3: pass None for NULL authorizer args instead of crashing (RustPython#8534)
* sqlite3: pass None for NULL authorizer args instead of crashing CPython's authorizer callback receives NULL for arg1/arg2/db_name/access when not applicable (e.g. SQLITE_READ on a table gives NULL for the database name in some versions). Previously RustPython passed these pointers to ptr_to_str which would crash or produce an error. Now ptr_to_str_or_none is used: NULL pointers become Python None, which matches CPython behavior and allows test_table_access and test_column_access to pass. Assisted-by: GitHub Copilot:claude-sonnet-4-6 * fix(sqlite3): avoid reentrant deadlock in Statement::new sqlite3_prepare_v2 can synchronously invoke the authorizer callback, which may call back into Connection methods (e.g. set_authorizer) that require the same db lock. Holding db_lock across the prepare() call caused a self-deadlock when a callback re-entered the connection. Release the lock after sql_limit check and copy the raw handle before calling prepare(), so FFI calls that can trigger Python re-entrancy happen outside the lock scope. Fixes hang in test_authorizer_concurrent_mutation_in_call
1 parent 32b1f21 commit 70b47dd

2 files changed

Lines changed: 21 additions & 11 deletions

File tree

Lib/test/test_sqlite3/test_userfunctions.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,13 +800,11 @@ def setUp(self):
800800
def tearDown(self):
801801
self.con.close()
802802

803-
@unittest.expectedFailure # TODO: RUSTPYTHON; error message differs
804803
def test_table_access(self):
805804
with self.assertRaises(sqlite.DatabaseError) as cm:
806805
self.con.execute("select * from t2")
807806
self.assertIn('prohibited', str(cm.exception))
808807

809-
@unittest.expectedFailure # TODO: RUSTPYTHON; error message differs
810808
def test_column_access(self):
811809
with self.assertRaises(sqlite.DatabaseError) as cm:
812810
self.con.execute("select c2 from t1")

crates/stdlib/src/_sqlite3.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -587,10 +587,10 @@ mod _sqlite3 {
587587
) -> c_int {
588588
let (callable, vm) = unsafe { (*data.cast::<Self>()).retrieve() };
589589
let f = || -> PyResult<c_int> {
590-
let arg1 = ptr_to_str(arg1, vm)?;
591-
let arg2 = ptr_to_str(arg2, vm)?;
592-
let db_name = ptr_to_str(db_name, vm)?;
593-
let access = ptr_to_str(access, vm)?;
590+
let arg1 = ptr_to_str_or_none(arg1, vm)?;
591+
let arg2 = ptr_to_str_or_none(arg2, vm)?;
592+
let db_name = ptr_to_str_or_none(db_name, vm)?;
593+
let access = ptr_to_str_or_none(access, vm)?;
594594

595595
let val = callable.call((action, arg1, arg2, db_name, access), vm)?;
596596
let Some(val) = val.downcast_ref::<PyInt>() else {
@@ -2842,12 +2842,14 @@ mod _sqlite3 {
28422842
}
28432843
let sql_cstr = sql.to_cstring(vm)?;
28442844

2845-
let db = connection.db_lock(vm)?;
2846-
2847-
db.sql_limit(sql.byte_len(), vm)?;
2845+
let raw = {
2846+
let db = connection.db_lock(vm)?;
2847+
db.sql_limit(sql.byte_len(), vm)?;
2848+
**db
2849+
};
28482850

28492851
let mut tail = null();
2850-
let st = db.prepare(sql_cstr.as_ptr(), &mut tail, vm)?;
2852+
let st = raw.prepare(sql_cstr.as_ptr(), &mut tail, vm)?;
28512853

28522854
let Some(st) = st else {
28532855
return Ok(None);
@@ -3540,7 +3542,17 @@ mod _sqlite3 {
35403542
return Err(vm.new_memory_error("string pointer is null"));
35413543
}
35423544
unsafe { CStr::from_ptr(p).to_str() }
3543-
.map_err(|_| vm.new_value_error("Invalid UIF-8 codepoint"))
3545+
.map_err(|_| vm.new_value_error("Invalid UTF-8 codepoint"))
3546+
}
3547+
3548+
fn ptr_to_str_or_none(p: *const libc::c_char, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
3549+
if p.is_null() {
3550+
return Ok(vm.ctx.none());
3551+
}
3552+
let s = unsafe { CStr::from_ptr(p) }
3553+
.to_str()
3554+
.map_err(|_| vm.new_value_error("Invalid UTF-8 codepoint".to_owned()))?;
3555+
Ok(vm.ctx.new_str(s).into())
35443556
}
35453557

35463558
fn ptr_to_string(

0 commit comments

Comments
 (0)