def coerce(self, obj: drgn.Object) -> drgn.Object: """ This function attemts to massage the input object into an object of a different type. """ # same type is fine if obj.type_ == self.type: return obj # "void *" can be coerced to any pointer type if (obj.type_.kind is drgn.TypeKind.POINTER and obj.type_.primitive is drgn.PrimitiveType.C_VOID): return drgn.cast(self.type, obj) # integers can be coerced to any pointer typo if obj.type_.kind is drgn.TypeKind.INT: return drgn.cast(self.type, obj) # "type" can be coerced to "type *" if obj.type_.kind is not drgn.TypeKind.POINTER and obj.address_of_( ).type_ == self.type: return obj.address_of_() raise TypeError("can not coerce {} to {}".format(obj.type_, self.type))
def print_namespace(ns): print("mlx5_flow_namespace %lx" % ns.address_of_()) prio_addr = ns.node.children.address_of_() for prio_node in list_for_each_entry('struct fs_node', prio_addr, 'list'): prio = cast("struct fs_prio *", prio_node) # print("level 1 namespace") print_prio(prio) # print(prio) n2_addr = prio.node.children.address_of_() for n2_node in list_for_each_entry('struct fs_node', n2_addr, 'list'): n2 = cast("struct mlx5_flow_namespace *", n2_node) # print("level 2 prio") # print(n2) p3_addr = n2.node.children.address_of_() for p3_node in list_for_each_entry('struct fs_node', p3_addr, 'list'): p3 = cast("struct fs_prio *", p3_node) # print("level 3 prio") # print(p3) table_addr = p3.node.children.address_of_() for table_node in list_for_each_entry('struct fs_node', table_addr, 'list'): table = cast("struct mlx5_flow_table *", table_node) # print("level 4 flow table") print_table(table)
def aux(node, index): if _is_internal_node(node, RADIX_TREE_INTERNAL_NODE): parent = _entry_to_node(node, RADIX_TREE_INTERNAL_NODE) for i, slot in enumerate(parent.slots): yield from aux( cast(parent.type_, slot).read_(), index + (i << parent.shift.value_())) elif node: yield index, cast('void *', node)
def get_cgroup(): if len(sys.argv) == 1: return prog["cgrp_dfl_root"].cgrp task = find_task(prog, os.getpid()) with open_dir(sys.argv[1], os.O_RDONLY) as fd: file_ = fget(task, fd) kn = cast("struct kernfs_node *", file_.f_path.dentry.d_inode.i_private) return cast("struct cgroup *", kn.priv)
def aux(node: Object, index: int) -> Iterator[Tuple[int, Object]]: if _is_internal_node(node, RADIX_TREE_INTERNAL_NODE): parent = _entry_to_node(node, RADIX_TREE_INTERNAL_NODE) for i, slot in enumerate(parent.slots): yield from aux( cast(parent.type_, slot).read_(), index + (i << parent.shift.value_()), ) elif node: yield index, cast("void *", node)
def print_namespace(ns): prio_addr = ns.node.children.address_of_() for prio_node in list_for_each_entry('struct fs_node', prio_addr, 'list'): # print(prio_node) prio = cast("struct fs_prio *", prio_node) # print(prio) print_prio(prio, 0)
def _vmemmap(prog): try: # KASAN return cast('struct page *', prog['vmemmap_base']) except KeyError: # x86-64 return Object(prog, 'struct page *', value=0xffffea0000000000)
def _slab_freelist(slab: Object) -> Set[int]: # In SLAB, the freelist is an array of free object indices. freelist = cast(freelist_type, slab.freelist) return { freelist[i].value_() for i in range(slab.active, slab_cache_num) }
def radix_tree_lookup(root, index): """ .. c:function:: void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) Look up the entry at a given index in a radix tree. If it is not found, this returns a ``NULL`` object. """ node, RADIX_TREE_INTERNAL_NODE = _radix_tree_root_node(root) RADIX_TREE_MAP_MASK = node.slots.type_.length - 1 while True: if not _is_internal_node(node, RADIX_TREE_INTERNAL_NODE): break parent = _entry_to_node(node, RADIX_TREE_INTERNAL_NODE) offset = (index >> parent.shift) & RADIX_TREE_MAP_MASK node = cast(parent.type_, parent.slots[offset]).read_() return cast('void *', node)
def test_sk_tcpstate(self): with create_socket() as sock: task = find_task(self.prog, os.getpid()) file = fget(task, sock.fileno()) sk = cast("struct socket *", file.private_data).sk self.assertEqual(sk_tcpstate(sk), self.prog["TCP_CLOSE"]) sock.bind(("localhost", 0)) sock.listen() self.assertEqual(sk_tcpstate(sk), self.prog["TCP_LISTEN"]) with socket.create_connection( sock.getsockname()), sock.accept()[0] as sock2: file = fget(task, sock2.fileno()) sk = cast("struct socket *", file.private_data).sk self.assertEqual(sk_tcpstate(sk), self.prog["TCP_ESTABLISHED"])
def page_to_pfn(page): """ .. c:function:: unsigned long page_to_pfn(struct page *page) Get the page frame number (PFN) of a page. """ return cast("unsigned long", page - _vmemmap(page.prog_))
def hash(rhashtable, type, member): nodes = [] tbl = rhashtable.tbl # print('') # print("rhashtable %lx" % rhashtable.address_of_()) # print("bucket_table %lx" % tbl) # buckets = tbl.buckets # print("buckets %lx" % buckets.address_of_()) buckets = tbl.buckets size = tbl.size.value_() print("") for i in range(size): rhash_head = buckets[i] if type_exist("struct rhash_lock_head"): rhash_head = cast("struct rhash_head *", rhash_head) if rhash_head.value_() == 0: continue while True: if rhash_head.value_() & 1: break obj = container_of(rhash_head, type, member) nodes.append(obj) rhash_head = rhash_head.next return nodes
def _radix_tree_root_node(root): try: node = root.xa_head except AttributeError: return root.rnode.read_(), 1 else: return cast("struct xa_node *", node).read_(), 2
def sk_tcpstate(sk): """ .. c:function:: enum TcpState sk_tcpstate(struct sock *sk) Return the TCP protocol state of a socket. """ return cast(sk.prog_["TCP_ESTABLISHED"].type_, sk.__sk_common.skc_state)
def _vmemmap(prog): try: # KASAN return cast("struct page *", prog["vmemmap_base"]) except KeyError: # x86-64 return Object(prog, "struct page *", value=0xFFFFEA0000000000)
def print_files(files, n): for i in range(n): file = files[i] print("%2d" % i, end='\t') if file.f_op.value_() == eventpoll_fops: print_eventpoll(file) elif file.f_op.value_() == socket_file_ops: sock = Object(prog, "struct socket", address=file.private_data) sk = sock.sk if sock.ops.value_() == netlink_ops: netlink_sock = cast('struct netlink_sock *', sk) print_netlink_sock(netlink_sock) elif sock.ops.value_() == inet_dgram_ops: print_udp_sock(sk) else: print('') elif file.f_op.value_() == pipefifo_fops: print('pipefifo_fops') elif file.f_op.value_() == null_fops: print('null_fops') elif file.f_op.value_() == xfs_file_operations: print('xfs_file_operations') elif file.f_op.value_() == shmem_file_operations: print('shmem_file_operations') else: print(file.f_op)
def for_each_pid(prog_or_ns: Union[Program, Object]) -> Iterator[Object]: """ Iterate over all PIDs in a namespace. :param prog_or_ns: ``struct pid_namespace *`` to iterate over, or :class:`Program` to iterate over initial PID namespace. :return: Iterator of ``struct pid *`` objects. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns["init_pid_ns"].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, "idr"): for nr, entry in idr_for_each(ns.idr): yield cast("struct pid *", entry) else: pid_hash = prog["pid_hash"] for i in range(1 << prog["pidhash_shift"].value_()): for upid in hlist_for_each_entry("struct upid", pid_hash[i].address_of_(), "pid_chain"): if upid.ns == ns: yield container_of(upid, "struct pid", f"numbers[{int(ns.level)}]")
def find_pid(prog_or_ns, nr): """ .. c:function:: struct pid *find_pid(struct pid_namespace *ns, int nr) Return the ``struct pid *`` for the given PID number in the given namespace. If given a :class:`Program` instead, the initial PID namespace is used. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns['init_pid_ns'].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, 'idr'): return cast('struct pid *', idr_find(ns.idr, nr)) else: # We could implement pid_hashfn() and only search that bucket, but it's # different for 32-bit and 64-bit systems, and it has changed at least # once, in v4.7. Searching the whole hash table is slower but # foolproof. pid_hash = prog['pid_hash'] for i in range(1 << prog['pidhash_shift'].value_()): for upid in hlist_for_each_entry('struct upid', pid_hash[i].address_of_(), 'pid_chain'): if upid.nr == nr and upid.ns == ns: return container_of(upid, 'struct pid', f'numbers[{ns.level.value_()}]') return NULL(prog, 'struct pid *')
def for_each_pid(prog_or_ns): """ .. c:function:: for_each_pid(struct pid_namespace *ns) Iterate over all of the PIDs in the given namespace. If given a :class:`Program` instead, the initial PID namespace is used. :return: Iterator of ``struct pid *`` objects. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns['init_pid_ns'].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, 'idr'): for nr, entry in idr_for_each(ns.idr): yield cast('struct pid *', entry) else: pid_hash = prog['pid_hash'] for i in range(1 << prog['pidhash_shift'].value_()): for upid in hlist_for_each_entry('struct upid', pid_hash[i].address_of_(), 'pid_chain'): if upid.ns == ns: yield container_of(upid, 'struct pid', f'numbers[{int(ns.level)}]')
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 = self.prog.type(self.output_type) has_input = False for i in objs: has_input = True # try subclass-specified input types first, so that they can # override any other behavior try: for (_, method) in inspect.getmembers(self, inspect.ismethod): if not hasattr(method, "input_typename_handled"): continue # Cache parsed type by setting an attribute on the # function that this method is bound to (same place # the input_typename_handled attribute is set). if not hasattr(method, "input_type_handled"): method.__func__.input_type_handled = self.prog.type( method.input_typename_handled) if i.type_ == method.input_type_handled: yield from method(i) raise StopIteration except StopIteration: continue # try passthrough of output type # note, this may also be handled by subclass-specified input types if i.type_ == out_type: yield i continue # try walkers try: # pylint: disable=import-outside-toplevel # # The reason we do the above is that putting # the import at the top-level hits a cyclic # import error which pretty-much breaks # everything. We should reconsider how we # handle all our imports. from sdb.commands.walk import Walk for obj in Walk(self.prog).call([i]): yield drgn.cast(out_type, obj) continue except TypeError: pass # error raise TypeError( 'command "{}" does not handle input of type {}'.format( self.names, i.type_)) if not has_input: yield from self.no_input()
def sock_cgroup_ptr(skcd): """ .. c:function:: struct cgroup *sock_cgroup_ptr(struct sock_cgroup_data *skcd) Get the cgroup for a socket from the given ``struct sock_cgroup_data *`` (usually from ``struct sock::sk_cgrp_data``). """ return cast("struct cgroup *", skcd.val)
def sk_tcpstate(sk: Object) -> Object: """ Return the TCP protocol state of a socket. :param sk: ``struct sock *`` :return: TCP state enum value. """ return cast(sk.prog_["TCP_ESTABLISHED"].type_, sk.__sk_common.skc_state)
def test_cast_primitive_value(self): obj = Object(self.prog, "long", value=2 ** 32 + 1) self.assertIdentical(cast("int", obj), Object(self.prog, "int", value=1)) self.assertIdentical( cast("int", obj.read_()), Object(self.prog, "int", value=1) ) self.assertIdentical( cast("const int", Object(self.prog, "int", value=1)), Object(self.prog, "const int", value=1), ) self.assertRaisesRegex( TypeError, "cannot cast to 'struct point'", cast, self.point_type, Object(self.prog, "int", value=1), )
def print_namespace(ns): prio_addr = ns.node.children.address_of_() for prio_node in list_for_each_entry('struct fs_node', prio_addr, 'list'): # print(prio_node) prio = cast("struct fs_prio *", prio_node) # print(prio) if prio.prio == 1: print_prio(prio, 0) ns_addr = prio.node.children.address_of_() for ns_node in list_for_each_entry('struct fs_node', prio_addr, 'list'): ns2 = cast("struct mlx5_flow_namespace *", ns_node) prio_addr2 = ns2.node.children.address_of_() for prio_node2 in list_for_each_entry('struct fs_node', prio_addr2, 'list'): prio2 = cast("struct fs_prio *", prio_node2) print_prio(prio, 1)
def bpf_prog_for_each(prog: Program) -> Iterator[Object]: """ Iterate over all BPF programs. :return: Iterator of ``struct bpf_prog *`` objects. """ for nr, entry in idr_for_each(prog["prog_idr"]): yield cast("struct bpf_prog *", entry)
def page_to_pfn(page: Object) -> Object: """ Get the page frame number (PFN) of a page. :param page: ``struct page *`` :return: ``unsigned long`` """ return cast("unsigned long", page - page.prog_["vmemmap"])
def print_udp_sock(sk): inet_sock = cast('struct inet_sock *', sk) dest_ip = inet_sock.sk.__sk_common.skc_daddr src_ip = inet_sock.sk.__sk_common.skc_rcv_saddr dest_port = ntohs(inet_sock.sk.__sk_common.skc_dport) src_port = ntohs(inet_sock.inet_sport) print("dest_ip: %s, src_ip: %s, dest_port: %d, src_port: %d" % \ (ipv4(ntohl(dest_ip.value_())), ipv4(ntohl(src_ip.value_())), dest_port, src_port))
def sock_cgroup_ptr(skcd: Object) -> Object: """ Get the cgroup for a socket from the given ``struct sock_cgroup_data *`` (usually from ``struct sock::sk_cgrp_data``). :param skcd: ``struct sock_cgroup_data *`` :return: ``struct cgroup *`` """ return cast("struct cgroup *", skcd.val)
def print_namespace(ns): # print(ns) prio_addr = ns.node.children.address_of_() for prio_node in list_for_each_entry('struct fs_node', prio_addr, 'list'): prio = cast("struct fs_prio *", prio_node) print_prio(prio) table_addr = prio.node.children.address_of_() for table_node in list_for_each_entry('struct fs_node', table_addr, 'list'): # print(table_node.type) if table_node.type.value_() == prog['FS_TYPE_NAMESPACE'].value_(): namespace = container_of(table_node, "struct mlx5_flow_namespace", "node") # print(namespace) # print("FS_TYPE_NAMESPACE") print_namespace(namespace) else: table = cast("struct mlx5_flow_table *", table_node) print_table(table)
def _helper(self, node: drgn.Object) -> Iterable[drgn.Object]: if not node: return count = node.bth_count if node.bth_core: # alterate recursive descent on the children and generating core objects core = drgn.cast('struct zfs_btree_core *', node) for i in range(count): yield from self._helper(core.btc_children[i]) yield self._val(core.btc_elems, i) # descend the final, far-right child node yield from self._helper(core.btc_children[count]) else: # generate each object in the leaf elements leaf = drgn.cast('struct zfs_btree_leaf *', node) for i in range(count): yield self._val(leaf.btl_elems, i)