Skip to content

Commit 09c33fc

Browse files
committed
[ranges] Add wrap-around support for infer-isolated, improve disjoint ranges #3
1 parent b2649b1 commit 09c33fc

File tree

46 files changed

+1029
-257
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1029
-257
lines changed

analyzer/analysis/rangeAnalysis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def get_ast_ranges(constraints, ast : claripy.BV):
4242

4343
# We calculate the min and max once
4444
s = claripy.Solver(timeout=global_config["Z3Timeout"])
45-
s.constraints = constraints
45+
s.constraints = constraints.copy()
4646
ast_min = s.min(ast)
4747
ast_max = s.max(ast)
4848

analyzer/analysis/range_strategies/find_constraints_bounds.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,45 @@ def find_range(self, constraints, ast : claripy.ast.bv.BVS,
5050
if ast_min == ast_max:
5151
return self.infer_isolated_strategy.find_range([], ast, ast_min, ast_max)
5252

53-
# print(f"min:{ast_min} max:{ast_max}")
53+
5454
try:
5555
sat_ranges = _find_sat_distribution(constraints, ast, ast_min, ast_max, stride=1)
5656
except claripy.ClaripyZ3Error as e:
5757
# timeout
5858
return None
5959

60+
# --------- One non-satisfiable range
61+
6062
if sat_ranges != None and len(sat_ranges) == 1:
61-
# It is a full range, we can treat it as isolated
62-
return self.infer_isolated_strategy.find_range([], ast, sat_ranges[0][0], sat_ranges[0][1])
63+
# We have one non-satisfiable range, so we can try to treat it as
64+
# isolated
65+
66+
r = self.infer_isolated_strategy.find_range([], ast, sat_ranges[0][0], sat_ranges[0][1])
67+
68+
if sat_ranges[0][0] > sat_ranges[0][1]:
69+
# The range wraps around, we have to be sure that the AST range is
70+
# a simple strided range, otherwise we get two separate disjoint ranges
71+
# which we cannot describe in our range (e.g., [ast != 0xf, ast <= 0xffff])
72+
if r.and_mask != None or r.or_mask != None or \
73+
ast_max != ((1 << ast.size()) - 1 - (r.stride - 1)) or ast_min != 0:
74+
75+
# We have a complex range thus fail (e.g., masking is performed)
76+
return None
77+
78+
return r
6379

6480
# --------- Signed range
6581
if ast_min == 0 and ast_max == (2**ast.size()) - 1:
6682
s = claripy.Solver(timeout=global_config["Z3Timeout"])
6783
new_min = (1 << (ast.size() - 1))
68-
s.constraints = constraints + [(ast > new_min)]
84+
s.constraints = constraints + [(ast >= new_min)]
6985
upper_ast_min = s.min(ast)
7086
upper_ast_max = s.max(ast)
7187
s = claripy.Solver(timeout=global_config["Z3Timeout"])
72-
s.constraints = constraints + [ast <= new_min]
88+
s.constraints = constraints + [ast < new_min]
7389
lower_ast_min = s.min(ast)
7490
lower_ast_max = s.max(ast)
7591

76-
# print(f" new_min:{hex(new_min)} upper_min: {hex(upper_ast_min)} upper_max: {hex(upper_ast_max)} lower_min: {hex(lower_ast_min)} lower_max: {hex(lower_ast_max)}")
7792

7893
if lower_ast_min == 0 and upper_ast_max == (2**ast.size()) - 1:
7994
# treat this as a single range that wraps around ( min > max )
@@ -124,8 +139,7 @@ def _find_sat_distribution(constraints, ast, start, end, stride = 1):
124139
return [(start, end)]
125140

126141
# Range with a "hole"
127-
# print(f"Range: {[(value+1, value-1)]}")
128-
return [value+1, value-1]
142+
return [(value+1, value-1)]
129143

130144
# TODO: Double check the validity of the code below
131145
return None

analyzer/analysis/range_strategies/find_masking.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def find_range(self, constraints, ast : claripy.ast.bv.BVS,
2424

2525
entropy, and_mask, or_mask = _find_entropy(s, ast, ast_max)
2626

27-
return range_complex(ast_min, ast_max, False, entropy, and_mask, or_mask)
27+
return range_complex(ast_min, ast_max, ast.size(), False, entropy, and_mask, or_mask)
2828

2929
def _find_entropy(s : claripy.Solver, ast : claripy.BV, ast_max : int):
3030

analyzer/analysis/range_strategies/infer_isolated.py

+47-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ def find_range(self, constraints, ast : claripy.ast.bv.BVS,
2020
ast_min : int = None, ast_max : int = None):
2121

2222
# We only support isolated ranges
23-
2423
if constraints:
2524
return None
2625

@@ -34,34 +33,73 @@ def find_range(self, constraints, ast : claripy.ast.bv.BVS,
3433
ast_max = s.max(ast)
3534

3635
if ast.depth == 1:
37-
return range_simple(ast_min, ast_max, 1, True)
36+
return range_simple(ast_min, ast_max, ast.size(), 1, True)
3837

3938
range_map = get_range_map_from_ast(ast)
4039

4140
if range_map.unknown:
42-
return None
41+
42+
# We try an extra optimization: separating the concrete value
43+
# from addition. This covers cases like:
44+
# 0xffffffff81000000 + <BV32 AST >
45+
# get_range_map_from_ast() cannot handle 'overflows', so we
46+
# split the AST and add the concrete value manually to the range.
47+
if ast.op == '__add__' and any(not arg.symbolic for arg in ast.args):
48+
49+
concrete_value = next(arg for arg in ast.args if not arg.symbolic).args[0]
50+
sub_ast = sum([arg for arg in ast.args if arg.symbolic])
51+
52+
range_map = get_range_map_from_ast(sub_ast)
53+
54+
if range_map.unknown:
55+
return None
56+
57+
range_map = range_map.switch_to_stride_mode()
58+
59+
if range_map.unknown:
60+
return None
61+
62+
s = claripy.Solver(timeout=global_config["Z3Timeout"])
63+
sub_ast_min = s.min(sub_ast)
64+
sub_ast_max = s.max(sub_ast)
65+
66+
isolated_ast_min = sub_ast_min + concrete_value
67+
isolated_ast_max = sub_ast_max + concrete_value
68+
69+
# handle overflows
70+
isolated_ast_min &= (1 << ast.size()) - 1
71+
isolated_ast_max &= (1 << ast.size()) - 1
72+
73+
if isolated_ast_min - isolated_ast_max == range_map.stride:
74+
isolated_ast_min = s.min(ast)
75+
isolated_ast_max = s.max(ast)
76+
77+
# incorporate non-isolated min and max, only adjust if they are
78+
# tighter
79+
# Note: Conditions hold for both normal and disjoint ranges
80+
ast_min = ast_min if isolated_ast_min < ast_min else isolated_ast_min
81+
ast_max = ast_max if isolated_ast_max > ast_max else isolated_ast_max
82+
83+
else:
84+
return None
4385

4486

4587
if range_map.stride_mode:
46-
return range_simple(ast_min, ast_max, range_map.stride, isolated=True)
88+
return range_simple(ast_min, ast_max, ast.size(), range_map.stride, isolated=True)
4789

4890
else:
49-
return range_complex(ast_min, ast_max, True, None, range_map.and_mask, range_map.or_mask, True)
91+
return range_complex(ast_min, ast_max, ast.size(), True, None, range_map.and_mask, range_map.or_mask, True)
5092

5193

5294
def is_linear_mask(and_mask, or_mask):
5395

5496
mask = and_mask & ~or_mask
5597

56-
# print(bin(mask))
57-
5898
highest_bit = mask.bit_length()
5999
lowest_bit = (mask & -mask).bit_length() - 1
60100

61101
stride_mask = (2 ** highest_bit - 1) & ~(2 ** lowest_bit - 1)
62102

63-
# print(bin(stride_mask))
64-
65103
return mask == stride_mask
66104

67105
@dataclass

analyzer/analysis/range_strategies/small_set.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def _list_to_stride_range(numbers : list):
1616
if numbers[i] + stride != numbers[i+1]:
1717
return None
1818

19-
return AstRange(min=numbers[0] , max=numbers[-1],
19+
return AstRange(min=numbers[0] , max=numbers[-1], ast_size=0,
2020
exact=True, isolated=True,
2121
intervals=[Interval(numbers[0], numbers[-1], stride)])
2222

analyzer/shared/ranges.py

+22-13
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,18 @@ def stride(self):
4949
# TODO: Handle multiple intervals
5050
None
5151

52-
def __init__(self, min, max, exact, entropy=None, isolated=False, and_mask=None, or_mask=None, values=[], intervals=[]):
52+
def __init__(self, min, max, ast_size, exact, entropy=None, isolated=False, and_mask=None, or_mask=None, values=[], intervals=[]):
5353
self.min = min
5454
self.max = max
55-
self.window = max - min
55+
self.__ast_size = ast_size
56+
57+
if min <= max:
58+
self.window = max - min
59+
else:
60+
assert(ast_size > 0)
61+
# wrap around range
62+
# window = 0:max + min:ast_size
63+
self.window = ((1 << ast_size) - 1) - (min - 1) + max
5664

5765
self.entropy = entropy
5866
self.isolated = isolated
@@ -102,10 +110,11 @@ def to_string(self):
102110
return ",".join([hex(i) for i in self.values]) + f" Exact: {self.exact}"
103111

104112
def copy(self):
105-
return AstRange(min=self.min, max=self.max, exact=self.exact,
106-
entropy=self.entropy, isolated=self.isolated,
107-
and_mask=self.and_mask, or_mask=self.or_mask,
108-
values=self.values, intervals=self.intervals)
113+
return AstRange(min=self.min, max=self.max, ast_size=self.__ast_size,
114+
exact=self.exact, entropy=self.entropy,
115+
isolated=self.isolated, and_mask=self.and_mask,
116+
or_mask=self.or_mask, values=self.values,
117+
intervals=self.intervals)
109118

110119
def __str__(self):
111120

@@ -118,11 +127,11 @@ def __repr__(self):
118127
def range_static(value, isolated):
119128
interval = Interval(min=value, max=value+1, stride=1)
120129

121-
return AstRange(min=value, max=value, exact=True, entropy=0, isolated=isolated, intervals=[interval])
130+
return AstRange(min=value, max=value, ast_size=0, exact=True, entropy=0, isolated=isolated, intervals=[interval])
122131

123132

124-
def range_simple(min, max, stride, isolated):
125-
return AstRange(min=min, max=max, exact=True, isolated=isolated, intervals=[Interval(min, max, stride)])
133+
def range_simple(min, max, ast_size, stride, isolated):
134+
return AstRange(min=min, max=max, ast_size=ast_size, exact=True, isolated=isolated, intervals=[Interval(min, max, stride)])
126135

127136

128137
def get_stride_from_mask(and_mask, or_mask):
@@ -136,7 +145,7 @@ def get_stride_from_mask(and_mask, or_mask):
136145
return 2 ** lowest_bit
137146

138147

139-
def range_complex(min, max, exact, entropy, and_mask, or_mask, isolated=False):
148+
def range_complex(min, max, ast_size, exact, entropy, and_mask, or_mask, isolated=False):
140149
stride = get_stride_from_mask(and_mask, or_mask)
141150

142151
highest_bit = max.bit_length()
@@ -151,7 +160,7 @@ def range_complex(min, max, exact, entropy, and_mask, or_mask, isolated=False):
151160
# We dont need the mask, stride is enough
152161
and_mask = None
153162

154-
return AstRange(min=min, max=max,exact=exact, entropy=entropy,
155-
isolated=isolated, and_mask=and_mask, or_mask=or_mask,
156-
intervals=[Interval(min, max, stride)])
163+
return AstRange(min=min, max=max, ast_size=ast_size, exact=exact,
164+
entropy=entropy, isolated=isolated, and_mask=and_mask,
165+
or_mask=or_mask, intervals=[Interval(min, max, stride)])
157166

analyzer/shared/transmission.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ def to_dict(self):
165165
('branches', [str(x) for x in self.branches]),
166166
('constraints', [str(x) for x in self.constraints]),
167167
('requirements', self.requirements.to_dict()),
168-
('range', ranges.AstRange(0,0,False).to_dict() if self.range == None else self.range.to_dict()),
169-
('range_w_branches', ranges.AstRange(0,0,False).to_dict() if self.range_with_branches == None else self.range_with_branches.to_dict()),
168+
('range', ranges.AstRange(0,0,0,False).to_dict() if self.range == None else self.range.to_dict()),
169+
('range_w_branches', ranges.AstRange(0,0,0,False).to_dict() if self.range_with_branches == None else self.range_with_branches.to_dict()),
170170
('control', str(self.control)),
171171
('n_dependent_loads', str(self.max_load_depth))
172172
]
+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
4000019 jmp 0x400dead
1010

1111
------------------------------------------------
12-
uuid: c881501a-57ea-45fb-8d28-450f6aa76dd6
12+
uuid: 7ffd1082-0000-4995-b313-dbcc2c9ced70
1313

1414
Secret Address:
1515
- Expr: <BV64 rdi>
@@ -26,7 +26,7 @@ Base:
2626
- Independent Range: 0xffffffffffffffe0
2727
Transmission:
2828
- Expr: <BV64 0xffffffffffffffe0 + (0#48 .. LOAD_16[<BV64 rdi>]_20)>
29-
- Range: (0x0,0xffffffffffffffff, 0x1) Exact: False
29+
- Range: (0xffffffffffffffe0,0xffdf, 0x1) Exact: True
3030

3131
Register Requirements: {<BV64 rdi>}
3232
Constraints: []
+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
alias_type_1:
33
4000000 movzx r8d, word ptr [rdi] ; {Attacker@rdi} > {Secret@0x4000000}
44
4000004 mov rcx, qword ptr [r8-0x20]
5-
4000008 mov r10, qword ptr [rdi+r8+0x50] ; {Attacker@rdi, Secret@0x4000000} > TRANSMISSION
5+
4000008 mov r10, qword ptr [rdi+r8+0x50] ; {Secret@0x4000000, Attacker@rdi} > TRANSMISSION
66
400000d mov r11, qword ptr [rsi]
77
4000010 movzx r9d, word ptr [r11]
88
4000014 mov rax, qword ptr [r11+r9+0x20]
99
4000019 jmp 0x400dead
1010

1111
------------------------------------------------
12-
uuid: ccb61920-0524-4704-a156-d5872324e38d
12+
uuid: 8fb91631-205c-4606-8a75-cf830f7f8b6d
1313

1414
Secret Address:
1515
- Expr: <BV64 rdi>
+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
4000019 jmp 0x400dead
1010

1111
------------------------------------------------
12-
uuid: 853ce41f-58d0-4a94-85f5-930a5bfa6123
12+
uuid: d6dc9608-50db-4823-baa6-c48a912b8a15
1313

1414
Secret Address:
1515
- Expr: <BV64 rsi>
+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
4000008 mov r10, qword ptr [rdi+r8+0x50]
66
400000d mov r11, qword ptr [rsi] ; {Attacker@rsi} > {Secret@0x400000d}
77
4000010 movzx r9d, word ptr [r11] ; {Attacker@0x400000d} > {Attacker@0x4000010}
8-
4000014 mov rax, qword ptr [r11+r9+0x20] ; {Secret@0x400000d, Attacker@0x4000010} > TRANSMISSION
8+
4000014 mov rax, qword ptr [r11+r9+0x20] ; {Attacker@0x4000010, Secret@0x400000d} > TRANSMISSION
99
4000019 jmp 0x400dead
1010

1111
------------------------------------------------
12-
uuid: 3924305c-37d3-48b0-ae6a-fd42e6a02b39
12+
uuid: 830a85c4-0c4f-4245-b16d-719531b0e796
1313

1414
Secret Address:
1515
- Expr: <BV64 rsi>

tests/test-cases/alias_type_1/ref-output/reasoner_data.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
+-----------+------------------------+------------------------+---------------------------+---------------------------+-----------------------------+----------------------------+--------------------------+-----------------------------------+-----------------------------------+--------------------------------------+--------------------------------------+----------------------------------------+---------------------------------------+-------------------------------------+
209209
| pc | transmission_range_min | transmission_range_max | transmission_range_window | transmission_range_stride | transmission_range_and_mask | transmission_range_or_mask | transmission_range_exact | transmission_range_w_branches_min | transmission_range_w_branches_max | transmission_range_w_branches_window | transmission_range_w_branches_stride | transmission_range_w_branches_and_mask | transmission_range_w_branches_or_mask | transmission_range_w_branches_exact |
210210
+-----------+------------------------+------------------------+---------------------------+---------------------------+-----------------------------+----------------------------+--------------------------+-----------------------------------+-----------------------------------+--------------------------------------+--------------------------------------+----------------------------------------+---------------------------------------+-------------------------------------+
211-
| 0x4000004 | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | False | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | False |
211+
| 0x4000004 | 18446744073709551584 | 65503 | 65535 | 1 | | | True | 18446744073709551584 | 65503 | 65535 | 1 | | | True |
212212
| 0x4000008 | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | True | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | True |
213213
| 0x4000010 | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | True | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | True |
214214
| 0x4000014 | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | True | 0 | 18446744073709551615 | 18446744073709551615 | 1 | | | True |

0 commit comments

Comments
 (0)