1*e3723e1fSApple OSS Distributions""" 2*e3723e1fSApple OSS DistributionsCustom pointer support 3*e3723e1fSApple OSS Distributions 4*e3723e1fSApple OSS DistributionsThis module provides support for special pointer types that are not native to the 5*e3723e1fSApple OSS Distributionslanguage used by the target being debugged. Such pointers may be represented as a struct 6*e3723e1fSApple OSS Distributionsor class (for example IOKit's shared pointers). 7*e3723e1fSApple OSS Distributions 8*e3723e1fSApple OSS DistributionsA custom pointer class must subclass the PointerPolicy class and implement all of its 9*e3723e1fSApple OSS Distributionsabstract methods. The MetaPointerPolicy metaclass ensures that all known subclasses are 10*e3723e1fSApple OSS Distributionsregistered in a global list (wherever they are located in the lldb macro sources). 11*e3723e1fSApple OSS Distributions 12*e3723e1fSApple OSS DistributionsA client can obtain a PointerPolicy instance by calling the match method with an SBValue 13*e3723e1fSApple OSS Distributionsinstance as an argument. The returned value is one of: 14*e3723e1fSApple OSS Distributions 15*e3723e1fSApple OSS Distributions * None - the match was unsuccessful and this SBValue instance is not a pointer. 16*e3723e1fSApple OSS Distributions * Concrete instance - An instance of the concrete PointerPolicy class that will handle 17*e3723e1fSApple OSS Distributions pointer operations for the given SBValue. 18*e3723e1fSApple OSS Distributions 19*e3723e1fSApple OSS DistributionsConcrete policy instances implement an API that allows a client to operate on a value 20*e3723e1fSApple OSS Distributionslike a native pointer (for example unwrapping a native pointer from a smart pointer). 21*e3723e1fSApple OSS Distributions 22*e3723e1fSApple OSS DistributionsExample: 23*e3723e1fSApple OSS Distributions 24*e3723e1fSApple OSS Distributions # Obtain an SBValue instance. 25*e3723e1fSApple OSS Distributions val = kern.global.GlobalVariable.GetSBValue() 26*e3723e1fSApple OSS Distributions 27*e3723e1fSApple OSS Distributions # Try to match the pointer policy for the given value. 28*e3723e1fSApple OSS Distributions policy = PointerPolicy.match(val) 29*e3723e1fSApple OSS Distributions 30*e3723e1fSApple OSS Distributions # Unwrap the pointer SBValue. 31*e3723e1fSApple OSS Distributions if policy: 32*e3723e1fSApple OSS Distributions val = policy.GetPointerSBValue(val) 33*e3723e1fSApple OSS Distributions 34*e3723e1fSApple OSS Distributions ... Operate on val as usual. 35*e3723e1fSApple OSS Distributions""" 36*e3723e1fSApple OSS Distributionsfrom operator import methodcaller 37*e3723e1fSApple OSS Distributionsfrom abc import ABCMeta, abstractmethod 38*e3723e1fSApple OSS Distributions 39*e3723e1fSApple OSS Distributionsimport lldb 40*e3723e1fSApple OSS Distributions 41*e3723e1fSApple OSS Distributionsfrom .caching import cache_statically 42*e3723e1fSApple OSS Distributions 43*e3723e1fSApple OSS Distributions 44*e3723e1fSApple OSS Distributionsclass MetaPointerPolicy(ABCMeta): 45*e3723e1fSApple OSS Distributions """ Register a custom pointer policy in global list. """ 46*e3723e1fSApple OSS Distributions 47*e3723e1fSApple OSS Distributions classes = [] 48*e3723e1fSApple OSS Distributions 49*e3723e1fSApple OSS Distributions def __new__(cls, clsname, bases, args): 50*e3723e1fSApple OSS Distributions newcls = super(MetaPointerPolicy, cls).__new__(cls, clsname, bases, args) 51*e3723e1fSApple OSS Distributions cls.classes.append(newcls) 52*e3723e1fSApple OSS Distributions return newcls 53*e3723e1fSApple OSS Distributions 54*e3723e1fSApple OSS Distributions 55*e3723e1fSApple OSS Distributionsclass Singleton(MetaPointerPolicy): 56*e3723e1fSApple OSS Distributions """ Meta class for creation of singleton instances. """ 57*e3723e1fSApple OSS Distributions 58*e3723e1fSApple OSS Distributions _instances = {} 59*e3723e1fSApple OSS Distributions 60*e3723e1fSApple OSS Distributions def __call__(cls, *args, **kwargs): 61*e3723e1fSApple OSS Distributions if cls not in cls._instances: 62*e3723e1fSApple OSS Distributions cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 63*e3723e1fSApple OSS Distributions return cls._instances[cls] 64*e3723e1fSApple OSS Distributions 65*e3723e1fSApple OSS Distributions 66*e3723e1fSApple OSS Distributionsclass PointerPolicy(object, metaclass=ABCMeta): 67*e3723e1fSApple OSS Distributions """ Abstract base class common to every custom pointer policy. """ 68*e3723e1fSApple OSS Distributions 69*e3723e1fSApple OSS Distributions @classmethod 70*e3723e1fSApple OSS Distributions def match(cls, sbvalue): 71*e3723e1fSApple OSS Distributions """ Match pointer representation based on given SBValue. """ 72*e3723e1fSApple OSS Distributions matching = filter(bool, map(methodcaller('match', sbvalue), MetaPointerPolicy.classes)) 73*e3723e1fSApple OSS Distributions return next(matching, None) 74*e3723e1fSApple OSS Distributions 75*e3723e1fSApple OSS Distributions @abstractmethod 76*e3723e1fSApple OSS Distributions def GetPointerSBValue(self, sbvalue): 77*e3723e1fSApple OSS Distributions """ Returns pointer value that debugger should operate on. """ 78*e3723e1fSApple OSS Distributions 79*e3723e1fSApple OSS Distributions 80*e3723e1fSApple OSS Distributions# Pointers need to have their TBI byte stripped if in use. TBI KASan, 81*e3723e1fSApple OSS Distributions# for instance, tags pointers to detect improper memory accesses. Reading 82*e3723e1fSApple OSS Distributions# values from such tagged pointers fails. 83*e3723e1fSApple OSS Distributions# 84*e3723e1fSApple OSS Distributions# Stripping the pointers requires to learn whether TBI is in use or not. 85*e3723e1fSApple OSS Distributions# We do that by checking presence of 'kasan_tbi_enabled' symbol which only 86*e3723e1fSApple OSS Distributions# exists on the TBI KASan variant. Since KASan is one of more TBI 87*e3723e1fSApple OSS Distributions# consumers (along with PAC or Sandbox) this is not an ideal approach. 88*e3723e1fSApple OSS Distributions# Inspecting respective CPU state would be more appropriate. 89*e3723e1fSApple OSS Distributions 90*e3723e1fSApple OSS Distributions 91*e3723e1fSApple OSS Distributionsclass NativePointer(PointerPolicy, metaclass=Singleton): 92*e3723e1fSApple OSS Distributions """ Policy for native pointers. 93*e3723e1fSApple OSS Distributions 94*e3723e1fSApple OSS Distributions Strips top bits of a pointer if TBI is in use. Otherwise 95*e3723e1fSApple OSS Distributions pointer is used as-is. 96*e3723e1fSApple OSS Distributions 97*e3723e1fSApple OSS Distributions Native pointers do not have any per-pointer attributes so this policy 98*e3723e1fSApple OSS Distributions can be singleton instance. 99*e3723e1fSApple OSS Distributions """ 100*e3723e1fSApple OSS Distributions 101*e3723e1fSApple OSS Distributions @staticmethod 102*e3723e1fSApple OSS Distributions @cache_statically 103*e3723e1fSApple OSS Distributions def isTagged(target=None): 104*e3723e1fSApple OSS Distributions """ Pointer stripping isn't required as of recent lldb changes that fixed ignoring 105*e3723e1fSApple OSS Distributions non-addresable bits. 106*e3723e1fSApple OSS Distributions Due to performance degredation on relevant coredumps, stripping is being 107*e3723e1fSApple OSS Distributions as a quick remedy. 108*e3723e1fSApple OSS Distributions Depending on future debugging needs, it'll be removed completely, or improved 109*e3723e1fSApple OSS Distributions to have acceptable performance. 110*e3723e1fSApple OSS Distributions """ 111*e3723e1fSApple OSS Distributions is_tagged = False 112*e3723e1fSApple OSS Distributions 113*e3723e1fSApple OSS Distributions """ Returns true on TBI KASan targets, false otherwise. """ 114*e3723e1fSApple OSS Distributions # is_tagged = target.FindFirstGlobalVariable('kasan_tbi_enabled').IsValid() 115*e3723e1fSApple OSS Distributions return is_tagged 116*e3723e1fSApple OSS Distributions 117*e3723e1fSApple OSS Distributions def __init__(self): 118*e3723e1fSApple OSS Distributions if self.isTagged(): 119*e3723e1fSApple OSS Distributions self._stripPtr = self.stripPtr 120*e3723e1fSApple OSS Distributions else: 121*e3723e1fSApple OSS Distributions self._stripPtr = lambda val: val 122*e3723e1fSApple OSS Distributions 123*e3723e1fSApple OSS Distributions @classmethod 124*e3723e1fSApple OSS Distributions def match(cls, sbvalue): 125*e3723e1fSApple OSS Distributions return cls() if sbvalue.GetType().IsPointerType() else None 126*e3723e1fSApple OSS Distributions 127*e3723e1fSApple OSS Distributions @staticmethod 128*e3723e1fSApple OSS Distributions def stripPtr(sbvalue: lldb.SBValue): 129*e3723e1fSApple OSS Distributions """ Strips the TBI byte value. Since the value is not a plain value but 130*e3723e1fSApple OSS Distributions represents a value of a variable, a register or an expression the 131*e3723e1fSApple OSS Distributions conversion is performed by (re-)creating the value through expression. 132*e3723e1fSApple OSS Distributions """ 133*e3723e1fSApple OSS Distributions if sbvalue.GetValueAsAddress() != sbvalue.GetValueAsUnsigned(): 134*e3723e1fSApple OSS Distributions addr = sbvalue.GetValueAsAddress() 135*e3723e1fSApple OSS Distributions sbv_new = sbvalue.CreateValueFromExpression(None, '(void *)' + str(addr)) 136*e3723e1fSApple OSS Distributions return sbv_new.Cast(sbvalue.GetType()) 137*e3723e1fSApple OSS Distributions 138*e3723e1fSApple OSS Distributions return sbvalue 139*e3723e1fSApple OSS Distributions 140*e3723e1fSApple OSS Distributions def GetPointerSBValue(self, sbvalue): 141*e3723e1fSApple OSS Distributions return self._stripPtr(sbvalue) 142