Skip to content

Commit bfbfd8a

Browse files
TonyMassonclaude
andcommitted
Fix Transaction memory safety by using Database reference counting
- Transaction now owns a Database clone instead of raw CBLDatabase pointer - Uses Database::clone() which automatically handles reference counting via retain() - Database is automatically released when Transaction is dropped - Eliminates use-after-free vulnerability where Database could be dropped while Transaction still holds a raw pointer This ensures the underlying CBLDatabase stays alive for the entire transaction lifetime through Couchbase Lite's internal reference counting. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent f7748e0 commit bfbfd8a

File tree

1 file changed

+8
-7
lines changed

1 file changed

+8
-7
lines changed

src/database.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ impl Database {
359359
///
360360
/// For simpler cases, consider using `in_transaction()` instead.
361361
pub fn begin_transaction(&mut self) -> Result<Transaction> {
362-
Transaction::new(self.get_ref())
362+
Transaction::new(self)
363363
}
364364

365365
/// Encrypts or decrypts a database, or changes its encryption key.
@@ -606,20 +606,21 @@ impl Database {
606606
/// A database transaction that can be committed or rolled back.
607607
/// When dropped without being committed, the transaction is automatically rolled back.
608608
pub struct Transaction {
609-
db_ref: *mut CBLDatabase,
609+
db: Database,
610610
committed: bool,
611611
}
612612

613613
impl Transaction {
614-
fn new(db_ref: *mut CBLDatabase) -> Result<Self> {
614+
fn new(db: &Database) -> Result<Self> {
615+
let db_clone = db.clone();
615616
unsafe {
616617
let mut err = CBLError::default();
617-
if !CBLDatabase_BeginTransaction(db_ref, &mut err) {
618+
if !CBLDatabase_BeginTransaction(db_clone.get_ref(), &mut err) {
618619
return failure(err);
619620
}
620621
}
621622
Ok(Transaction {
622-
db_ref,
623+
db: db_clone,
623624
committed: false,
624625
})
625626
}
@@ -628,7 +629,7 @@ impl Transaction {
628629
pub fn commit(mut self) -> Result<()> {
629630
unsafe {
630631
let mut err = CBLError::default();
631-
if !CBLDatabase_EndTransaction(self.db_ref, true, &mut err) {
632+
if !CBLDatabase_EndTransaction(self.db.get_ref(), true, &mut err) {
632633
return failure(err);
633634
}
634635
}
@@ -642,7 +643,7 @@ impl Drop for Transaction {
642643
if !self.committed {
643644
unsafe {
644645
let mut err = CBLError::default();
645-
let _ = CBLDatabase_EndTransaction(self.db_ref, false, &mut err);
646+
let _ = CBLDatabase_EndTransaction(self.db.get_ref(), false, &mut err);
646647
}
647648
}
648649
}

0 commit comments

Comments
 (0)