I have a callback type in C which is:
extern "C" fn disk_unmounted(disk: DADiskRef, ctx: *const T);
The callback is actually written in Rust but called by OSX Disk Arbitration API.
Now, i want to pass a &T
as the ctx
instead of *const T
(which means i have to dereference it myself).
I can change the type of the callback to
extern "C" fn disk_unmounted(disk: DADiskRef, ctx: &T);
but will this cause any UB?
tl;dr:
can &T
be used in place of *const T
and &mut T
in place of *mut T
while doing FFI?
This should be fine as long as the invariants of &
/&mut
references are upheld by the C side.
2 Likes
alice
August 16, 2023, 8:43am
3
Well, it depends. If C code tried to call that method with a null pointer, then that's immediate UB because references can't be null.
2 Likes
Well, all the code is in Rust and i'm sure the reference will be non-null as i'm passing the reference to the callback myself.
the callback handler is similar to this
extern "C" fn mount_handler(disk: DADiskRef, ctx: &Arc<RwLock<bool>>) -> DADissenterRef {
let allow = ctx.try_read().unwrap().clone();
if allow {
ptr::null()
} else {
let msg = CFString::new("Mounting of devices is not allowed");
let maybe_res = unsafe {
DADissenterCreate(CFAllocatorGetDefault(), -119930876, msg.as_concrete_TypeRef())
};
return maybe_res;
}
}
and the way the callback is registered:
let allow = self.allow_mount.clone(); // type is Arc<RwLock<bool>>
unsafe {
DARegisterDiskMountApprovalCallback(
session,
kDADiskDescriptionMatchVolumeMountable,
mount_handler,
&allow,
);
}
the actual function declaration of DARegisterDiskMountApprovalCallback
is:
void DARegisterDiskMountApprovalCallback(DASessionRef session, CFDictionaryRef match, DADiskMountApprovalCallback callback, void *context);
which i modified in Rust:
extern "C" {
fn DARegisterDiskMountApprovalCallback(
session: DASessionRef,
r#match: CFDictionaryRef,
callback: DADiskMountApprovalCallback,
ctx: &Arc<RwLock<bool>>,
);
}
i do get the lint
warning: `extern` block uses type `Arc<RwLock<bool>>`, which is not FFI-safe
If the performance is not absolutely critical why not use Option<&T>
instead of &T
? This is guaranteed to have the same layout but if through some bug you got a null pointer you would panic in a predictable way.
2 Likes
riking
August 16, 2023, 5:02pm
6
You should absolutely keep the extern
block as the correct function definition and create a helper function which takes your &Arc and performs the cast.