mirror of https://github.com/pwndbg/pwndbg.git
support for printing DMA buf information (#3312)
* support for printing DMA buffer information * supported detection of `CONFIG_DEBUG_FS` * changes based on commentspull/3320/head^2
parent
c2c31fc01e
commit
581a7df9e4
@ -0,0 +1,17 @@
|
||||
<!-- THIS PART OF THIS FILE IS AUTOGENERATED. DO NOT MODIFY IT. See scripts/generate-docs.sh -->
|
||||
# kdmabuf
|
||||
|
||||
```text
|
||||
usage: kdmabuf [-h]
|
||||
|
||||
```
|
||||
|
||||
Prints DMA buf info
|
||||
### Optional arguments
|
||||
|
||||
|Short|Long|Help|
|
||||
| :--- | :--- | :--- |
|
||||
|-h|--help|show this help message and exit|
|
||||
|
||||
<!-- END OF AUTOGENERATED PART. Do not modify this line or the line below, they mark the end of the auto-generated part of the file. If you want to extend the documentation in a way which cannot easily be done by adding to the command help description, write below the following line. -->
|
||||
<!-- ------------\>8---- ----\>8---- ----\>8------------ -->
|
||||
@ -0,0 +1,107 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import pwndbg.aglib.kernel
|
||||
|
||||
|
||||
def find_dmabuf_offsets(dmabuf) -> Tuple[int, int, int]:
|
||||
MAX = 0x30
|
||||
sg_table_off, exp_name_off, list_node_off = None, None, None
|
||||
ptrsize = pwndbg.aglib.arch.ptrsize
|
||||
heap_buffer = pwndbg.aglib.memory.read_pointer_width(dmabuf + 2 * ptrsize)
|
||||
for i in range(1, MAX):
|
||||
# see load_dmabuf_typeinfo (struct dma_buf) for an explanation
|
||||
# this loop is searching the `size` field from `list_node`
|
||||
size = pwndbg.aglib.memory.read_pointer_width(dmabuf - (i + 5) * ptrsize)
|
||||
file = pwndbg.aglib.memory.read_pointer_width(dmabuf - (i + 4) * ptrsize)
|
||||
attachments_prev = pwndbg.aglib.memory.read_pointer_width(dmabuf - (i + 3) * ptrsize)
|
||||
attachments_next = pwndbg.aglib.memory.read_pointer_width(dmabuf - (i + 2) * ptrsize)
|
||||
ops = pwndbg.aglib.memory.read_pointer_width(dmabuf - (i + 1) * ptrsize)
|
||||
vmapping_counter = pwndbg.aglib.memory.read_pointer_width(dmabuf - i * ptrsize)
|
||||
if pwndbg.aglib.memory.is_kernel(size):
|
||||
continue
|
||||
if not pwndbg.aglib.memory.is_kernel(file):
|
||||
continue
|
||||
if not (
|
||||
pwndbg.aglib.memory.is_kernel(attachments_next)
|
||||
and pwndbg.aglib.memory.is_kernel(attachments_prev)
|
||||
):
|
||||
continue
|
||||
if not pwndbg.aglib.memory.is_kernel(ops):
|
||||
continue
|
||||
if pwndbg.aglib.memory.is_kernel(vmapping_counter):
|
||||
continue
|
||||
# (i + 5) * ptrsize is the distance from the `size` to `list_node`
|
||||
list_node_off = (i + 5) * ptrsize
|
||||
break
|
||||
assert list_node_off is not None, "cannot determine the offset of list_node"
|
||||
dmabuf -= list_node_off
|
||||
for i in range(5, MAX):
|
||||
ptr = pwndbg.aglib.memory.read_pointer_width(dmabuf + i * ptrsize)
|
||||
try:
|
||||
if len(pwndbg.aglib.memory.string(ptr).decode()) == 0:
|
||||
continue
|
||||
except Exception:
|
||||
continue
|
||||
exp_name_off = i * ptrsize
|
||||
break
|
||||
assert exp_name_off is not None, "cannot determine the offset of exp_name"
|
||||
sz = pwndbg.aglib.memory.read_pointer_width(dmabuf)
|
||||
for i in range(MAX):
|
||||
if pwndbg.aglib.memory.read_pointer_width(heap_buffer + i * ptrsize) == sz:
|
||||
sg_table_off = (i + 1) * ptrsize
|
||||
break
|
||||
assert sg_table_off is not None, "cannot determine the offset of sg_table"
|
||||
return sg_table_off, exp_name_off, list_node_off
|
||||
|
||||
|
||||
def load_dmabuf_typeinfo(first_dmabuf: int):
|
||||
# reaching here means priv exists
|
||||
if pwndbg.aglib.typeinfo.lookup_types("struct dma_buf") is not None:
|
||||
return
|
||||
sg_table_off, exp_name_off, list_node_off = find_dmabuf_offsets(first_dmabuf)
|
||||
result = pwndbg.aglib.kernel.symbol.COMMON_TYPES
|
||||
result += f"""
|
||||
typedef unsigned long dma_addr_t;
|
||||
struct scatterlist {{
|
||||
unsigned long page_link; // either to a page or scatterlist
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
dma_addr_t dma_address;
|
||||
/*** potentially has 8 more bytes
|
||||
#ifdef CONFIG_NEED_SG_DMA_LENGTH
|
||||
unsigned int dma_length;
|
||||
#endif
|
||||
#ifdef CONFIG_NEED_SG_DMA_FLAGS
|
||||
unsigned int dma_flags;
|
||||
#endif
|
||||
***/
|
||||
}};
|
||||
struct sg_table {{
|
||||
struct scatterlist *sgl; /* the list */
|
||||
unsigned int nents; /* number of mapped entries */
|
||||
unsigned int orig_nents; /* original size of list */
|
||||
}};
|
||||
struct system_heap_buffer {{
|
||||
char _pad[{sg_table_off}];
|
||||
struct sg_table sg_table;
|
||||
/* rest of the fields are irrelevant */
|
||||
}};
|
||||
struct dma_buf {{
|
||||
size_t size; // (i + 5) is here
|
||||
void *file;
|
||||
struct list_head attachments;
|
||||
void *ops; // const struct dma_buf_ops *
|
||||
unsigned vmapping_counter;
|
||||
char _pad1[{exp_name_off - pwndbg.aglib.arch.ptrsize * 6}];
|
||||
const char *exp_name;
|
||||
const char *name;
|
||||
char _pad2[{list_node_off - exp_name_off - pwndbg.aglib.arch.ptrsize * 2}];
|
||||
struct list_head list_node;
|
||||
struct system_heap_buffer *priv; // treating the voidptr as system_heap_buffer
|
||||
/* rest of the fields are irrelevant */
|
||||
}};
|
||||
"""
|
||||
header_file_path = pwndbg.commands.cymbol.create_temp_header_file(result)
|
||||
pwndbg.commands.cymbol.add_structure_from_header(header_file_path, "dmabuf_structs", True)
|
||||
@ -0,0 +1,96 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
|
||||
import pwndbg.aglib.kernel
|
||||
import pwndbg.aglib.kernel.dmabuf
|
||||
import pwndbg.color.message as M
|
||||
from pwndbg.aglib.kernel.macros import for_each_entry
|
||||
from pwndbg.commands import CommandCategory
|
||||
from pwndbg.lib.exception import IndentContextManager
|
||||
|
||||
SG_CHAIN = 0x1
|
||||
SG_END = 0x2
|
||||
|
||||
parser = argparse.ArgumentParser(description="Prints DMA buf info")
|
||||
|
||||
|
||||
def print_dmabuf(dmabuf, idx, indent):
|
||||
size = int(dmabuf["size"])
|
||||
file = int(dmabuf["file"])
|
||||
exp_name = pwndbg.aglib.memory.string(int(dmabuf["exp_name"])).decode()
|
||||
name = int(dmabuf["name"])
|
||||
desc = indent.prefix(f"[0x{idx:02x}] DMA-buf") + f" @ {indent.addr_hex(int(dmabuf))}"
|
||||
desc += f" [size: {indent.aux_hex(size)}, file: {indent.aux_hex(file)}, exporter: {exp_name}]"
|
||||
if name != 0:
|
||||
desc += f" (name: {pwndbg.aglib.memory.string(name)})"
|
||||
indent.print(desc)
|
||||
|
||||
|
||||
def print_sgl(sgl, indent):
|
||||
sgl_type_len = pwndbg.aglib.typeinfo.lookup_types("struct scatterlist").sizeof
|
||||
next_sgl = int(sgl)
|
||||
idx = 0
|
||||
while True:
|
||||
sgl = pwndbg.aglib.memory.get_typed_pointer("struct scatterlist", next_sgl)
|
||||
page_link = int(sgl["page_link"])
|
||||
page = page_link & ~(SG_CHAIN | SG_END)
|
||||
if page_link & SG_CHAIN:
|
||||
next_sgl = page
|
||||
continue
|
||||
virt = pwndbg.aglib.kernel.page_to_virt(page)
|
||||
phys = pwndbg.aglib.kernel.virt_to_phys(virt)
|
||||
offset = int(sgl["offset"])
|
||||
length = int(sgl["length"])
|
||||
desc = "- " + indent.prefix(f"[0x{idx:02x}] {indent.addr_hex(virt)}")
|
||||
desc += f" (len: {indent.aux_hex(length)}, off: {indent.aux_hex(offset)}) [page: {indent.aux_hex(page)}, phys: {indent.aux_hex(phys)}]"
|
||||
idx += 1
|
||||
indent.print(desc)
|
||||
if page_link & SG_END:
|
||||
break
|
||||
next_sgl += sgl_type_len
|
||||
tmp = pwndbg.aglib.memory.read_pointer_width(next_sgl)
|
||||
if not pwndbg.aglib.memory.is_kernel(tmp):
|
||||
next_sgl += pwndbg.aglib.arch.ptrsize
|
||||
tmp = pwndbg.aglib.memory.read_pointer_width(next_sgl)
|
||||
if not pwndbg.aglib.memory.is_kernel(tmp):
|
||||
break
|
||||
|
||||
|
||||
# adapted from https://github.com/bata24/gef/tree/dev
|
||||
@pwndbg.commands.Command(parser, category=CommandCategory.KERNEL)
|
||||
@pwndbg.commands.OnlyWhenQemuKernel
|
||||
@pwndbg.commands.OnlyWithKernelDebugSymbols
|
||||
@pwndbg.commands.OnlyWhenPagingEnabled
|
||||
def kdmabuf():
|
||||
db_name = "db_list"
|
||||
if pwndbg.aglib.kernel.krelease() >= (6, 10):
|
||||
db_name = "debugfs_list"
|
||||
if "CONFIG_DEBUG_FS" not in pwndbg.aglib.kernel.kconfig():
|
||||
print(M.warn("dma_buf->priv does not exist"))
|
||||
db_list = pwndbg.aglib.kernel.db_list()
|
||||
if db_list is None:
|
||||
print(M.warn(f"{db_name} not found"))
|
||||
return
|
||||
db_list = pwndbg.aglib.memory.get_typed_pointer("struct list_head", db_list)
|
||||
if int(db_list) == int(db_list["next"]):
|
||||
print(M.warn(f"{db_name} ({hex(int(db_list))}) is empty"))
|
||||
return
|
||||
indent = IndentContextManager()
|
||||
if not pwndbg.aglib.kernel.has_debug_info():
|
||||
pwndbg.aglib.kernel.dmabuf.load_dmabuf_typeinfo(int(db_list["next"]))
|
||||
for idx, e in enumerate(for_each_entry(db_list.dereference(), "struct dma_buf", "list_node")):
|
||||
print_dmabuf(e, idx, indent)
|
||||
priv = e["priv"]
|
||||
if not pwndbg.aglib.memory.is_kernel(int(priv)):
|
||||
indent.print(M.warn("(no entries)"))
|
||||
continue
|
||||
nents = int(priv["sg_table"]["nents"])
|
||||
if nents == 0:
|
||||
indent.print(M.warn("(no entries)"))
|
||||
continue
|
||||
with indent:
|
||||
desc = indent.prefix("system_heap_buffer")
|
||||
desc += f" @ {indent.addr_hex(int(priv))} [nents: {indent.aux_hex(nents)}]"
|
||||
indent.print(desc)
|
||||
print_sgl(priv["sg_table"]["sgl"], indent)
|
||||
Loading…
Reference in new issue