1""" 2Wrappers around globals and caches to service the kmem package 3""" 4from __future__ import absolute_import, division, print_function 5 6from builtins import bytes 7from builtins import range 8from builtins import object 9 10from abc import ABCMeta, abstractmethod 11from collections import namedtuple 12from core import ( 13 caching, 14 gettype, 15 lldbwrap, 16) 17from ctypes import c_int64 18from six import add_metaclass 19 20class MemoryRange(namedtuple('MemoryRange', ['start', 'end'])): 21 @property 22 def size(self): 23 start, end = self 24 return end - start 25 26 def contains(self, addr): 27 start, end = self 28 return start <= addr < end 29 30 def __repr__(self): 31 return "{0.__class__.__name__}[{0.start:#x}, {0.end:#x})".format(self) 32 33 34class VMPointerUnpacker(object): 35 """ 36 Pointer unpacker for pointers packed with VM_PACK_POINTER() 37 """ 38 def __init__(self, target, param_var): 39 params = target.chkFindFirstGlobalVariable(param_var) 40 self.base_relative = params.xGetScalarByName('vmpp_base_relative') 41 self.bits = params.xGetScalarByName('vmpp_bits') 42 self.shift = params.xGetScalarByName('vmpp_shift') 43 self.base = params.xGetScalarByName('vmpp_base') 44 45 def unpack(self, packed): 46 """ 47 Unpacks an address according to the VM_PACK_POINTER() scheme 48 49 @param packed (int) 50 The packed value to unpack 51 52 @returns (int) 53 The unpacked address 54 """ 55 56 if not packed: 57 return None 58 59 if self.base_relative: 60 addr = (packed << self.shift) + self.base 61 else: 62 bits = self.bits 63 shift = self.shift 64 addr = c_int64(packed << (64 - bits)).value 65 addr >>= 64 - bits - shift 66 67 return addr & 0xffffffffffffffff 68 69 def unpack_value(self, sbv): 70 """ 71 Conveniency wrapper for self.unpack(sbv.chkGetValueAsUnsigned()) 72 """ 73 return self.unpack(sbv.chkGetValueAsUnsigned()) 74 75 76@add_metaclass(ABCMeta) 77class KMem(object): 78 """ 79 Singleton class that holds various important information 80 that is needed to make sense of the kernel memory layout, 81 heap data structures, globals, ... 82 """ 83 84 _HEAP_NAMES = [ "", "default.", "data.", "" ] 85 86 @staticmethod 87 def _parse_range(zone_info_v, name): 88 """ 89 Create a tuple representing a range (min_address, max_address, size) 90 """ 91 range_v = zone_info_v.chkGetChildMemberWithName(name) 92 left = range_v.xGetIntegerByName('min_address') 93 right = range_v.xGetIntegerByName('max_address') 94 return MemoryRange(left, right) 95 96 def __init__(self, target): 97 self.target = target 98 99 # 100 # Cache some globals everyone needs 101 # 102 self.page_shift = target.chkFindFirstGlobalVariable('page_shift').xGetValueAsInteger() 103 self.page_size = 1 << self.page_shift 104 self.page_mask = self.page_size - 1 105 106 phase_v = target.chkFindFirstGlobalVariable('startup_phase') 107 self.phase = phase_v.xGetValueAsInteger() 108 self.phases = set( 109 e.GetName()[len('STARTUP_SUB_'):] 110 for e in phase_v.GetType().get_enum_members_array() 111 if e.GetValueAsUnsigned() <= self.phase 112 ) 113 114 # 115 # Setup the number of CPUs we have 116 # 117 self.ncpus = target.chkFindFirstGlobalVariable('zpercpu_early_count').xGetValueAsInteger() 118 self.master_cpu = target.chkFindFirstGlobalVariable('master_cpu').xGetValueAsInteger() 119 self.zcpus = range(self.ncpus) if 'ZALLOC' in self.phases else (self.master_cpu, ) 120 self.pcpus = range(self.ncpus) if 'PERCPU' in self.phases else (self.master_cpu, ) 121 122 # 123 # Load all the ranges we will need 124 # 125 zone_info = target.chkFindFirstGlobalVariable('zone_info') 126 self.meta_range = self._parse_range(zone_info, 'zi_meta_range') 127 self.bits_range = self._parse_range(zone_info, 'zi_bits_range') 128 self.zone_range = self._parse_range(zone_info, 'zi_map_range') 129 try: 130 self.pgz_range = self._parse_range(zone_info, 'zi_pgz_range') 131 self.pgz_bt = target.chkFindFirstGlobalVariable('pgz_backtraces').xDereference() 132 except: 133 self.pgz_range = MemoryRange(0, 0) 134 self.pgz_bt = None 135 136 kmem_ranges = target.chkFindFirstGlobalVariable('kmem_ranges') 137 count = kmem_ranges.GetByteSize() // target.GetAddressByteSize() 138 addresses = target.xIterAsUInt64(kmem_ranges.GetLoadAddress(), count) 139 self.kmem_ranges = [ 140 MemoryRange(next(addresses), next(addresses)) 141 for i in range(0, count, 2) 142 ] 143 144 # 145 # And other important globals 146 # 147 self.stext = target.chkFindFirstGlobalVariable('vm_kernel_stext').xGetValueAsInteger() 148 self.num_zones = target.chkFindFirstGlobalVariable('num_zones').xGetValueAsInteger() 149 self.mag_size = target.chkFindFirstGlobalVariable('_zc_mag_size').xGetValueAsInteger() 150 self.zone_array = target.chkFindFirstGlobalVariable('zone_array') 151 self.zsec_array = target.chkFindFirstGlobalVariable('zone_security_array') 152 153 self.kernel_map = target.chkFindFirstGlobalVariable('kernel_map').Dereference() 154 self.vm_kobject = target.chkFindFirstGlobalVariable('kernel_object_store') 155 156 # 157 # Cache some crucial types used for memory walks 158 # 159 self.zpm_type = gettype('struct zone_page_metadata') 160 self.vm_map_type = gettype('struct _vm_map') 161 self.vmo_type = self.vm_kobject.GetType() 162 163 # 164 # Recognize whether the target is any form of KASAN kernel. 165 # 166 if any(target.FindFirstGlobalVariable('kasan_enabled')): 167 self.kasan = True 168 self.kasan_tbi = any(target.FindFirstGlobalVariable('kasan_tbi_enabled')) 169 self.kasan_classic = not self.kasan_tbi 170 else: 171 self.kasan = False 172 self.kasan_tbi = False 173 self.kasan_classic = False 174 175 # 176 # VM_PACK_POINTER Unpackers 177 # 178 self.kn_kq_packing = VMPointerUnpacker(target, 'kn_kq_packing_params') 179 self.vm_page_packing = VMPointerUnpacker(target, 'vm_page_packing_params') 180 self.rwlde_caller_packing = VMPointerUnpacker(target, 'rwlde_caller_packing_params') 181 182 @staticmethod 183 @caching.cache_statically 184 def get_shared(target=None): 185 """ 186 Returns a shared instance of the class 187 """ 188 189 arch = target.triple[:target.triple.find('-')] 190 191 if arch.startswith('arm64e'): 192 return _KMemARM64e(target) 193 elif arch.startswith('arm64'): 194 return _KMemARM64(target) 195 elif arch.startswith('x86_64'): 196 return _KMemX86(target) 197 else: 198 raise RuntimeError("Unsupported architecture: {}".format(arch)) 199 200 def iter_addresses(self, iterable): 201 """ 202 Conveniency wrapper to transform a list of integer to addresses 203 """ 204 return (self.make_address(a) for a in iterable) 205 206 # 207 # Abstract per-arch methods 208 # 209 210 @property 211 @abstractmethod 212 def has_ptrauth(self): 213 """ whether this target has ptrauth """ 214 215 pass 216 217 @abstractmethod 218 def PERCPU_BASE(self, cpu): 219 """ 220 Returns the per-cpu base for a given CPU number 221 222 @param cpu (int) 223 A CPU number 224 225 @returns (int) 226 The percpu base for this CPU 227 """ 228 229 pass 230 231 @abstractmethod 232 def make_address(self, addr): 233 """ 234 Make an address out of an integer 235 236 @param addr (int) 237 An address to convert 238 239 @returns (int) 240 """ 241 242 pass 243 244 245class _KMemARM64(KMem): 246 """ 247 Specialization of KMem for arm64 248 """ 249 250 def __init__(self, target): 251 super(_KMemARM64, self).__init__(target) 252 253 self.arm64_CpuDataEntries = target.chkFindFirstGlobalVariable('CpuDataEntries') 254 self.arm64_BootCpuData = target.chkFindFirstGlobalVariable('percpu_slot_cpu_data') 255 self.arm64_t1sz = target.chkFindFirstGlobalVariable('gT1Sz').xGetValueAsInteger() 256 self.arm64_sign_mask = 1 << (63 - self.arm64_t1sz) 257 258 @property 259 def has_ptrauth(self): 260 return False 261 262 def PERCPU_BASE(self, cpu): 263 cpu_data = self.arm64_CpuDataEntries.chkGetChildAtIndex(cpu) 264 boot_vaddr = self.arm64_BootCpuData.GetLoadAddress() 265 266 return cpu_data.xGetIntegerByName('cpu_data_vaddr') - boot_vaddr 267 268 def make_address(self, addr): 269 sign_mask = self.arm64_sign_mask 270 addr = addr & (sign_mask + sign_mask - 1) 271 return ((addr ^ sign_mask) - sign_mask) & 0xffffffffffffffff 272 273 274class _KMemARM64e(_KMemARM64): 275 """ 276 Specialization of KMem for arm64e 277 """ 278 279 @property 280 def has_ptrauth(self): 281 return True 282 283 284class _KMemX86(KMem): 285 """ 286 Specialization of KMem for Intel 287 """ 288 289 def __init__(self, target): 290 super(_KMemX86, self).__init__(target) 291 292 self.intel_cpu_data = target.chkFindFirstGlobalVariable('cpu_data_ptr') 293 294 @property 295 def has_ptrauth(self): 296 return False 297 298 def PERCPU_BASE(self, cpu): 299 cpu_data = self.intel_cpu_data.chkGetChildAtIndex(cpu) 300 return cpu_data.xGetIntegerByName('cpu_pcpu_base') 301 302 def make_address(self, addr): 303 return addr 304 305 306class PERCPUValue(object): 307 """ 308 Provides an enumerator for a percpu value 309 """ 310 311 def __init__(self, name, target = None): 312 """ 313 @param name (str) 314 The percpu slot name 315 316 @param target (SBTarget or None) 317 """ 318 319 self.kmem = KMem.get_shared() 320 self.sbv = self.kmem.target.chkFindFirstGlobalVariable('percpu_slot_' + name) 321 322 def __getitem__(self, cpu): 323 if cpu in self.kmem.pcpus: 324 sbv = self.sbv 325 addr = sbv.GetLoadAddress() + self.kmem.PERCPU_BASE(cpu) 326 return sbv.chkCreateValueFromAddress(sbv.GetName(), addr, sbv.GetType()) 327 raise IndexError 328 329 def __iter__(self): 330 return (item[1] for items in self.items()) 331 332 def items(self): 333 """ 334 Iterator of (cpu, SBValue) tuples for the given PERCPUValue 335 """ 336 337 kmem = self.kmem 338 sbv = self.sbv 339 name = sbv.GetName() 340 ty = sbv.GetType() 341 addr = sbv.GetLoadAddress() 342 343 return ( 344 (cpu, sbv.chkCreateValueFromAddress(name, addr + kmem.PERCPU_BASE(cpu), ty)) 345 for cpu in kmem.pcpus 346 ) 347 348__all__ = [ 349 KMem.__name__, 350 MemoryRange.__name__, 351 PERCPUValue.__name__, 352] 353