Skip to content

Commit 67604e1

Browse files
committed
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())
1 parent 59fa6ae commit 67604e1

File tree

5 files changed

+77
-48
lines changed

5 files changed

+77
-48
lines changed

python/binaryview.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5286,8 +5286,8 @@ def define_data_var(
52865286

52875287
if isinstance(var_type, str):
52885288
(var_type, _) = self.parse_type_string(var_type)
5289-
tc = var_type._to_core_struct()
5290-
core.BNDefineDataVariable(self.handle, addr, tc)
5289+
tc = var_type.immutable_copy()
5290+
core.BNDefineDataVariable(self.handle, addr, tc._to_core_struct())
52915291

52925292
if name is not None:
52935293
if isinstance(name, str):
@@ -5321,8 +5321,11 @@ def define_user_data_var(
53215321

53225322
if isinstance(var_type, str):
53235323
(var_type, _) = self.parse_type_string(var_type)
5324-
tc = var_type._to_core_struct()
5325-
core.BNDefineUserDataVariable(self.handle, addr, tc)
5324+
5325+
# this var_type temporary is essential! It holds the reference to the immutable type
5326+
# until after BNDefineUserDataVariable takes the reference.
5327+
var_type = var_type.immutable_copy()
5328+
core.BNDefineUserDataVariable(self.handle, addr, var_type._to_core_struct())
53265329

53275330
if name is not None:
53285331
if isinstance(name, str):
@@ -8377,6 +8380,7 @@ def define_type(
83778380
default_name = new_name
83788381
assert default_name is not None, "default_name can only be None if named type is derived from string passed to type_obj"
83798382
name = _types.QualifiedName(default_name)._to_core_struct()
8383+
type_obj = type_obj.immutable_copy()
83808384
reg_name = core.BNDefineAnalysisType(self.handle, type_id, name, type_obj.handle)
83818385
result = _types.QualifiedName._from_core_struct(reg_name)
83828386
core.BNFreeQualifiedName(reg_name)
@@ -8407,6 +8411,7 @@ def define_user_type(self, name: Optional['_types.QualifiedNameType'], type_obj:
84078411
if name is None:
84088412
raise ValueError("name can only be None if named type is derived from string passed to type_obj")
84098413
_name = _types.QualifiedName(name)._to_core_struct()
8414+
type_obj = type_obj.immutable_copy()
84108415
core.BNDefineUserAnalysisType(self.handle, _name, type_obj.handle)
84118416

84128417
def define_types(self, types: List[Tuple[str, Optional['_types.QualifiedNameType'], StringOrType]], progress_func: Optional[ProgressFuncType]) -> Mapping[str, '_types.QualifiedName']:
@@ -8685,7 +8690,8 @@ def export_type_to_library(self, lib: typelibrary.TypeLibrary, name: Optional[st
86858690
(type_obj, new_name) = self.parse_type_string(type_obj)
86868691
if name is None:
86878692
_name = new_name
8688-
if not isinstance(type_obj, (_types.Type, _types.TypeBuilder)):
8693+
type_obj = type_obj.immutable_copy()
8694+
if not isinstance(type_obj, _types.Type):
86898695
raise TypeError("type_obj must be a Type object")
86908696
if _name is None:
86918697
raise ValueError("name can only be None if named type is derived from string passed to type_obj")
@@ -8719,7 +8725,8 @@ def export_object_to_library(
87198725
(type_obj, new_name) = self.parse_type_string(type_obj)
87208726
if name is None:
87218727
_name = new_name
8722-
if not isinstance(type_obj, (_types.Type, _types.TypeBuilder)):
8728+
type_obj = type_obj.immutable_copy()
8729+
if not isinstance(type_obj, _types.Type):
87238730
raise TypeError("type_obj must be a Type object")
87248731
if _name is None:
87258732
raise ValueError("name can only be None if named type is derived from string passed to type_obj")

python/function.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,7 @@ def return_type(self, value: Optional[StringOrType]) -> None: # type: ignore
13591359
type_conf.type = value.handle
13601360
type_conf.confidence = core.max_confidence
13611361
else:
1362+
value = value.immutable_copy()
13621363
type_conf.type = value.handle
13631364
type_conf.confidence = value.confidence
13641365
core.BNSetUserFunctionReturnType(self.handle, type_conf)
@@ -2270,11 +2271,14 @@ def create_graph_immediate(
22702271
def apply_imported_types(self, sym: 'types.CoreSymbol', type: Optional[StringOrType] = None) -> None:
22712272
if isinstance(type, str):
22722273
(type, _) = self.view.parse_type_string(type)
2274+
if type is not None:
2275+
type = type.immutable_copy()
22732276
core.BNApplyImportedTypes(self.handle, sym.handle, None if type is None else type.handle)
22742277

22752278
def apply_auto_discovered_type(self, func_type: StringOrType) -> None:
22762279
if isinstance(func_type, str):
22772280
(func_type, _) = self.view.parse_type_string(func_type)
2281+
func_type = func_type.immutable_copy()
22782282
core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle)
22792283

22802284
def set_auto_indirect_branches(
@@ -2440,11 +2444,13 @@ def get_block_annotations(self, addr: int,
24402444
def set_auto_type(self, value: StringOrType) -> None:
24412445
if isinstance(value, str):
24422446
(value, _) = self.view.parse_type_string(value)
2447+
value = value.immutable_copy()
24432448
core.BNSetFunctionAutoType(self.handle, value.handle)
24442449

24452450
def set_user_type(self, value: StringOrType) -> None:
24462451
if isinstance(value, str):
24472452
(value, _) = self.view.parse_type_string(value)
2453+
value = value.immutable_copy()
24482454
core.BNSetFunctionUserType(self.handle, value.handle)
24492455

24502456
@property
@@ -2462,6 +2468,7 @@ def set_auto_return_type(self, value: StringOrType) -> None:
24622468
type_conf.type = value
24632469
type_conf.confidence = core.max_confidence
24642470
else:
2471+
value = value.immutable_copy()
24652472
type_conf.type = value.handle
24662473
type_conf.confidence = value.confidence
24672474
core.BNSetAutoFunctionReturnType(self.handle, type_conf)
@@ -2807,14 +2814,14 @@ def set_user_instr_highlight(
28072814
def create_auto_stack_var(self, offset: int, var_type: StringOrType, name: str) -> None:
28082815
if isinstance(var_type, str):
28092816
(var_type, _) = self.view.parse_type_string(var_type)
2810-
tc = var_type._to_core_struct()
2811-
core.BNCreateAutoStackVariable(self.handle, offset, tc, name)
2817+
tc = var_type.immutable_copy()
2818+
core.BNCreateAutoStackVariable(self.handle, offset, tc._to_core_struct(), name)
28122819

28132820
def create_user_stack_var(self, offset: int, var_type: StringOrType, name: str) -> None:
28142821
if isinstance(var_type, str):
28152822
(var_type, _) = self.view.parse_type_string(var_type)
2816-
tc = var_type._to_core_struct()
2817-
core.BNCreateUserStackVariable(self.handle, offset, tc, name)
2823+
tc = var_type.immutable_copy()
2824+
core.BNCreateUserStackVariable(self.handle, offset, tc._to_core_struct(), name)
28182825

28192826
def delete_auto_stack_var(self, offset: int) -> None:
28202827
core.BNDeleteAutoStackVariable(self.handle, offset)
@@ -2827,16 +2834,16 @@ def create_auto_var(
28272834
) -> None:
28282835
if isinstance(var_type, str):
28292836
(var_type, _) = self.view.parse_type_string(var_type)
2830-
tc = var_type._to_core_struct()
2831-
core.BNCreateAutoVariable(self.handle, var.to_BNVariable(), tc, name, ignore_disjoint_uses)
2837+
tc = var_type.immutable_copy()
2838+
core.BNCreateAutoVariable(self.handle, var.to_BNVariable(), tc._to_core_struct(), name, ignore_disjoint_uses)
28322839

28332840
def create_user_var(
28342841
self, var: 'variable.Variable', var_type: StringOrType, name: str, ignore_disjoint_uses: bool = False
28352842
) -> None:
28362843
if isinstance(var_type, str):
28372844
(var_type, _) = self.view.parse_type_string(var_type)
2838-
tc = var_type._to_core_struct()
2839-
core.BNCreateUserVariable(self.handle, var.to_BNVariable(), tc, name, ignore_disjoint_uses)
2845+
tc = var_type.immutable_copy()
2846+
core.BNCreateUserVariable(self.handle, var.to_BNVariable(), tc._to_core_struct(), name, ignore_disjoint_uses)
28402847

28412848
def delete_user_var(self, var: 'variable.Variable') -> None:
28422849
core.BNDeleteUserVariable(self.handle, var.to_BNVariable())
@@ -2955,6 +2962,7 @@ def set_call_type_adjustment(
29552962
else:
29562963
confidence = adjust_type.confidence
29572964
type_conf = core.BNTypeWithConfidence()
2965+
adjust_type = adjust_type.immutable_copy()
29582966
type_conf.type = adjust_type.handle
29592967
type_conf.confidence = confidence
29602968
else:

python/highlevelil.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4816,8 +4816,8 @@ def set_expr_type(self, expr_index: int, expr_type: StringOrType) -> None:
48164816
"""
48174817
if isinstance(expr_type, str):
48184818
(expr_type, _) = self.view.parse_type_string(expr_type)
4819-
tc = expr_type._to_core_struct()
4820-
core.BNSetHighLevelILExprType(self.handle, expr_index, tc)
4819+
ic = expr_type.immutable_copy()
4820+
core.BNSetHighLevelILExprType(self.handle, expr_index, ic._to_core_struct())
48214821

48224822

48234823
class HighLevelILBasicBlock(basicblock.BasicBlock):

python/mediumlevelil.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6311,7 +6311,8 @@ def set_expr_type(self, expr_index: int, expr_type: Optional[StringOrType]) -> N
63116311
if expr_type is not None:
63126312
if isinstance(expr_type, str):
63136313
(expr_type, _) = self.view.parse_type_string(expr_type)
6314-
tc = expr_type._to_core_struct()
6314+
ic = expr_type.immutable_copy()
6315+
tc = ic._to_core_struct()
63156316
else:
63166317
tc = core.BNTypeWithConfidence()
63176318
tc.type = None

python/types.py

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,10 @@ class FunctionParameter:
435435
location: Optional['variable.VariableNameAndType'] = None
436436

437437
def __repr__(self):
438+
ic = self.type.immutable_copy()
438439
if (self.location is not None) and (self.location.name != self.name):
439-
return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()} @ {self.location.name}"
440-
return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()}"
440+
return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()} @ {self.location.name}"
441+
return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()}"
441442

442443
def immutable_copy(self) -> 'FunctionParameter':
443444
return FunctionParameter(self.type.immutable_copy(), self.name, self.location)
@@ -602,18 +603,8 @@ def __repr__(self):
602603
def __str__(self):
603604
return str(self.immutable_copy())
604605

605-
@property
606-
def handle(self) -> core.BNTypeHandle:
607-
return self.immutable_copy().handle
608-
609606
def __hash__(self):
610-
return hash(ctypes.addressof(self.handle.contents))
611-
612-
def _to_core_struct(self) -> core.BNTypeWithConfidence:
613-
type_conf = core.BNTypeWithConfidence()
614-
type_conf.type = self.handle
615-
type_conf.confidence = self.confidence
616-
return type_conf
607+
return hash(ctypes.addressof(self._handle.contents))
617608

618609
def immutable_copy(self):
619610
Types = {
@@ -832,7 +823,8 @@ def child(self) -> 'Type':
832823

833824
@child.setter
834825
def child(self, value: SomeType) -> None:
835-
core.BNTypeBuilderSetChildType(self._handle, value.immutable_copy()._to_core_struct())
826+
ic = value.immutable_copy()
827+
core.BNTypeBuilderSetChildType(self._handle, ic._to_core_struct())
836828

837829
@property
838830
def alternate_name(self) -> Optional[str]:
@@ -978,7 +970,8 @@ def create(
978970

979971
_const = BoolWithConfidence.get_core_struct(const)
980972
_volatile = BoolWithConfidence.get_core_struct(volatile)
981-
handle = core.BNCreatePointerTypeBuilderOfWidth(_width, type._to_core_struct(), _const, _volatile, ref_type)
973+
ic = type.immutable_copy()
974+
handle = core.BNCreatePointerTypeBuilderOfWidth(_width, ic._to_core_struct(), _const, _volatile, ref_type)
982975
assert handle is not None, "BNCreatePointerTypeBuilderOfWidth returned None"
983976
return cls(handle, platform, confidence)
984977

@@ -1099,7 +1092,8 @@ def create(
10991092
cls, type: SomeType, element_count: int, platform: Optional['_platform.Platform'] = None,
11001093
confidence: int = core.max_confidence
11011094
) -> 'ArrayBuilder':
1102-
handle = core.BNCreateArrayTypeBuilder(type._to_core_struct(), element_count)
1095+
ic = type.immutable_copy()
1096+
handle = core.BNCreateArrayTypeBuilder(ic._to_core_struct(), element_count)
11031097
assert handle is not None, "BNCreateArrayTypeBuilder returned None"
11041098
return cls(handle, platform, confidence)
11051099

@@ -1127,11 +1121,11 @@ def create(
11271121
name_type: 'NameType' = NameType.NoNameType,
11281122
pure: Optional[BoolWithConfidence] = None
11291123
) -> 'FunctionBuilder':
1130-
param_buf = FunctionBuilder._to_core_struct(params)
1124+
param_buf, type_list = FunctionBuilder._to_core_struct(params)
11311125
if return_type is None:
1132-
ret_conf = Type.void()._to_core_struct()
1126+
ret_conf = Type.void()
11331127
else:
1134-
ret_conf = return_type._to_core_struct()
1128+
ret_conf = return_type.immutable_copy()
11351129

11361130
conv_conf = core.BNCallingConventionWithConfidence()
11371131
if calling_convention is None:
@@ -1185,7 +1179,7 @@ def create(
11851179
if params is None:
11861180
params = []
11871181
handle = core.BNCreateFunctionTypeBuilder(
1188-
ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf,
1182+
ret_conf._to_core_struct(), conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf,
11891183
reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust),
11901184
return_regs_set, name_type, pure_conf
11911185
)
@@ -1282,20 +1276,30 @@ def variable_arguments(self) -> BoolWithConfidence:
12821276
def _to_core_struct(params: Optional[ParamsType] = None):
12831277
if params is None:
12841278
params = []
1279+
1280+
# type_list is very important as we need to keep a reference to the intermediate type
1281+
# objects as we're getting their handles if they go out of scope while we're holding
1282+
# their handles we get a UAF. This is only necessary as we're inside a helper that
1283+
# has to deal with raw type objects
1284+
type_list = []
12851285
param_buf = (core.BNFunctionParameter * len(params))()
12861286
for i, param in enumerate(params):
12871287
core_param = param_buf[i]
12881288
if isinstance(param, (Type, TypeBuilder)):
1289+
param = param.immutable_copy()
1290+
type_list.append(param)
12891291
assert param.handle is not None, "Attempting to construct function parameter without properly constructed type"
12901292
core_param.name = ""
12911293
core_param.type = param.handle
12921294
core_param.typeConfidence = param.confidence
12931295
core_param.defaultLocation = True
12941296
elif isinstance(param, FunctionParameter):
12951297
assert param.type is not None, "Attempting to construct function parameter without properly constructed type"
1298+
param_type = param.type.immutable_copy()
1299+
type_list.append(param_type)
12961300
core_param.name = param.name
1297-
core_param.type = param.type.handle
1298-
core_param.typeConfidence = param.type.confidence
1301+
core_param.type = param_type.handle
1302+
core_param.typeConfidence = param_type.confidence
12991303
if param.location is None:
13001304
core_param.defaultLocation = True
13011305
else:
@@ -1307,13 +1311,15 @@ def _to_core_struct(params: Optional[ParamsType] = None):
13071311
name, _type = param
13081312
if not isinstance(name, str) or not isinstance(_type, (Type, TypeBuilder)):
13091313
raise ValueError(f"Conversion from unsupported function parameter type {type(param)}")
1314+
_type = _type.immutable_copy()
1315+
type_list.append(_type)
13101316
core_param.name = name
13111317
core_param.type = _type.handle
13121318
core_param.typeConfidence = _type.confidence
13131319
core_param.defaultLocation = True
13141320
else:
13151321
raise ValueError(f"Conversion from unsupported function parameter type {type(param)}")
1316-
return param_buf
1322+
return param_buf, type_list
13171323

13181324
@parameters.setter
13191325
def parameters(self, params: List[FunctionParameter]) -> None:
@@ -1438,17 +1444,20 @@ def _add_members_to_builder(structure_builder_handle, members: Optional[MembersT
14381444
for member in members:
14391445
if isinstance(member, Tuple):
14401446
_type, _name = member
1447+
ic = _type.immutable_copy()
14411448
core.BNAddStructureBuilderMember(
1442-
structure_builder_handle, _type._to_core_struct(), _name, MemberAccess.NoAccess, MemberScope.NoScope
1449+
structure_builder_handle, ic._to_core_struct(), _name, MemberAccess.NoAccess, MemberScope.NoScope
14431450
)
14441451
elif isinstance(member, StructureMember):
1452+
ic = member.type.immutable_copy()
14451453
core.BNAddStructureBuilderMemberAtOffset(
1446-
structure_builder_handle, member.type._to_core_struct(), member.name, member.offset, False,
1454+
structure_builder_handle, ic._to_core_struct(), member.name, member.offset, False,
14471455
member.access, member.scope, member.bit_position, member.bit_width
14481456
)
14491457
elif isinstance(member, (TypeBuilder, Type)):
1458+
ic = member.immutable_copy()
14501459
core.BNAddStructureBuilderMember(
1451-
structure_builder_handle, member._to_core_struct(), "", MemberAccess.NoAccess, MemberScope.NoScope
1460+
structure_builder_handle, ic._to_core_struct(), "", MemberAccess.NoAccess, MemberScope.NoScope
14521461
)
14531462
else:
14541463
raise ValueError(f"Structure member type {member} not supported")
@@ -1611,8 +1620,9 @@ def index_by_offset(self, offset: MemberOffset) -> Optional[MemberIndex]:
16111620
return None
16121621

16131622
def replace(self, index: int, type: SomeType, name: str = "", overwrite_existing: bool = True):
1623+
ic = type.immutable_copy()
16141624
core.BNReplaceStructureBuilderMember(
1615-
self.builder_handle, index, type._to_core_struct(), name, overwrite_existing
1625+
self.builder_handle, index, ic._to_core_struct(), name, overwrite_existing
16161626
)
16171627

16181628
def remove(self, index: int):
@@ -1622,8 +1632,9 @@ def insert(
16221632
self, offset: int, type: SomeType, name: str = "", overwrite_existing: bool = True,
16231633
access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope, bit_position: int = 0, bit_width: int = 0
16241634
):
1635+
ic = type.immutable_copy()
16251636
core.BNAddStructureBuilderMemberAtOffset(
1626-
self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
1637+
self.builder_handle, ic._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
16271638
bit_width
16281639
)
16291640

@@ -1632,16 +1643,18 @@ def append(
16321643
scope: MemberScope = MemberScope.NoScope
16331644
) -> 'StructureBuilder':
16341645
# appends a member at the end of the structure growing the structure
1635-
core.BNAddStructureBuilderMember(self.builder_handle, type._to_core_struct(), name, access, scope)
1646+
ic = type.immutable_copy()
1647+
core.BNAddStructureBuilderMember(self.builder_handle, ic._to_core_struct(), name, access, scope)
16361648
return self
16371649

16381650
def add_member_at_offset(
16391651
self, name: MemberName, type: SomeType, offset: MemberOffset, overwrite_existing: bool = True,
16401652
access: MemberAccess = MemberAccess.NoAccess, scope: MemberScope = MemberScope.NoScope, bit_position: int = 0, bit_width: int = 0
16411653
) -> 'StructureBuilder':
16421654
# Adds structure member to the given offset optionally clearing any members within the range offset-offset+len(type)
1655+
ic = type.immutable_copy()
16431656
core.BNAddStructureBuilderMemberAtOffset(
1644-
self.builder_handle, type._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
1657+
self.builder_handle, ic._to_core_struct(), name, offset, overwrite_existing, access, scope, bit_position,
16451658
bit_width
16461659
)
16471660
return self
@@ -3083,7 +3096,7 @@ def create(
30833096
ret = VoidType.create()
30843097
if params is None:
30853098
params = []
3086-
param_buf = FunctionBuilder._to_core_struct(params)
3099+
param_buf, type_list = FunctionBuilder._to_core_struct(params)
30873100
ret_conf = ret._to_core_struct()
30883101
conv_conf = core.BNCallingConventionWithConfidence()
30893102
if calling_convention is None:

0 commit comments

Comments
 (0)