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