Rust wrapper `Activity::new_with_action` is broken
Version and Platform (required):
- Binary Ninja Version: binaryninja-api
dev/5.2.8353 - Edition: Commercial
- OS: macOS
- OS Version: 26.0.1 (25A362)
- CPU Architecture: arm64
Bug Description:
From rust/src/workflow/activity.rs:
pub fn new_with_action<F>(config: impl AsConfig, mut action: F) -> Ref<Self>
where
F: FnMut(&AnalysisContext),
{
unsafe extern "C" fn cb_action<F: FnMut(&AnalysisContext)>(
ctxt: *mut c_void,
analysis: *mut BNAnalysisContext,
) {
let ctxt = &mut *(ctxt as *mut F);
if let Some(analysis) = NonNull::new(analysis) {
ctxt(&AnalysisContext::from_raw(analysis))
}
}
let config = config.as_config();
let result = unsafe {
BNCreateActivity(
config.as_ptr(),
&mut action as *mut F as *mut c_void,
Some(cb_action::<F>),
)
};
unsafe { Activity::ref_from_raw(NonNull::new(result).unwrap()) }
}
This follows a standard pattern for wrapping C APIs that use callbacks, using a wrapper function in order to allow new_with_action to be called with any object implementing FnMut(&AnalysisContext).
Except, the object is supposed to be stored somewhere. :) In this case, the code just passes &mut action, which is a pointer to action on the stack. This is not valid since the callback will be called after new_with_action has already returned.
This will appear to work if you pass a callback that is a plain function or a closure with no captures, since in that case the contents of the object aren't used. But it will break as soon as you pass a closure with captures.