From a95bf778687ce5770c753abbb30fb9bbf2d53c12 Mon Sep 17 00:00:00 2001 From: d2weber <29163905+d2weber@users.noreply.github.com> Date: Sat, 7 Mar 2026 10:14:01 +0100 Subject: [PATCH] fix(ffi): don't steal Arc in dc_jsonrpc_init (#7962) dc_jsonrpc_init called Arc::from_raw on the account_manager pointer, which took ownership of the caller's refcount. When the local Arc dropped at the end of the function, the refcount was decremented, leaving the C side's pointer with a stolen refcount. This caused a use-after-free race between dc_accounts_unref and dc_jsonrpc_unref at shutdown. Wrap in ManuallyDrop to prevent the implicit drop, keeping the caller's refcount intact. Regression introduced in #7662. --- deltachat-ffi/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 07010d72a..20be73db1 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -15,6 +15,7 @@ use std::collections::BTreeMap; use std::convert::TryFrom; use std::fmt::Write; use std::future::Future; +use std::mem::ManuallyDrop; use std::ptr; use std::str::FromStr; use std::sync::{Arc, LazyLock, Mutex}; @@ -5150,10 +5151,10 @@ pub unsafe extern "C" fn dc_jsonrpc_init( return ptr::null_mut(); } - let account_manager = Arc::from_raw(account_manager); - let cmd_api = block_on(deltachat_jsonrpc::api::CommandApi::from_arc( - account_manager.clone(), - )); + let account_manager = ManuallyDrop::new(Arc::from_raw(account_manager)); + let cmd_api = block_on(deltachat_jsonrpc::api::CommandApi::from_arc(Arc::clone( + &account_manager, + ))); let (request_handle, receiver) = RpcClient::new(); let handle = RpcSession::new(request_handle, cmd_api);