def __invalid_memory_objects_check(self, objs: Iterable[drgn.Object], fatal: bool) -> Iterable[drgn.Object]: """ A filter method for objects passed through the pipeline that are backed by invalid memory. When `fatal` is set to True we raise an error which will stop this control flow when such objects are encountered. If `fatal` is False we just print the error and go on. """ for obj in objs: try: obj.read_() except drgn.FaultError as err: if obj.address_ is None: # # This is possible when the object was created `echo`. # err_msg = str(err) else: err_msg = f"addresss {hex(obj.address_of_().value_())}" err = CommandError(self.name, f"invalid memory access: {err_msg}") if fatal: raise err print(err.text) continue yield obj
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: for obj in objs: # # We canonicalize the type just in case it is a typedef # to a pointer (e.g. typedef char* char_p). # obj_type = type_canonicalize(obj.type_) if obj_type.kind != drgn.TypeKind.POINTER: raise CommandError( self.name, f"'{obj.type_.type_name()}' is not a valid pointer type") if obj_type.type.type_name() == 'void': raise CommandError(self.name, "cannot dereference a void pointer") yield drgn.Object(get_prog(), type=obj.type_.type, address=obj.value_())
def __init__(self, args: str = "", name: str = "_") -> None: super().__init__(args, name) if not self.args.type: self.parser.error("the following arguments are required: <type>") tname = " ".join(self.args.type) try: self.type = target.get_type(tname) except LookupError: raise CommandError(self.name, f"could not find type '{tname}'")
def caller(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: """ This method will dispatch to the appropriate instance function based on the type of the input we receive. """ out_type = None if self.output_type is not None: out_type = target.get_type(self.output_type) baked = dict() for (_, method) in inspect.getmembers(self, inspect.ismethod): if not hasattr(method, "input_typename_handled"): continue baked[type_canonicalize_name( method.input_typename_handled)] = method if self.isfirst: assert not objs yield from self.no_input() return for i in objs: obj_type_name = type_canonical_name(i.type_) # try subclass-specified input types first, so that they can # override any other behavior if obj_type_name in baked: yield from baked[obj_type_name](i) continue # try passthrough of output type # note, this may also be handled by subclass-specified input types if obj_type_name == type_canonical_name(out_type): yield i continue # try walkers if out_type is not None: try: # pylint: disable=protected-access for obj in Walk()._call([i]): yield drgn.cast(out_type, obj) continue except CommandError: pass # error raise CommandError( self.name, 'no handler for input of type {}'.format(i.type_))
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: """ This function will call walk() on each input object, verifying the types as we go. """ assert self.input_type is not None type_ = target.get_type(self.input_type) for obj in objs: if obj.type_ != type_: raise CommandError( self.name, 'expected input of type {}, but received {}'.format( type_, obj.type_)) yield from self.walk(obj)
def check_input_type(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: """ This function acts as a generator, checking that each passed object matches the input type for the command """ assert self.input_type is not None type_name = type_canonicalize_name(self.input_type) for obj in objs: if type_canonical_name(obj.type_) != type_name: raise CommandError( self.name, f'expected input of type {self.input_type}, but received ' f'type {obj.type_}') yield obj
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: """ This function will call walk() on each input object, verifying the types as we go. """ assert self.input_type is not None expected_type = type_canonicalize_name(self.input_type) for obj in objs: if type_canonical_name(obj.type_) != expected_type: raise CommandError( self.name, 'expected input of type {}, but received {}'.format( expected_type, type_canonical_name(obj.type_))) yield from self.walk(obj)
def _call( # type: ignore[return] self, objs: Iterable[drgn.Object]) -> Optional[Iterable[drgn.Object]]: """ This function will call pretty_print() on each input object, verifying the types as we go. """ assert self.input_type is not None type_name = type_canonicalize_name(self.input_type) for obj in objs: if type_canonical_name(obj.type_) != type_name: raise CommandError( self.name, f'exepected input of type {self.input_type}, but received ' f'type {obj.type_}') self.pretty_print([obj])
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: baked = { type_canonicalize_name(type_): class_ for type_, class_ in Walker.allWalkers.items() } has_input = False for i in objs: has_input = True this_type_name = type_canonical_name(i.type_) if this_type_name not in baked: raise CommandError(self.name, Walk._help_message(i.type_)) yield from baked[this_type_name]().walk(i) # If we got no input and we're the last thing in the pipeline, we're # probably the first thing in the pipeline. Print out the available # walkers. if not has_input and self.islast: print(Walk._help_message())
def call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: # pylint: disable=missing-docstring # # Even though we have __invalid_memory_objects_check() to # ensure that the objects returned are valid, we still # need to account for invalid accesses happening while # the command is running. # try: result = self._call(objs) if result is not None: # # The whole point of the SingleInputCommands are that # they don't stop executing in the first encounter of # a bad dereference. That's why we check here whether # the command that we are running is a subclass of # SingleInputCommand and we set the `fatal` flag # accordinly. # yield from self.__invalid_memory_objects_check( result, not issubclass(self.__class__, SingleInputCommand)) except drgn.FaultError as err: raise CommandError(self.name, f"invalid memory access: {str(err)}")
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: for obj in objs: # # Even though we have __invalid_memory_objects_check() to # ensure that the objects returned are valid, we still # need to account for invalid accesses happening while # the command is running. # result = None try: result = self._call_one(obj) except drgn.FaultError as err: if obj.address_ is None: # # This is possible when the object was created `echo`. # err_msg = f"invalid memory access: {str(err)}" else: err_msg = "invalid memory access while handling object " err_msg += "at address {hex(obj.address_of_().value_())}" cmd_err = CommandError(self.name, err_msg) print(cmd_err.text) if result is not None: yield from result
def no_input(self) -> Iterable[drgn.Object]: # pylint: disable=missing-docstring raise CommandError(self.name, 'command requires an input')
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: for obj in objs: try: yield drgn.cast(self.type, obj) except TypeError as err: raise CommandError(self.name, str(err))