This is xnu-11215.1.10. See this file in:
"""
Custom pointer support

This module provides support for special pointer types that are not native to the
language used by the target being debugged. Such pointers may be represented as a struct
or class (for example IOKit's shared pointers).

A custom pointer class must subclass the PointerPolicy class and implement all of its
abstract methods. The MetaPointerPolicy metaclass ensures that all known subclasses are
registered in a global list (wherever they are located in the lldb macro sources).

A client can obtain a PointerPolicy instance by calling the match method with an SBValue
instance as an argument. The returned value is one of:

    * None - the match was unsuccessful and this SBValue instance is not a pointer.
    * Concrete instance - An instance of the concrete PointerPolicy class that will handle
      pointer operations for the given SBValue.

Concrete policy instances implement an API that allows a client to operate on a value
like a native pointer (for example unwrapping a native pointer from a smart pointer).

Example:

    # Obtain an SBValue instance.
    val = kern.global.GlobalVariable.GetSBValue()

    # Try to match the pointer policy for the given value.
    policy = PointerPolicy.match(val)

    # Unwrap the pointer SBValue.
    if policy:
        val = policy.GetPointerSBValue(val)

    ... Operate on val as usual.
"""
from operator import methodcaller
from abc import ABCMeta, abstractmethod

import lldb

from .caching import cache_statically


class MetaPointerPolicy(ABCMeta):
    """ Register a custom pointer policy in global list. """

    classes = []

    def __new__(cls, clsname, bases, args):
        newcls = super(MetaPointerPolicy, cls).__new__(cls, clsname, bases, args)
        cls.classes.append(newcls)
        return newcls


class Singleton(MetaPointerPolicy):
    """ Meta class for creation of singleton instances. """

    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class PointerPolicy(object, metaclass=ABCMeta):
    """ Abstract base class common to every custom pointer policy. """

    @classmethod
    def match(cls, sbvalue):
        """ Match pointer representation based on given SBValue. """
        matching = filter(bool, map(methodcaller('match', sbvalue), MetaPointerPolicy.classes))
        return next(matching, None)

    @abstractmethod
    def GetPointerSBValue(self, sbvalue):
        """ Returns pointer value that debugger should operate on. """


# Pointers need to have their TBI byte stripped if in use. TBI KASan,
# for instance, tags pointers to detect improper memory accesses. Reading
# values from such tagged pointers fails.
#
# Stripping the pointers requires to learn whether TBI is in use or not.
# We do that by checking presence of 'kasan_tbi_enabled' symbol which only
# exists on the TBI KASan variant. Since KASan is one of more TBI
# consumers (along with PAC or Sandbox) this is not an ideal approach.
# Inspecting respective CPU state would be more appropriate.


class NativePointer(PointerPolicy, metaclass=Singleton):
    """ Policy for native pointers.

        Strips top bits of a pointer if TBI is in use. Otherwise
        pointer is used as-is.

        Native pointers do not have any per-pointer attributes so this policy
        can be singleton instance.
    """

    @staticmethod
    @cache_statically
    def isTagged(target=None):
        """ Returns true on TBI KASan targets, false otherwise. """
        is_tagged = target.FindFirstGlobalVariable('kasan_tbi_enabled').GetValueAsUnsigned()
        return is_tagged

    def __init__(self):
        if self.isTagged():
            self._stripPtr = self.stripPtr
        else:
            self._stripPtr = lambda val: val

    @classmethod
    def match(cls, sbvalue):
        return cls() if sbvalue.GetType().IsPointerType() else None

    @staticmethod
    def stripPtr(sbvalue):
        """ Strips the TBI byte value. Since the value is not a plain value but
            represents a value of a variable, a register or an expression the
            conversion is performed by (re-)creating the value through expression.
        """
        if sbvalue.GetValueAsAddress() != sbvalue.GetValueAsUnsigned():
            addr = sbvalue.GetValueAsAddress()
            sbv_new = sbvalue.CreateValueFromExpression(None, '(void *)' + str(addr))
            return sbv_new.Cast(sbvalue.GetType())


        return sbvalue

    def GetPointerSBValue(self, sbvalue):
        return self._stripPtr(sbvalue)