Skip to content

Commit

Permalink
Ensure consistent lifetimes for UI-accessible BinaryView instances in…
Browse files Browse the repository at this point in the history
… Python.
  • Loading branch information
bpotchik committed Aug 17, 2023
1 parent df64568 commit 1174e54
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 0 deletions.
3 changes: 3 additions & 0 deletions binaryninjaapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -14385,6 +14385,7 @@ namespace BinaryNinja {
static BNScriptingProviderExecuteResult ExecuteScriptInputCallback(void* ctxt, const char* input);
static BNScriptingProviderExecuteResult ExecuteScriptFromFilenameCallback(void *ctxt, const char* filename);
static void CancelScriptInputCallback(void* ctxt);
static void ReleaseBinaryViewCallback(void* ctxt, BNBinaryView* view);
static void SetCurrentBinaryViewCallback(void* ctxt, BNBinaryView* view);
static void SetCurrentFunctionCallback(void* ctxt, BNFunction* func);
static void SetCurrentBasicBlockCallback(void* ctxt, BNBasicBlock* block);
Expand All @@ -14399,6 +14400,7 @@ namespace BinaryNinja {
virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) = 0;
virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::string& filename) = 0;
virtual void CancelScriptInput();
virtual void ReleaseBinaryView(BinaryView* view);
virtual void SetCurrentBinaryView(BinaryView* view);
virtual void SetCurrentFunction(Function* func);
virtual void SetCurrentBasicBlock(BasicBlock* block);
Expand Down Expand Up @@ -14432,6 +14434,7 @@ namespace BinaryNinja {
virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) override;
virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::string& filename) override;
virtual void CancelScriptInput() override;
virtual void ReleaseBinaryView(BinaryView* view) override;
virtual void SetCurrentBinaryView(BinaryView* view) override;
virtual void SetCurrentFunction(Function* func) override;
virtual void SetCurrentBasicBlock(BasicBlock* block) override;
Expand Down
2 changes: 2 additions & 0 deletions binaryninjacore.h
Original file line number Diff line number Diff line change
Expand Up @@ -2540,6 +2540,7 @@ extern "C"
BNScriptingProviderExecuteResult (*executeScriptInput)(void* ctxt, const char* input);
BNScriptingProviderExecuteResult (*executeScriptInputFromFilename)(void *ctxt, const char* input);
void (*cancelScriptInput)(void* ctxt);
void (*releaseBinaryView)(void* ctxt, BNBinaryView* view);
void (*setCurrentBinaryView)(void* ctxt, BNBinaryView* view);
void (*setCurrentFunction)(void* ctxt, BNFunction* func);
void (*setCurrentBasicBlock)(void* ctxt, BNBasicBlock* block);
Expand Down Expand Up @@ -6001,6 +6002,7 @@ extern "C"
BINARYNINJACOREAPI BNScriptingProviderExecuteResult BNExecuteScriptInputFromFilename(
BNScriptingInstance* instance, const char* filename);
BINARYNINJACOREAPI void BNCancelScriptInput(BNScriptingInstance* instance);
BINARYNINJACOREAPI void BNScriptingInstanceReleaseBinaryView(BNScriptingInstance* instance, BNBinaryView* view);
BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentBinaryView(BNScriptingInstance* instance, BNBinaryView* view);
BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentFunction(BNScriptingInstance* instance, BNFunction* func);
BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentBasicBlock(BNScriptingInstance* instance, BNBasicBlock* block);
Expand Down
26 changes: 26 additions & 0 deletions python/binaryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -1911,13 +1911,39 @@ class BinaryView:
registered_view_type = None
_associated_data = {}
_registered_instances = []
_cached_instances = {}

@classmethod
def _cache_insert(cls, instance):
key = ctypes.addressof(instance.handle.contents)
if key not in cls._cached_instances:
cls._cached_instances[ctypes.addressof(instance.handle.contents)] = instance

@classmethod
def _cache_remove(cls, handle):
key = ctypes.addressof(handle.contents)
if key in cls._cached_instances:
cls._cached_instances.pop(key)

@classmethod
def _cache_contains(cls, handle):
return ctypes.addressof(handle.contents) in cls._cached_instances

def __new__(cls, file_metadata=None, parent_view=None, handle=None):
if handle:
key = ctypes.addressof(handle.contents)
if key in cls._cached_instances:
return cls._cached_instances[key]
return super().__new__(cls)

def __init__(
self, file_metadata: Optional['filemetadata.FileMetadata'] = None, parent_view: Optional['BinaryView'] = None,
handle: Optional[core.BNBinaryViewHandle] = None
):
if handle is not None:
_handle = handle
if self.__class__._cache_contains(handle):
return
if file_metadata is None:
self._file = filemetadata.FileMetadata(handle=core.BNGetFileForView(handle))
else:
Expand Down
8 changes: 8 additions & 0 deletions python/scriptingprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def __init__(self, provider, handle=None):
self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input)
self._cb.executeScriptInputFromFilename = self._cb.executeScriptInputFromFilename.__class__(self._execute_script_input_from_filename)
self._cb.cancelScriptInput = self._cb.cancelScriptInput.__class__(self._cancel_script_input)
self._cb.releaseBinaryView = self._cb.releaseBinaryView.__class__(self._release_binary_view)
self._cb.setCurrentBinaryView = self._cb.setCurrentBinaryView.__class__(self._set_current_binary_view)
self._cb.setCurrentFunction = self._cb.setCurrentFunction.__class__(self._set_current_function)
self._cb.setCurrentBasicBlock = self._cb.setCurrentBasicBlock.__class__(self._set_current_basic_block)
Expand Down Expand Up @@ -189,10 +190,17 @@ def _cancel_script_input(self, ctxt):
log_error(traceback.format_exc())
return ScriptingProviderExecuteResult.ScriptExecutionCancelled

def _release_binary_view(self, ctxt, view):
try:
binaryview.BinaryView._cache_remove(view)
except:
log_error(traceback.format_exc())

def _set_current_binary_view(self, ctxt, view):
try:
if view:
view = binaryview.BinaryView(handle=core.BNNewViewReference(view))
binaryview.BinaryView._cache_insert(view)
else:
view = None
self.perform_set_current_binary_view(view)
Expand Down
19 changes: 19 additions & 0 deletions scriptingprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ ScriptingInstance::ScriptingInstance(ScriptingProvider* provider)
cb.externalRefReleased = nullptr;
cb.executeScriptInput = ExecuteScriptInputCallback;
cb.cancelScriptInput = CancelScriptInputCallback;
cb.releaseBinaryView = ReleaseBinaryViewCallback;
cb.setCurrentBinaryView = SetCurrentBinaryViewCallback;
cb.setCurrentFunction = SetCurrentFunctionCallback;
cb.setCurrentBasicBlock = SetCurrentBasicBlockCallback;
Expand Down Expand Up @@ -102,6 +103,14 @@ void ScriptingInstance::CancelScriptInputCallback(void* ctxt)
}


void ScriptingInstance::ReleaseBinaryViewCallback(void* ctxt, BNBinaryView* view)
{
CallbackRef<ScriptingInstance> instance(ctxt);
Ref<BinaryView> object = view ? new BinaryView(BNNewViewReference(view)) : nullptr;
instance->ReleaseBinaryView(object);
}


void ScriptingInstance::SetCurrentBinaryViewCallback(void* ctxt, BNBinaryView* view)
{
CallbackRef<ScriptingInstance> instance(ctxt);
Expand Down Expand Up @@ -168,6 +177,10 @@ void ScriptingInstance::DestroyInstance()

void ScriptingInstance::CancelScriptInput() {}


void ScriptingInstance::ReleaseBinaryView(BinaryView*) {}


void ScriptingInstance::SetCurrentBinaryView(BinaryView*) {}


Expand Down Expand Up @@ -265,6 +278,12 @@ void CoreScriptingInstance::CancelScriptInput()
}


void CoreScriptingInstance::ReleaseBinaryView(BinaryView* view)
{
BNScriptingInstanceReleaseBinaryView(m_object, view ? view->GetObject() : nullptr);
}


void CoreScriptingInstance::SetCurrentBinaryView(BinaryView* view)
{
BNSetScriptingInstanceCurrentBinaryView(m_object, view ? view->GetObject() : nullptr);
Expand Down

0 comments on commit 1174e54

Please sign in to comment.