⬢ DragonFlyBSD Kernel Audit
DF-5320 / ttys_diff.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""DF-5320 evidence: decode sysctl kern.ttys dumps and show that the ucom
tty's driver callbacks (t_oproc/t_param/t_stop = ucom.ko text) and t_sc
(kernel-heap pointer into the driver softc) persist UNCHANGED after the USB
device detached and the softc was kfree'd.

New ttys are TAILQ_INSERT_HEAD'ed (ttyregister), and sysctl_kern_ttys walks
from the head => the most recently registered tty is record 0.  The ucom
tty is therefore record 0 of the live dump (registered at ucom attach) and
record 0 of the dead dump (ttys are never freed / unregistered).
"""
import struct, sys

TS = 376  # sizeof(struct tty) on X86_64_GENERIC 6.5-DEVELOPMENT
          # (live-base = 376 bytes exactly; see README)

def records(path):
    b = open(path, 'rb').read()
    assert len(b) % TS == 0, (path, len(b))
    return [b[i:i+TS] for i in range(0, len(b), TS)]

def ptrs(r):
    out = {}
    for off in range(0, TS-7, 8):
        v = struct.unpack('<Q', r[off:off+8])[0]
        if v >= 0xffffffff80000000 or (0xfffff80000000000 <= v < 0xffffffff00000000):
            out[off] = v
    return out

base, live, dead = (records(p) for p in sys.argv[1:4])
print(f"records: base={len(base)} live={len(live)} dead={len(dead)}")
print(f"sizeof(struct tty) assumed: {TS}  (4136-3760 == 376)")
u_live, u_dead = ptrs(live[0]), ptrs(dead[0])
print("live[0] (ucom tty, head-inserted at attach):")
for k in sorted(u_live): print(f"  +0x{k:03x}: 0x{u_live[k]:016x}")
print("dead[0] (same tty after USB detach + softc kfree):")
for k in sorted(u_dead): print(f"  +0x{k:03x}: 0x{u_dead[k]:016x}")
same = {k for k in u_live if k in u_dead and u_live[k] == u_dead[k]}
print("fields identical across detach:", [hex(k) for k in sorted(same)])
cb = [k for k in same if u_live[k] >= 0xffffffff80000000]
print("=> driver TEXT pointers retained after detach (t_oproc/t_param/t_stop):",
      [f"+0x{k:x}=0x{u_live[k]:x}" for k in sorted(cb)])
sc = [k for k in same if 0xfffff80000000000 <= u_live[k] < 0xffffffff00000000]
print("=> HEAP pointer retained after detach (t_sc -> freed ucom_softc):",
      [f"+0x{k:x}=0x{u_live[k]:x}" for k in sorted(sc)])