summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorPeter LaFosse <peter@vector35.com>2025-10-31 11:19:15 -0400
committerGlenn Smith <glenn@vector35.com>2026-01-28 22:50:07 -0500
commit2099579041340399246976046395863741933136 (patch)
treebd2e5cc337ed2381e7b43409e9aca92a724ad8fa /python
parentfd8eeb4cc6a4ba19631ef787b3fc505ae2b1eef8 (diff)
Fix a fundamental problem with TypeBuilder.handle by deleting it
TypeBuilder.handle creates an immutable_type gets the handle and then deletes the object to which the handle belonged to. This was fundamentally a UAF. immutable_copy is an error prone api as its very easy to misuse. We should consider a re-architecture of this API. `TypeBuilder.immutable_copy().<anything>` or `Type.mutable_copy().<anything>` should be considered sketchy and immutable_copy().handle is just a straight up UAF. Prior to this commit the following would cause a crash: current_data_variable.type = PointerBuilder.create(FunctionType.create())
Diffstat (limited to 'python')
-rw-r--r--python/binaryview.py19
-rw-r--r--python/function.py24
-rw-r--r--python/highlevelil.py4
-rw-r--r--python/mediumlevelil.py3
-rw-r--r--python/types.py78
5 files changed, 79 insertions, 49 deletions
diff --git a/python/binaryview.py b/python/binaryview.py
index 54898478..2f441f34 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -5432,8 +5432,8 @@ class BinaryView:
if isinstance(var_type, str):
(var_type, _) = self.parse_type_string(var_type)
- tc = var_type._to_core_struct()
- core.BNDefineDataVariable(self.handle, addr, tc)
+ tc = var_type.immutable_copy()
+ core.BNDefineDataVariable(self.handle, addr, tc._to_core_struct())
if name is not None:
if isinstance(name, str):
@@ -5467,8 +5467,11 @@ class BinaryView:
if isinstance(var_type, str):
(var_type, _) = self.parse_type_string(var_type)
- tc = var_type._to_core_struct()
- core.BNDefineUserDataVariable(self.handle, addr, tc)
+
+ # this var_type temporary is essential! It holds the reference to the immutable type
+ # until after BNDefineUserDataVariable takes the reference.
+ var_type = var_type.immutable_copy()
+ core.BNDefineUserDataVariable(self.handle, addr, var_type._to_core_struct())
if name is not None:
if isinstance(name, str):
@@ -8523,6 +8526,7 @@ class BinaryView:
default_name = new_name
assert default_name is not None, "default_name can only be None if named type is derived from string passed to type_obj"
name = _types.QualifiedName(default_name)._to_core_struct()
+ type_obj = type_obj.immutable_copy()
reg_name = core.BNDefineAnalysisType(self.handle, type_id, name, type_obj.handle)
result = _types.QualifiedName._from_core_struct(reg_name)
core.BNFreeQualifiedName(reg_name)
@@ -8553,6 +8557,7 @@ class BinaryView:
if name is None:
raise ValueError("name can only be None if named type is derived from string passed to type_obj")
_name = _types.QualifiedName(name)._to_core_struct()
+ type_obj = type_obj.immutable_copy()
core.BNDefineUserAnalysisType(self.handle, _name, type_obj.handle)
def define_types(self, types: List[Tuple[str, Optional['_types.QualifiedNameType'], StringOrType]], progress_func: Optional[ProgressFuncType]) -> Mapping[str, '_types.QualifiedName']:
@@ -8831,7 +8836,8 @@ class BinaryView:
(type_obj, new_name) = self.parse_type_string(type_obj)
if name is None:
_name = new_name
- if not isinstance(type_obj, (_types.Type, _types.TypeBuilder)):
+ type_obj = type_obj.immutable_copy()
+ if not isinstance(type_obj, _types.Type):
raise TypeError("type_obj must be a Type object")
if _name is None:
raise ValueError("name can only be None if named type is derived from string passed to type_obj")
@@ -8865,7 +8871,8 @@ class BinaryView:
(type_obj, new_name) = self.parse_type_string(type_obj)
if name is None:
_name = new_name
- if not isinstance(type_obj, (_types.Type, _types.TypeBuilder)):
+ type_obj = type_obj.immutable_copy()
+ if not isinstance(type_obj, _types.Type):
raise TypeError("type_obj must be a Type object")
if _name is None:
raise ValueError("name can only be None if named type is derived from string passed to type_obj")
diff --git a/python/function.py b/python/function.py
index a592efba..b66fd039 100644
--- a/python/function.py
+++ b/python/function.py
@@ -1359,6 +1359,7 @@ class Function:
type_conf.type = value.handle
type_conf.confidence = core.max_confidence
else:
+ value = value.immutable_copy()
type_conf.type = value.handle
type_conf.confidence = value.confidence
core.BNSetUserFunctionReturnType(self.handle, type_conf)
@@ -2270,11 +2271,14 @@ class Function:
def apply_imported_types(self, sym: 'types.CoreSymbol', type: Optional[StringOrType] = None) -> None:
if isinstance(type, str):
(type, _) = self.view.parse_type_string(type)
+ if type is not None:
+ type = type.immutable_copy()
core.BNApplyImportedTypes(self.handle, sym.handle, None if type is None else type.handle)
def apply_auto_discovered_type(self, func_type: StringOrType) -> None:
if isinstance(func_type, str):
(func_type, _) = self.view.parse_type_string(func_type)
+ func_type = func_type.immutable_copy()
core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle)
def set_auto_indirect_branches(
@@ -2440,11 +2444,13 @@ class Function:
def set_auto_type(self, value: StringOrType) -> None:
if isinstance(value, str):
(value, _) = self.view.parse_type_string(value)
+ value = value.immutable_copy()
core.BNSetFunctionAutoType(self.handle, value.handle)
def set_user_type(self, value: StringOrType) -> None:
if isinstance(value, str):
(value, _) = self.view.parse_type_string(value)
+ value = value.immutable_copy()
core.BNSetFunctionUserType(self.handle, value.handle)
@property
@@ -2462,6 +2468,7 @@ class Function:
type_conf.type = value
type_conf.confidence = core.max_confidence
else:
+ value = value.immutable_copy()
type_conf.type = value.handle
type_conf.confidence = value.confidence
core.BNSetAutoFunctionReturnType(self.handle, type_conf)
@@ -2807,14 +2814,14 @@ class Function:
def create_auto_stack_var(self, offset: int, var_type: StringOrType, name: str) -> None:
if isinstance(var_type, str):
(var_type, _) = self.view.parse_type_string(var_type)
- tc = var_type._to_core_struct()
- core.BNCreateAutoStackVariable(self.handle, offset, tc, name)
+ tc = var_type.immutable_copy()
+ core.BNCreateAutoStackVariable(self.handle, offset, tc._to_core_struct(), name)
def create_user_stack_var(self, offset: int, var_type: StringOrType, name: str) -> None:
if isinstance(var_type, str):
(var_type, _) = self.view.parse_type_string(var_type)
- tc = var_type._to_core_struct()
- core.BNCreateUserStackVariable(self.handle, offset, tc, name)
+ tc = var_type.immutable_copy()
+ core.BNCreateUserStackVariable(self.handle, offset, tc._to_core_struct(), name)
def delete_auto_stack_var(self, offset: int) -> None:
core.BNDeleteAutoStackVariable(self.handle, offset)
@@ -2827,16 +2834,16 @@ class Function:
) -> None:
if isinstance(var_type, str):
(var_type, _) = self.view.parse_type_string(var_type)
- tc = var_type._to_core_struct()
- core.BNCreateAutoVariable(self.handle, var.to_BNVariable(), tc, name, ignore_disjoint_uses)
+ tc = var_type.immutable_copy()
+ core.BNCreateAutoVariable(self.handle, var.to_BNVariable(), tc._to_core_struct(), name, ignore_disjoint_uses)
def create_user_var(
self, var: 'variable.Variable', var_type: StringOrType, name: str, ignore_disjoint_uses: bool = False
) -> None:
if isinstance(var_type, str):
(var_type, _) = self.view.parse_type_string(var_type)
- tc = var_type._to_core_struct()
- core.BNCreateUserVariable(self.handle, var.to_BNVariable(), tc, name, ignore_disjoint_uses)
+ tc = var_type.immutable_copy()
+ core.BNCreateUserVariable(self.handle, var.to_BNVariable(), tc._to_core_struct(), name, ignore_disjoint_uses)
def delete_user_var(self, var: 'variable.Variable') -> None:
core.BNDeleteUserVariable(self.handle, var.to_BNVariable())
@@ -2955,6 +2962,7 @@ class Function:
else:
confidence = adjust_type.confidence
type_conf = core.BNTypeWithConfidence()
+ adjust_type = adjust_type.immutable_copy()
type_conf.type = adjust_type.handle
type_conf.confidence = confidence
else:
diff --git a/python/highlevelil.py b/python/highlevelil.py
index e16ab760..bb1f6129 100644
--- a/python/highlevelil.py
+++ b/python/highlevelil.py
@@ -4816,8 +4816,8 @@ class HighLevelILFunction:
"""
if isinstance(expr_type, str):
(expr_type, _) = self.view.parse_type_string(expr_type)
- tc = expr_type._to_core_struct()
- core.BNSetHighLevelILExprType(self.handle, expr_index, tc)
+ ic = expr_type.immutable_copy()
+ core.BNSetHighLevelILExprType(self.handle, expr_index, ic._to_core_struct())
class HighLevelILBasicBlock(basicblock.BasicBlock):
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index 6377996b..84ef1382 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -6350,7 +6350,8 @@ class MediumLevelILFunction:
if expr_type is not None:
if isinstance(expr_type, str):
(expr_type, _) = self.view.parse_type_string(expr_type)
- tc = expr_type._to_core_struct()
+ ic = expr_type.immutable_copy()
+ tc = ic._to_core_struct()
else:
tc = core.BNTypeWithConfidence()
tc.type = None
diff --git a/python/types.py b/python/types.py
index 44583c4d..0da212d3 100644
--- a/python/types.py
+++ b/python/types.py
@@ -435,9 +435,10 @@ class FunctionParameter:
location: Optional['variable.VariableNameAndType'] = None
def __repr__(self):
+ ic = self.type.immutable_copy()
if (self.location is not None) and (self.location.name != self.name):
- return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()} @ {self.location.name}"
- return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()}"
+ return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()} @ {self.location.name}"
+ return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()}"
def immutable_copy(self) -> 'FunctionParameter':
return FunctionParameter(self.type.immutable_copy(), self.name, self.location)
@@ -635,18 +636,8 @@ class TypeBuilder:
def __str__(self):
return str(self.immutable_copy())
- @property
- def handle(self) -> core.BNTypeHandle:
- return self.immutable_copy().handle
-
def __hash__(self):
- return hash(ctypes.addressof(self.handle.contents))
-
- def _to_core_struct(self) -> core.BNTypeWithConfidence:
- type_conf = core.BNTypeWithConfidence()
- type_conf.type = self.handle
- type_conf.confidence = self.confidence
- return type_conf
+ return hash(ctypes.addressof(self._handle.contents))
def immutable_copy(self):
Types = {
@@ -865,7 +856,8 @@ class TypeBuilder:
@child.setter
def child(self, value: SomeType) -> None:
- core.BNTypeBuilderSetChildType(self._handle, value.immutable_copy()._to_core_struct())
+ ic = value.immutable_copy()
+ core.BNTypeBuilderSetChildType(self._handle, ic._to_core_struct())
@property
def alternate_name(self) -> Optional[str]:
@@ -1011,7 +1003,8 @@ class PointerBuilder(TypeBuilder):
_const = BoolWithConfidence.get_core_struct(const)
_volatile = BoolWithConfidence.get_core_struct(volatile)
- handle = core.BNCreatePointerTypeBuilderOfWidth(_width, type._to_core_struct(), _const, _volatile, ref_type)
+ ic = type.immutable_copy()
+ handle = core.BNCreatePointerTypeBuilderOfWidth(_width, ic._to_core_struct(), _const, _volatile, ref_type)
assert handle is not None, "BNCreatePointerTypeBuilderOfWidth returned None"
return cls(handle, platform, confidence)
@@ -1132,7 +1125,8 @@ class ArrayBuilder(TypeBuilder):
cls, type: SomeType, element_count: int, platform: Optional['_platform.Platform'] = None,
confidence: int = core.max_confidence
) -> 'ArrayBuilder':
- handle = core.BNCreateArrayTypeBuilder(type._to_core_struct(), element_count)
+ ic = type.immutable_copy()
+ handle = core.BNCreateArrayTypeBuilder(ic._to_core_struct(), element_count)
assert handle is not None, "BNCreateArrayTypeBuilder returned None"
return cls(handle, platform, confidence)
@@ -1160,11 +1154,11 @@ class FunctionBuilder(TypeBuilder):
name_type: 'NameType' = NameType.NoNameType,
pure: Optional[BoolWithConfidence] = None
) -> 'FunctionBuilder':
- param_buf = FunctionBuilder._to_core_struct(params)
+ param_buf, type_list = FunctionBuilder._to_core_struct(params)
if return_type is None:
- ret_conf = Type.void()._to_core_struct()
+ ret_conf = Type.void()
else:
- ret_conf = return_type._to_core_struct()
+ ret_conf = return_type.immutable_copy()
conv_conf = core.BNCallingConventionWithConfidence()
if calling_convention is None:
@@ -1218,7 +1212,7 @@ class FunctionBuilder(TypeBuilder):
if params is None:
params = []
handle = core.BNCreateFunctionTypeBuilder(
- ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf,
+ ret_conf._to_core_struct(), conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf,
reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust),
return_regs_set, name_type, pure_conf
)
@@ -1315,10 +1309,18 @@ class FunctionBuilder(TypeBuilder):
def _to_core_struct(params: Optional[ParamsType] = None):
if params is None:
params = []
+
+ # type_list is very important as we need to keep a reference to the intermediate type
+ # objects as we're getting their handles if they go out of scope while we're holding
+ # their handles we get a UAF. This is only necessary as we're inside a helper that
+ # has to deal with raw type objects
+ type_list = []
param_buf = (core.BNFunctionParameter * len(params))()
for i, param in enumerate(params):
core_param = param_buf[i]
if isinstance(param, (Type, TypeBuilder)):
+ param = param.immutable_copy()
+ type_list.append(param)
assert param.handle is not None, "Attempting to construct function parameter without properly constructed type"
core_param.name = ""
core_param.type = param.handle
@@ -1326,9 +1328,11 @@ class FunctionBuilder(TypeBuilder):
core_param.defaultLocation = True
elif isinstance(param, FunctionParameter):
assert param.type is not None, "Attempting to construct function parameter without properly constructed type"
+ param_type = param.type.immutable_copy()
+ type_list.append(param_type)
core_param.name = param.name
- core_param.type = param.type.handle
- core_param.typeConfidence = param.type.confidence
+ core_param.type = param_type.handle
+ core_param.typeConfidence = param_type.confidence
if param.location is None:
core_param.defaultLocation = True
else:
@@ -1340,17 +1344,20 @@ class FunctionBuilder(TypeBuilder):
name, _type = param
if not isinstance(name, str) or not isinstance(_type, (Type, TypeBuilder)):
raise ValueError(f"Conversion from unsupported function parameter type {type(param)}")
+ _type = _type.immutable_copy()
+ type_list.append(_type)
core_param.name = name
core_param.type = _type.handle
core_param.typeConfidence = _type.confidence
core_param.defaultLocation = True
else:
raise ValueError(f"Conversion from unsupported function parameter type {type(param)}")
- return param_buf
+ return param_buf, type_list
@parameters.setter
def parameters(self, params: List[FunctionParameter]) -> None:
- core.BNSetFunctionTypeBuilderParameters(self._handle, FunctionBuilder._to_core_struct(params), len(params))
+ ic, type_list = FunctionBuilder._to_core_struct(params)
+ core.BNSetFunctionTypeBuilderParameters(self._handle, ic, len(params))
@property
def children(self) -> List[TypeBuilder]:
@@ -1471,17 +1478,20 @@ class StructureBuilder(TypeBuilder):
for member in members:
if isinstance(member, Tuple):
_type, _name = member
+ ic = _type.immutable_copy()
core.BNAddStructureBuilderMember(
- structure_builder_handle, _type._to_core_struct(), _name, MemberAccess.NoAccess, MemberScope.NoScope
+ structure_builder_handle, ic._to_core_struct(), _name, MemberAccess.NoAccess, MemberScope.NoScope
)
elif isinstance(member, StructureMember):
+ ic = member.type.immutable_copy()
core.BNAddStructureBuilderMemberAtOffset(
- structure_builder_handle, member.type._to_core_struct(), member.name, member.offset, False,
+ structure_builder_handle, ic._to_core_struct(), member.name, member.offset, False,
member.access, member.scope, member.bit_position, member.bit_width
)
elif isinstance(member, (TypeBuilder, Type)):
+ ic = member.immutable_copy()
core.BNAddStructureBuilderMember(
- structure_builder_handle, member._to_core_struct(), "", MemberAccess.NoAccess, MemberScope.NoScope
+ structure_builder_handle, ic._to_core_struct(), "", MemberAccess.NoAccess, MemberScope.NoScope
)
else:
raise ValueError(f"Structure member type {member} not supported")
@@ -1644,8 +1654,9 @@ class StructureBuilder(TypeBuilder):
return None
def replace(self, index: int, type: SomeType, name: str = "", overwrite_existing: bool = True):
+ ic = type.immutable_copy()
core.BNReplaceStructureBuilderMember(
- self.builder_handle, index, type._to_core_struct(), name, overwrite_existing
+ self.builder_handle, index, ic._to_core_struct(), name, overwrite_existing
)
def remove(self, index: int):
@@ -1655,8 +1666,9 @@ class StructureBuilder(TypeBuilder):
self, offset: int, type: SomeType, name: str = "", overwrite_existing: bool = True,
access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope, bit_position: int = 0, bit_width: int = 0
):
+ ic = type.immutable_copy()
core.BNAddStructureBuilderMemberAtOffset(
- self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
+ self.builder_handle, ic._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
bit_width
)
@@ -1665,7 +1677,8 @@ class StructureBuilder(TypeBuilder):
scope: MemberScope = MemberScope.NoScope
) -> 'StructureBuilder':
# appends a member at the end of the structure growing the structure
- core.BNAddStructureBuilderMember(self.builder_handle, type._to_core_struct(), name, access, scope)
+ ic = type.immutable_copy()
+ core.BNAddStructureBuilderMember(self.builder_handle, ic._to_core_struct(), name, access, scope)
return self
def add_member_at_offset(
@@ -1673,8 +1686,9 @@ class StructureBuilder(TypeBuilder):
access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope, bit_position: int = 0, bit_width: int = 0
) -> 'StructureBuilder':
# Adds structure member to the given offset optionally clearing any members within the range offset-offset+len(type)
+ ic = type.immutable_copy()
core.BNAddStructureBuilderMemberAtOffset(
- self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
+ self.builder_handle, ic._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
bit_width
)
return self
@@ -3116,7 +3130,7 @@ class FunctionType(Type):
ret = VoidType.create()
if params is None:
params = []
- param_buf = FunctionBuilder._to_core_struct(params)
+ param_buf, type_list = FunctionBuilder._to_core_struct(params)
ret_conf = ret._to_core_struct()
conv_conf = core.BNCallingConventionWithConfidence()
if calling_convention is None: