Skip to content

Commit 92808bc

Browse files
rbranemesare
authored andcommitted
improve the CustomBinaryView init process
1 parent 6446d7a commit 92808bc

File tree

1 file changed

+96
-90
lines changed

1 file changed

+96
-90
lines changed

rust/src/custombinaryview.rs

Lines changed: 96 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use binaryninjacore_sys::*;
1919
pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
2020

2121
use std::marker::PhantomData;
22-
use std::mem;
2322
use std::mem::MaybeUninit;
2423
use std::os::raw::c_void;
2524
use std::ptr;
@@ -454,17 +453,34 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
454453
return Err(());
455454
}
456455

457-
// wildly unsafe struct representing the context of a BNCustomBinaryView
458-
// this type should *never* be allowed to drop as the fields are in varying
459-
// states of uninitialized/already consumed throughout the life of the object.
456+
// struct representing the context of a BNCustomBinaryView. Can be safely
457+
// dropped at any moment.
460458
struct CustomViewContext<V>
461459
where
462460
V: CustomBinaryView,
463461
{
464-
view: mem::MaybeUninit<V>,
465462
raw_handle: *mut BNBinaryView,
466-
initialized: bool,
467-
args: V::Args,
463+
state: CustomViewContextState<V>,
464+
}
465+
466+
enum CustomViewContextState<V>
467+
where
468+
V: CustomBinaryView,
469+
{
470+
Uninitialized { args: V::Args },
471+
Initialized { view: V },
472+
// dummy state, used as a helper to change states, only happen if the
473+
// `new` or `init` function fails.
474+
None,
475+
}
476+
477+
impl<V: CustomBinaryView> CustomViewContext<V> {
478+
fn assume_init_ref(&self) -> &V {
479+
let CustomViewContextState::Initialized { view } = &self.state else {
480+
panic!("CustomViewContextState in invalid state");
481+
};
482+
view
483+
}
468484
}
469485

470486
extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
@@ -475,23 +491,24 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
475491
let context = &mut *(ctxt as *mut CustomViewContext<V>);
476492
let handle = BinaryView::ref_from_raw(context.raw_handle);
477493

478-
match V::new(handle.as_ref(), &context.args) {
479-
Ok(v) => {
480-
ptr::write(&mut context.view, mem::MaybeUninit::new(v));
481-
context.initialized = true;
482-
483-
match context
484-
.view
485-
.assume_init_mut()
486-
.init(ptr::read(&context.args))
487-
{
488-
Ok(_) => true,
489-
Err(_) => {
490-
error!("CustomBinaryView::init failed; custom view returned Err");
491-
false
492-
}
494+
// take the uninitialized state and use the args to call init
495+
let mut state = CustomViewContextState::None;
496+
core::mem::swap(&mut context.state, &mut state);
497+
let CustomViewContextState::Uninitialized { args } = state else {
498+
panic!("CustomViewContextState in invalid state");
499+
};
500+
match V::new(handle.as_ref(), &args) {
501+
Ok(mut view) => match view.init(args) {
502+
Ok(_) => {
503+
// put the initialized state
504+
context.state = CustomViewContextState::Initialized { view };
505+
true
493506
}
494-
}
507+
Err(_) => {
508+
error!("CustomBinaryView::init failed; custom view returned Err");
509+
false
510+
}
511+
},
495512
Err(_) => {
496513
error!("CustomBinaryView::new failed; custom view returned Err");
497514
false
@@ -506,48 +523,43 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
506523
{
507524
ffi_wrap!("BinaryViewBase::freeObject", unsafe {
508525
let context = ctxt as *mut CustomViewContext<V>;
509-
let context = *Box::from_raw(context);
510-
511-
if context.initialized {
512-
mem::forget(context.args); // already consumed
513-
// mem::drop(context.view); // cb_init was called
514-
} else {
515-
mem::drop(context.args); // never consumed
516-
// mem::forget(context.view); // cb_init was not called, is uninit
517-
518-
if context.raw_handle.is_null() {
519-
// being called here is essentially a guarantee that BNCreateBinaryViewOfType
520-
// is above above us on the call stack somewhere -- no matter what we do, a crash
521-
// is pretty much certain at this point.
522-
//
523-
// this has been observed when two views of the same BinaryViewType are created
524-
// against the same BNFileMetaData object, and one of the views gets freed while
525-
// the second one is being initialized -- somehow the partially initialized one
526-
// gets freed before BNCreateBinaryViewOfType returns.
527-
//
528-
// multiples views of the same BinaryViewType in a BNFileMetaData object are
529-
// prohibited, so an API contract was violated in order to get here.
530-
//
531-
// if we're here, it's too late to do anything about it, though we can at least not
532-
// run the destructor on the custom view since that memory is unitialized.
533-
error!(
534-
"BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
535-
);
536-
} else if !context.initialized {
537-
// making it here means somebody went out of their way to leak a BinaryView
538-
// after calling BNCreateCustomView and never gave the BNBinaryView handle
539-
// to the core (which would have called cb_init)
540-
//
541-
// the result is a half-initialized BinaryView that the core will happily hand out
542-
// references to via BNGetFileViewofType even though it was never initialized
543-
// all the way.
544-
//
545-
// TODO update when this corner case gets fixed in the core?
546-
//
547-
// we can't do anything to prevent this, but we can at least have the crash
548-
// not be our fault.
549-
error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
550-
}
526+
let context = Box::from_raw(context);
527+
528+
if context.raw_handle.is_null() {
529+
// being called here is essentially a guarantee that BNCreateBinaryViewOfType
530+
// is above above us on the call stack somewhere -- no matter what we do, a crash
531+
// is pretty much certain at this point.
532+
//
533+
// this has been observed when two views of the same BinaryViewType are created
534+
// against the same BNFileMetaData object, and one of the views gets freed while
535+
// the second one is being initialized -- somehow the partially initialized one
536+
// gets freed before BNCreateBinaryViewOfType returns.
537+
//
538+
// multiples views of the same BinaryViewType in a BNFileMetaData object are
539+
// prohibited, so an API contract was violated in order to get here.
540+
//
541+
// if we're here, it's too late to do anything about it, though we can at least not
542+
// run the destructor on the custom view since that memory is unitialized.
543+
error!(
544+
"BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
545+
);
546+
} else if matches!(
547+
&context.state,
548+
CustomViewContextState::None | CustomViewContextState::Uninitialized { .. }
549+
) {
550+
// making it here means somebody went out of their way to leak a BinaryView
551+
// after calling BNCreateCustomView and never gave the BNBinaryView handle
552+
// to the core (which would have called cb_init)
553+
//
554+
// the result is a half-initialized BinaryView that the core will happily hand out
555+
// references to via BNGetFileViewofType even though it was never initialized
556+
// all the way.
557+
//
558+
// TODO update when this corner case gets fixed in the core?
559+
//
560+
// we can't do anything to prevent this, but we can at least have the crash
561+
// not be our fault.
562+
error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
551563
}
552564
})
553565
}
@@ -564,8 +576,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
564576
ffi_wrap!("BinaryViewBase::read", unsafe {
565577
let context = &*(ctxt as *mut CustomViewContext<V>);
566578
let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
567-
568-
context.view.assume_init_ref().read(dest, offset)
579+
context.assume_init_ref().read(dest, offset)
569580
})
570581
}
571582

@@ -582,7 +593,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
582593
let context = &*(ctxt as *mut CustomViewContext<V>);
583594
let src = slice::from_raw_parts(src as *const u8, len);
584595

585-
context.view.assume_init_ref().write(offset, src)
596+
context.assume_init_ref().write(offset, src)
586597
})
587598
}
588599

@@ -599,7 +610,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
599610
let context = &*(ctxt as *mut CustomViewContext<V>);
600611
let src = slice::from_raw_parts(src as *const u8, len);
601612

602-
context.view.assume_init_ref().insert(offset, src)
613+
context.assume_init_ref().insert(offset, src)
603614
})
604615
}
605616

@@ -610,7 +621,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
610621
ffi_wrap!("BinaryViewBase::remove", unsafe {
611622
let context = &*(ctxt as *mut CustomViewContext<V>);
612623

613-
context.view.assume_init_ref().remove(offset, len as usize)
624+
context.assume_init_ref().remove(offset, len as usize)
614625
})
615626
}
616627

@@ -621,7 +632,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
621632
ffi_wrap!("BinaryViewBase::modification_status", unsafe {
622633
let context = &*(ctxt as *mut CustomViewContext<V>);
623634

624-
context.view.assume_init_ref().modification_status(offset)
635+
context.assume_init_ref().modification_status(offset)
625636
})
626637
}
627638

@@ -632,7 +643,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
632643
ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
633644
let context = &*(ctxt as *mut CustomViewContext<V>);
634645

635-
context.view.assume_init_ref().offset_valid(offset)
646+
context.assume_init_ref().offset_valid(offset)
636647
})
637648
}
638649

@@ -643,7 +654,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
643654
ffi_wrap!("BinaryViewBase::readable", unsafe {
644655
let context = &*(ctxt as *mut CustomViewContext<V>);
645656

646-
context.view.assume_init_ref().offset_readable(offset)
657+
context.assume_init_ref().offset_readable(offset)
647658
})
648659
}
649660

@@ -654,7 +665,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
654665
ffi_wrap!("BinaryViewBase::writable", unsafe {
655666
let context = &*(ctxt as *mut CustomViewContext<V>);
656667

657-
context.view.assume_init_ref().offset_writable(offset)
668+
context.assume_init_ref().offset_writable(offset)
658669
})
659670
}
660671

@@ -665,7 +676,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
665676
ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
666677
let context = &*(ctxt as *mut CustomViewContext<V>);
667678

668-
context.view.assume_init_ref().offset_executable(offset)
679+
context.assume_init_ref().offset_executable(offset)
669680
})
670681
}
671682

@@ -676,7 +687,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
676687
ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
677688
let context = &*(ctxt as *mut CustomViewContext<V>);
678689

679-
context.view.assume_init_ref().offset_backed_by_file(offset)
690+
context.assume_init_ref().offset_backed_by_file(offset)
680691
})
681692
}
682693

@@ -687,10 +698,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
687698
ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
688699
let context = &*(ctxt as *mut CustomViewContext<V>);
689700

690-
context
691-
.view
692-
.assume_init_ref()
693-
.next_valid_offset_after(offset)
701+
context.assume_init_ref().next_valid_offset_after(offset)
694702
})
695703
}
696704

@@ -701,7 +709,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
701709
ffi_wrap!("BinaryViewBase::start", unsafe {
702710
let context = &*(ctxt as *mut CustomViewContext<V>);
703711

704-
context.view.assume_init_ref().start()
712+
context.assume_init_ref().start()
705713
})
706714
}
707715

@@ -712,7 +720,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
712720
ffi_wrap!("BinaryViewBase::len", unsafe {
713721
let context = &*(ctxt as *mut CustomViewContext<V>);
714722

715-
context.view.assume_init_ref().len() as u64
723+
context.assume_init_ref().len() as u64
716724
})
717725
}
718726

@@ -723,7 +731,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
723731
ffi_wrap!("BinaryViewBase::entry_point", unsafe {
724732
let context = &*(ctxt as *mut CustomViewContext<V>);
725733

726-
context.view.assume_init_ref().entry_point()
734+
context.assume_init_ref().entry_point()
727735
})
728736
}
729737

@@ -734,7 +742,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
734742
ffi_wrap!("BinaryViewBase::executable", unsafe {
735743
let context = &*(ctxt as *mut CustomViewContext<V>);
736744

737-
context.view.assume_init_ref().executable()
745+
context.assume_init_ref().executable()
738746
})
739747
}
740748

@@ -745,7 +753,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
745753
ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
746754
let context = &*(ctxt as *mut CustomViewContext<V>);
747755

748-
context.view.assume_init_ref().default_endianness()
756+
context.assume_init_ref().default_endianness()
749757
})
750758
}
751759

@@ -756,7 +764,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
756764
ffi_wrap!("BinaryViewBase::relocatable", unsafe {
757765
let context = &*(ctxt as *mut CustomViewContext<V>);
758766

759-
context.view.assume_init_ref().relocatable()
767+
context.assume_init_ref().relocatable()
760768
})
761769
}
762770

@@ -767,7 +775,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
767775
ffi_wrap!("BinaryViewBase::address_size", unsafe {
768776
let context = &*(ctxt as *mut CustomViewContext<V>);
769777

770-
context.view.assume_init_ref().address_size()
778+
context.assume_init_ref().address_size()
771779
})
772780
}
773781

@@ -782,10 +790,8 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
782790
}
783791

784792
let ctxt = Box::new(CustomViewContext::<V> {
785-
view: mem::MaybeUninit::uninit(),
786793
raw_handle: ptr::null_mut(),
787-
initialized: false,
788-
args: view_args,
794+
state: CustomViewContextState::Uninitialized { args: view_args },
789795
});
790796

791797
let ctxt = Box::into_raw(ctxt);

0 commit comments

Comments
 (0)