Skip to content

Commit 934192a

Browse files
committed
fix scriptingprovider inconsistencies
1 parent f1890a8 commit 934192a

File tree

2 files changed

+302
-65
lines changed

2 files changed

+302
-65
lines changed

rust/src/scriptingprovider.rs

Lines changed: 107 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use core::{ffi, mem, ptr};
2+
use std::mem::MaybeUninit;
23
use std::pin::Pin;
4+
use std::sync::Arc;
35

46
use binaryninjacore_sys::*;
57

@@ -18,6 +20,9 @@ pub struct ScriptingProvider {
1820
handle: ptr::NonNull<BNScriptingProvider>,
1921
}
2022

23+
unsafe impl Sync for ScriptingProvider {}
24+
unsafe impl Send for ScriptingProvider {}
25+
2126
impl ScriptingProvider {
2227
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNScriptingProvider>) -> Self {
2328
Self { handle }
@@ -95,30 +100,9 @@ impl ScriptingProvider {
95100
}
96101
}
97102

98-
pub fn new_instance<S: ScriptingInstanceCallbacks>(&self) -> ScriptingInstance {
99-
// SAFETY freed by cb_destroy_instance
100-
let uninit = Box::leak(Box::new(mem::MaybeUninit::zeroed()));
101-
let mut callbacks = BNScriptingInstanceCallbacks {
102-
context: unsafe { uninit.assume_init_mut() as *mut S as *mut ffi::c_void },
103-
destroyInstance: Some(cb_destroy_instance::<S>),
104-
externalRefTaken: Some(cb_external_ref_taken::<S>),
105-
externalRefReleased: Some(cb_external_ref_released::<S>),
106-
executeScriptInput: Some(cb_execute_script_input::<S>),
107-
executeScriptInputFromFilename: Some(cb_execute_script_input_from_filename::<S>),
108-
cancelScriptInput: Some(cb_cancel_script_input::<S>),
109-
releaseBinaryView: Some(cb_release_binary_view::<S>),
110-
setCurrentBinaryView: Some(cb_set_current_binary_view::<S>),
111-
setCurrentFunction: Some(cb_set_current_function::<S>),
112-
setCurrentBasicBlock: Some(cb_set_current_basic_block::<S>),
113-
setCurrentAddress: Some(cb_set_current_address::<S>),
114-
setCurrentSelection: Some(cb_set_current_selection::<S>),
115-
completeInput: Some(cb_complete_input::<S>),
116-
stop: Some(cb_stop::<S>),
117-
};
118-
let result = unsafe { BNInitScriptingInstance(self.as_raw(), &mut callbacks) };
119-
let instance = unsafe { ScriptingInstance::from_raw(ptr::NonNull::new(result).unwrap()) };
120-
uninit.write(S::new(*self, &instance));
121-
instance
103+
pub fn new_instance(&self) -> Ref<ScriptingInstance> {
104+
let instance = unsafe { BNCreateScriptingProviderInstance(self.as_raw()) };
105+
unsafe { ScriptingInstance::ref_from_raw(ptr::NonNull::new(instance).unwrap()) }
122106
}
123107
}
124108

@@ -143,6 +127,9 @@ pub struct ScriptingInstance {
143127
handle: ptr::NonNull<BNScriptingInstance>,
144128
}
145129

130+
unsafe impl Sync for ScriptingInstance {}
131+
unsafe impl Send for ScriptingInstance {}
132+
146133
impl ToOwned for ScriptingInstance {
147134
type Owned = Ref<Self>;
148135

@@ -163,18 +150,10 @@ unsafe impl RefCountable for ScriptingInstance {
163150
}
164151

165152
impl ScriptingInstance {
166-
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNScriptingInstance>) -> Self {
167-
Self { handle }
168-
}
169-
170153
pub(crate) unsafe fn ref_from_raw(handle: ptr::NonNull<BNScriptingInstance>) -> Ref<Self> {
171154
Ref::new(Self { handle })
172155
}
173156

174-
pub(crate) unsafe fn into_raw(self) -> *mut BNScriptingInstance {
175-
mem::ManuallyDrop::new(self).handle.as_ptr()
176-
}
177-
178157
#[allow(clippy::mut_from_ref)]
179158
pub(crate) unsafe fn as_raw(&self) -> &mut BNScriptingInstance {
180159
&mut *self.handle.as_ptr()
@@ -214,10 +193,10 @@ impl ScriptingInstance {
214193
unsafe { BNNotifyInputReadyStateForScriptingInstance(self.as_raw(), state) }
215194
}
216195

217-
pub fn register_output_listener<L: ScriptingOutputListener>(
218-
self,
196+
pub fn register_output_listener<'a, L: ScriptingOutputListener>(
197+
&'a self,
219198
listener: L,
220-
) -> ScriptingInstanceWithListener<L> {
199+
) -> ScriptingInstanceWithListener<'a, L> {
221200
let mut listener = Box::pin(listener);
222201
let mut callbacks = BNScriptingOutputListener {
223202
context: unsafe { listener.as_mut().get_unchecked_mut() } as *mut _ as *mut ffi::c_void,
@@ -323,26 +302,26 @@ impl ScriptingInstance {
323302
}
324303
}
325304

326-
pub struct ScriptingInstanceWithListener<L: ScriptingOutputListener> {
327-
handle: ScriptingInstance,
305+
pub struct ScriptingInstanceWithListener<'a, L: ScriptingOutputListener> {
306+
handle: &'a ScriptingInstance,
328307
listener: Pin<Box<L>>,
329308
}
330309

331-
impl<L: ScriptingOutputListener> AsRef<ScriptingInstance> for ScriptingInstanceWithListener<L> {
332-
fn as_ref(&self) -> &ScriptingInstance {
333-
&self.handle
310+
impl<L: ScriptingOutputListener> AsRef<L> for ScriptingInstanceWithListener<'_, L> {
311+
fn as_ref(&self) -> &L {
312+
&self.listener
334313
}
335314
}
336315

337-
impl<L: ScriptingOutputListener> core::ops::Deref for ScriptingInstanceWithListener<L> {
338-
type Target = ScriptingInstance;
316+
impl<L: ScriptingOutputListener> core::ops::Deref for ScriptingInstanceWithListener<'_, L> {
317+
type Target = L;
339318
fn deref(&self) -> &Self::Target {
340-
&self.handle
319+
&self.listener
341320
}
342321
}
343322

344-
impl<L: ScriptingOutputListener> ScriptingInstanceWithListener<L> {
345-
pub fn unregister(mut self) -> ScriptingInstance {
323+
impl<L: ScriptingOutputListener> ScriptingInstanceWithListener<'_, L> {
324+
pub fn unregister(mut self) {
346325
let mut callbacks = BNScriptingOutputListener {
347326
context: unsafe { self.listener.as_mut().get_unchecked_mut() } as *mut _
348327
as *mut ffi::c_void,
@@ -354,28 +333,29 @@ impl<L: ScriptingOutputListener> ScriptingInstanceWithListener<L> {
354333
unsafe {
355334
BNUnregisterScriptingInstanceOutputListener(self.handle.as_raw(), &mut callbacks)
356335
};
357-
// drop the listener
358-
let Self {
359-
handle: instance,
360-
listener: _,
361-
} = self;
362-
// return the inner instance
363-
instance
364336
}
365337
}
366338

367339
pub trait ScriptingCustomProvider: Sync + Send {
340+
type Instance: ScriptingInstanceCallbacks;
368341
fn new(core: ScriptingProvider) -> Self;
369-
fn create_instance(&self) -> ScriptingInstance;
342+
fn init_instance(&self, handle: CoreScriptingInstance) -> Self::Instance;
343+
fn get_core(&self) -> &ScriptingProvider;
370344
fn load_module(&self, repo_path: &str, plugin_path: &str, force: bool) -> bool;
371345
fn install_modules(&self, modules: &str) -> bool;
346+
347+
fn new_instance(&self) -> (Arc<Self::Instance>, Ref<ScriptingInstance>)
348+
where
349+
Self: Sized,
350+
{
351+
register_instance::<Self>(self)
352+
}
372353
}
373354

355+
pub struct CoreScriptingInstance(Ref<ScriptingInstance>);
356+
374357
pub trait ScriptingInstanceCallbacks: Sync + Send {
375-
fn new(provider: ScriptingProvider, handle: &ScriptingInstance) -> Self;
376-
fn destroy_instance(&self);
377-
fn external_ref_taken(&self);
378-
fn external_ref_released(&self);
358+
fn get_core(&self) -> &CoreScriptingInstance;
379359
fn execute_script_input(&self, input: &str) -> ScriptingProviderExecuteResult;
380360
fn execute_script_input_from_filename(&self, input: &str) -> ScriptingProviderExecuteResult;
381361
fn cancel_script_input(&self);
@@ -387,6 +367,22 @@ pub trait ScriptingInstanceCallbacks: Sync + Send {
387367
fn set_current_selection(&self, begin: u64, end: u64);
388368
fn complete_input(&self, text: &str, state: u64) -> String;
389369
fn stop(&self);
370+
371+
fn notify_output(&self, text: &str) {
372+
self.get_core().0.notify_output(text);
373+
}
374+
fn notify_warning(&self, text: &str) {
375+
self.get_core().0.notify_warning(text);
376+
}
377+
fn notify_error(&self, text: &str) {
378+
self.get_core().0.notify_error(text);
379+
}
380+
fn register_output_listener<'a, L: ScriptingOutputListener>(
381+
&'a self,
382+
listener: L,
383+
) -> ScriptingInstanceWithListener<'a, L> {
384+
self.get_core().0.register_output_listener(listener)
385+
}
390386
}
391387

392388
pub trait ScriptingOutputListener: Sync + Send {
@@ -404,7 +400,6 @@ where
404400
{
405401
let name = name.into_bytes_with_nul();
406402
let api_name = api_name.into_bytes_with_nul();
407-
// SAFETY: Websocket provider is never freed
408403
let provider_uinit = Box::leak(Box::new(mem::MaybeUninit::zeroed()));
409404
let result = unsafe {
410405
BNRegisterScriptingProvider(
@@ -423,11 +418,59 @@ where
423418
(unsafe { provider_uinit.assume_init_ref() }, provider_core)
424419
}
425420

421+
fn register_instance<S: ScriptingCustomProvider>(
422+
provider: &S,
423+
) -> (Arc<S::Instance>, Ref<ScriptingInstance>) {
424+
let instance_uninit: *const MaybeUninit<S::Instance> = Arc::into_raw(Arc::new_uninit());
425+
let mut callbacks = BNScriptingInstanceCallbacks {
426+
context: instance_uninit as *mut ffi::c_void,
427+
destroyInstance: Some(cb_destroy_instance::<S::Instance>),
428+
externalRefTaken: Some(cb_external_ref_taken::<S::Instance>),
429+
externalRefReleased: Some(cb_external_ref_released::<S::Instance>),
430+
executeScriptInput: Some(cb_execute_script_input::<S::Instance>),
431+
executeScriptInputFromFilename: Some(cb_execute_script_input_from_filename::<S::Instance>),
432+
cancelScriptInput: Some(cb_cancel_script_input::<S::Instance>),
433+
releaseBinaryView: Some(cb_release_binary_view::<S::Instance>),
434+
setCurrentBinaryView: Some(cb_set_current_binary_view::<S::Instance>),
435+
setCurrentFunction: Some(cb_set_current_function::<S::Instance>),
436+
setCurrentBasicBlock: Some(cb_set_current_basic_block::<S::Instance>),
437+
setCurrentAddress: Some(cb_set_current_address::<S::Instance>),
438+
setCurrentSelection: Some(cb_set_current_selection::<S::Instance>),
439+
completeInput: Some(cb_complete_input::<S::Instance>),
440+
stop: Some(cb_stop::<S::Instance>),
441+
};
442+
let handle = unsafe { BNInitScriptingInstance(provider.get_core().as_raw(), &mut callbacks) };
443+
let core_instance = unsafe {
444+
CoreScriptingInstance(ScriptingInstance::ref_from_raw(
445+
ptr::NonNull::new(handle).unwrap(),
446+
))
447+
};
448+
let new_instance = provider.init_instance(core_instance);
449+
450+
// recreate the Arc, initialize it, clone it, then leak it again
451+
let instance: Arc<_> = unsafe {
452+
let mut instance_uninit = Arc::from_raw(instance_uninit);
453+
Arc::get_mut(&mut instance_uninit)
454+
.unwrap()
455+
.write(new_instance);
456+
let instance = Arc::<MaybeUninit<_>>::assume_init(instance_uninit);
457+
let result = Arc::clone(&instance);
458+
// leak, this will be owned by the [ScriptingInstance]
459+
let _ptr = Arc::into_raw(instance);
460+
result
461+
};
462+
463+
let core_instance =
464+
unsafe { ScriptingInstance::ref_from_raw(ptr::NonNull::new(handle).unwrap()) };
465+
(instance, core_instance)
466+
}
467+
426468
unsafe extern "C" fn cb_create_instance<S: ScriptingCustomProvider>(
427469
ctxt: *mut ffi::c_void,
428470
) -> *mut BNScriptingInstance {
429471
let ctxt = &mut *(ctxt as *mut S);
430-
ctxt.create_instance().into_raw()
472+
let (_rust_instance, core_instance) = register_instance(ctxt);
473+
core_instance.as_raw()
431474
}
432475

433476
unsafe extern "C" fn cb_load_module<S: ScriptingCustomProvider>(
@@ -456,19 +499,17 @@ unsafe extern "C" fn cb_install_modules<S: ScriptingCustomProvider>(
456499
}
457500

458501
unsafe extern "C" fn cb_destroy_instance<S: ScriptingInstanceCallbacks>(ctxt: *mut ffi::c_void) {
459-
drop(Box::from_raw(ctxt as *mut S))
502+
Arc::from_raw(ctxt as *mut S);
460503
}
461504

462505
unsafe extern "C" fn cb_external_ref_taken<S: ScriptingInstanceCallbacks>(ctxt: *mut ffi::c_void) {
463-
let ctxt = &mut *(ctxt as *mut S);
464-
ctxt.external_ref_taken()
506+
Arc::increment_strong_count(ctxt as *mut S);
465507
}
466508

467509
unsafe extern "C" fn cb_external_ref_released<S: ScriptingInstanceCallbacks>(
468510
ctxt: *mut ffi::c_void,
469511
) {
470-
let ctxt = &mut *(ctxt as *mut S);
471-
ctxt.external_ref_released()
512+
Arc::decrement_strong_count(ctxt as *mut S);
472513
}
473514

474515
unsafe extern "C" fn cb_execute_script_input<S: ScriptingInstanceCallbacks>(
@@ -477,7 +518,8 @@ unsafe extern "C" fn cb_execute_script_input<S: ScriptingInstanceCallbacks>(
477518
) -> BNScriptingProviderExecuteResult {
478519
let input = ffi::CStr::from_ptr(input);
479520
let ctxt = &mut *(ctxt as *mut S);
480-
ctxt.execute_script_input(&input.to_string_lossy())
521+
let result = ctxt.execute_script_input(&input.to_string_lossy());
522+
result
481523
}
482524

483525
unsafe extern "C" fn cb_execute_script_input_from_filename<S: ScriptingInstanceCallbacks>(

0 commit comments

Comments
 (0)