]>
git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/kasan.py
3 from core
.configuration
import *
18 0xf1: 'STACK_LEFT_RZ',
20 0xf3: 'STACK_RIGHT_RZ',
22 0xf8: 'STACK_OOSCOPE',
26 0xfb: 'HEAP_RIGHT_RZ',
32 enable
= kern
.globals.kasan_enabled
37 def shadow_for_address(addr
, shift
):
38 return ((addr
>> 3) + shift
)
40 def address_for_shadow(addr
, shift
):
41 return ((addr
- shift
) << 3)
43 def get_shadow_byte(shadow_addr
):
44 return unsigned(kern
.GetValueFromAddress(shadow_addr
, 'uint8_t *')[0])
47 for (k
,v
) in shadow_strings
.iteritems():
48 print " {:02x}: {}".format(k
,v
)
50 def print_shadow_context(addr
, context
):
51 addr
= shadow_for_address(addr
, shift
)
52 base
= (addr
& ~
0xf) - 16 * context
53 shadow
= kern
.GetValueFromAddress(unsigned(base
), "uint8_t *")
55 print " "*17 + " 0 1 2 3 4 5 6 7 8 9 a b c d e f"
56 for x
in range(0, 2*context
+1):
59 for y
in xrange(x
*16, (x
+1)*16):
64 elif base
+y
+1 == addr
:
67 vals
+= "{}{:02x}{}".format(l
, sh
, r
)
69 print("{:x}:{}".format(base
+ 16*x
, vals
))
72 def print_alloc_free_entry(addr
, orig_ptr
):
73 h
= kern
.GetValueFromAddress(addr
, 'struct freelist_entry *')
74 asz
= unsigned(h
.size
)
75 usz
= unsigned(h
.user_size
)
76 pgsz
= unsigned(kern
.globals.page_size
)
80 if str(zone
.z_name
).startswith("fakestack"):
81 alloc_type
= "fakestack"
85 leftrz
= unsigned(zone
.kasan_redzone
)
88 if asz
- usz
>= 2*pgsz
:
91 leftrz
= kasan_guard_size
93 rightrz
= asz
- usz
- leftrz
95 print "Freed {} object".format(alloc_type
)
96 print "Valid range: 0x{:x} -- 0x{:x} ({} bytes)".format(addr
+ leftrz
, addr
+ leftrz
+ usz
- 1, usz
)
97 print "Total range: 0x{:x} -- 0x{:x} ({} bytes)".format(addr
, addr
+ asz
- 1, asz
)
98 print "Offset: {} bytes".format(orig_ptr
- addr
- leftrz
)
99 print "Redzone: {} / {} bytes".format(leftrz
, rightrz
)
101 print "Zone: 0x{:x} <{:s}>".format(unsigned(zone
), zone
.z_name
)
103 btframes
= unsigned(h
.frames
)
106 print "Free site backtrace ({} frames):".format(btframes
)
107 for i
in xrange(0, btframes
):
108 fr
= unsigned(kern
.globals.vm_kernel_slid_base
) + unsigned(h
.backtrace
[i
])
109 print " #{:}: {}".format(btframes
-i
-1, GetSourceInformationForAddress(fr
))
112 print_hexdump(addr
, asz
, 1)
116 def magic_for_addr(addr
, xor
):
117 magic
= addr
& 0xffff
118 magic ^
= (addr
>> 16) & 0xffff
119 magic ^
= (addr
>> 32) & 0xffff
120 magic ^
= (addr
>> 48) & 0xffff
124 def print_alloc_info(_addr
):
125 addr
= (_addr
& ~
0x7)
127 _shp
= shadow_for_address(_addr
, shift
)
128 _shbyte
= get_shadow_byte(_shp
)
129 _shstr
= shadow_byte_to_string(_shbyte
)
131 # If we're in a left redzone, scan to the start of the real allocation, where
132 # the header should live
134 while shbyte
== 0xfa:
136 shbyte
= get_shadow_byte(shadow_for_address(addr
, shift
))
138 # Search backwards for an allocation
140 while searchbytes
< 8*4096:
142 shp
= shadow_for_address(addr
, shift
)
143 shbyte
= get_shadow_byte(shp
)
144 shstr
= shadow_byte_to_string(shbyte
)
146 headerp
= addr
- alloc_header_sz
147 liveh
= kern
.GetValueFromAddress(headerp
, 'struct kasan_alloc_header *')
148 freeh
= kern
.GetValueFromAddress(addr
, 'struct freelist_entry *')
150 # heap allocations should only ever have these shadow values
151 if shbyte
not in (0,1,2,3,4,5,6,7, 0xfa, 0xfb, 0xfd, 0xf5):
152 print "No allocation found at 0x{:x} (found shadow {:x})".format(_addr
, shbyte
)
155 if magic_for_addr(addr
, 0x3a65) == unsigned(liveh
.magic
):
156 usz
= unsigned(liveh
.user_size
)
157 asz
= unsigned(liveh
.alloc_size
)
158 leftrz
= unsigned(liveh
.left_rz
)
159 base
= headerp
+ alloc_header_sz
- leftrz
161 if _addr
>= base
and _addr
< base
+ asz
:
162 footer
= kern
.GetValueFromAddress(addr
+ usz
, 'struct kasan_alloc_footer *')
163 rightrz
= asz
- usz
- leftrz
164 offset
= _addr
- addr
166 print "Live heap object"
167 print "Valid range: 0x{:x} -- 0x{:x} ({} bytes)".format(addr
, addr
+ usz
- 1, usz
)
168 print "Total range: 0x{:x} -- 0x{:x} ({} bytes)".format(base
, base
+ asz
- 1, asz
)
169 print "Offset: {} bytes (shadow: 0x{:02x} {}, remaining: {} bytes)".format(offset
, _shbyte
, _shstr
, usz
- offset
)
170 print "Redzone: {} / {} bytes".format(leftrz
, rightrz
)
172 btframes
= unsigned(liveh
.frames
)
174 print "Alloc site backtrace ({} frames):".format(btframes
)
175 for i
in xrange(0, btframes
):
176 fr
= unsigned(kern
.globals.vm_kernel_slid_base
) + unsigned(footer
.backtrace
[i
])
177 print " #{:}: {}".format(btframes
-i
-1, GetSourceInformationForAddress(fr
))
180 print_hexdump(base
, asz
, 1)
183 elif magic_for_addr(addr
, 0xf233) == unsigned(freeh
.magic
):
184 asz
= unsigned(freeh
.size
)
185 if _addr
>= addr
and _addr
< addr
+ asz
:
186 print_alloc_free_entry(addr
, _addr
)
192 print "No allocation found at 0x{:x}".format(_addr
)
194 def shadow_byte_to_string(sb
):
195 return shadow_strings
.get(sb
, '??')
197 def print_whatis(_addr
, ctx
):
205 shaddr
= shadow_for_address(addr
, shift
)
207 shbyte
= get_shadow_byte(shaddr
)
209 print "Unmapped shadow 0x{:x} for address 0x{:x}".format(shaddr
, addr
)
214 if shbyte
in [0xfa, 0xfb, 0xfd, 0xf5]:
215 print_alloc_info(_addr
)
218 if shbyte
not in [0,1,2,3,4,5,6,7,0xf8]:
219 print "Poisoned memory, shadow {:x} [{}]".format(shbyte
, shadow_byte_to_string(shbyte
))
223 extra
= "Out-of-scope"
225 # look for the base of the object
226 while shbyte
in [0,1,2,3,4,5,6,7,0xf8]:
232 shbyte
= get_shadow_byte(shadow_for_address(addr
, shift
))
235 print "No object found"
240 # If we did not find a left/mid redzone, we aren't in an object
241 if leftrz
not in [0xf1, 0xf2, 0xfa, 0xf9]:
242 print "No object found"
245 # now size the object
246 addr
= (_addr
& ~
0x7) + 8
247 shbyte
= get_shadow_byte(shadow_for_address(addr
, shift
))
248 while shbyte
in [0,1,2,3,4,5,6,7,0xf8]:
254 shbyte
= get_shadow_byte(shadow_for_address(addr
, shift
))
257 print "No object found"
261 # work out the type of the object from its redzone
263 if leftrz
== 0xf1 or leftrz
== 0xf2:
265 elif leftrz
== 0xf9 and rightrz
== 0xf9:
267 elif leftrz
== 0xfa and rightrz
== 0xfb:
268 print_alloc_info(_addr
)
271 print "{} {} object".format(extra
, objtype
)
272 print "Valid range: 0x{:x} -- 0x{:x} ({} bytes)".format(base
, base
+total_size
-1, total_size
)
273 print "Offset: {} bytes".format(_addr
- base
)
275 print_hexdump(base
, total_size
, 1)
277 def print_hexdump(base
, size
, ctx
):
281 start
= base
- 16*ctx
283 size
= min(size
+ 16*2*ctx
, 256)
286 data_array
= kern
.GetValueFromAddress(start
, "uint8_t *")
287 print_hex_data(data_array
[0:size
], start
, "Hexdump")
291 def kasan_subcommand(cmd
, args
, opts
):
294 addr
= long(args
[0], 0)
296 if cmd
in ['a2s', 'toshadow', 'fromaddr', 'fromaddress']:
297 print "0x{:016x}".format(shadow_for_address(addr
, shift
))
298 elif cmd
in ['s2a', 'toaddr', 'toaddress', 'fromshadow']:
299 print "0x{:016x}".format(address_for_shadow(addr
, shift
))
300 elif cmd
== 'shadow':
301 shadow
= shadow_for_address(addr
, shift
)
302 sb
= get_shadow_byte(shadow
)
303 print("0x{:02x} @ 0x{:016x} [{}]\n\n".format(sb
, shadow
, shadow_byte_to_string(sb
)))
304 ctx
= long(opts
.get("-C", 5))
305 print_shadow_context(addr
, ctx
)
306 elif cmd
== 'key' or cmd
== 'legend':
309 pages_used
= unsigned(kern
.globals.shadow_pages_used
)
310 pages_total
= unsigned(kern
.globals.shadow_pages_total
)
311 nkexts
= unsigned(kern
.globals.kexts_loaded
)
312 print "Offset: 0x{:016x}".format(shift
)
313 print "Shadow used: {} / {} ({:.1f}%)".format(pages_used
, pages_total
, 100.0*pages_used
/pages_total
)
314 print "Kexts loaded: {}".format(nkexts
)
315 elif cmd
== 'whatis':
316 ctx
= long(opts
.get("-C", 1))
317 print_whatis(addr
, ctx
)
318 elif cmd
== 'alloc' or cmd
== 'heap':
319 print_alloc_info(addr
)
321 print "Unknown subcommand: `{}'".format(cmd
)
323 @lldb_command('kasan', 'C:')
324 def Kasan(cmd_args
=None, cmd_options
={}):
325 """kasan <cmd> [opts..]
329 info basic KASan information
330 shadow <addr> print shadow around 'addr'
331 heap <addr> show info about heap object at 'addr'
332 whatis <addr> print whatever KASan knows about address
333 toshadow <addr> convert address to shadow pointer
334 toaddr <shdw> convert shadow pointer to address
335 legend print a shadow byte table
337 -C <num> : num lines of context to show"""
339 if not is_kasan_build():
340 print "KASan not enabled in build"
343 if len(cmd_args
) == 0:
348 shift
= unsigned(kern
.globals.__asan
_shadow
_memory
_dynamic
_address
)
350 # Since the VM is not aware of the KASan shadow mapping, accesses to it will
351 # fail. Setting kdp_read_io=1 avoids this check.
352 if GetConnectionProtocol() == "kdp" and unsigned(kern
.globals.kdp_read_io
) == 0:
353 print "Setting kdp_read_io=1 to allow KASan shadow reads"
354 if sizeof(kern
.globals.kdp_read_io
) == 4:
355 WriteInt32ToMemoryAddress(1, addressof(kern
.globals.kdp_read_io
))
356 elif sizeof(kern
.globals.kdp_read_io
) == 8:
357 WriteInt64ToMemoryAddress(1, addressof(kern
.globals.kdp_read_io
))
358 readio
= unsigned(kern
.globals.kdp_read_io
)
361 return kasan_subcommand(cmd_args
[0], cmd_args
[1:], cmd_options
)