# DF-5779 — `seq_puts()` shim passes its data string as the kernel printf FORMAT

## What / where

* Shim unit under audit: `sys/dev/drm/linux_printf.c` (seq_printf; known
  finding DF-2224 covers its va_list forwarding and is NOT re-reported).
* The remaining shim in the same unit, declared in
  `sys/dev/drm/include/linux/seq_file.h:44`:

```c
#define seq_puts(m, str)	ksnprintf((m)->buf, (m)->size, str)
```

`str` — which the Linux seq_puts() API defines as pure DATA (never
interpreted) — sits in ksnprintf's `__printflike(3,4)` format parameter.

## Build

On the host, from this directory:

    ../../../dfbsd-qemu/vm.sh up          # if down
    ./build.sh                           # transfers sources (base64 via run_root),
                                         # builds seqputs_rig.ko + seqputs_rigc.ko in-guest

Manual equivalent (in guest /tmp/rig): `make SYSDIR=/usr/src/sys WERROR=""`
with `KMOD=seqputs_rig SRCS=seqputs_rig.c` (DragonFly bsd.kmod.mk).

## Run

    ./run.sh

1. `kldload /tmp/rig/seqputs_rig.ko`  — rigs A+B (safe):
   * A: controlled `%n` → `seqputs_rig A: %n wrote 2`  (write conversion LIVE)
   * B: `seq_puts(&m, "conn=DP-1 link=%d ok=100%%\n")` →
        `link=0` — `%d` interpreted against a NEVER-passed vararg
2. `kldload /tmp/rig/seqputs_rigc.ko` — rig C: same macro, data with nine
   `%s` → **kernel panic** (wild strlen), see panic.txt.

## Expected output (baseline, vulnerable shim)

    seqputs_rig: loaded
    seqputs_rig A: %n wrote 2 (2 == LIVE write conv)
    seqputs_rig B: seq_puts(m,data) produced: 'conn=DP-1 link=0 ok=100%
    '
    seqputs_rig B: Linux contract would print the literal data string verbatim (no interpretation)
    seqputs_rigc: firing %s-walk on unset varargs...
    Fatal trap 12: page fault while in kernel mode
    fault virtual address = 0x1a640
    ...
    Stopped at      strlen: cmpb $0,(%rdi)

## Fix validation

`seqputs_rigfix.c` compiles the FIXED shim (`ksnprintf(buf, size, "%s", str)`)
and replays the same inputs:

    rigfix B: 'conn=DP-1 link=%d ok=100%%
    '                      <- verbatim, no interpretation
    rigfix C: 'm=%s s=%s x=%s y=%s z=%s w=%s v=%s u=%s t=%s
    '                      <- the string that PANICKED baseline: now verbatim
    rigfix N: 'w=%d write=%n addr=%p
    '                      <- %n/%d/%p in data stay literal
    rigfix: DONE verbatim, guest alive

## Reachability (honest scope)

In-tree TODAY: every seq_puts/seq_printf call site is a debugfs show
callback, and DragonFly defines neither CONFIG_DEBUG_FS nor any debugfs
dispatch (`sys/dev/drm/drm_internal.h:139-143` stub; `drm_debugfs.c` not in
sys/conf/files; no `struct seq_file` instantiation exists anywhere). All
in-tree `seq_puts` arguments are `%`-free literals. The bug is therefore
LATENT: it converts any future ported caller that passes a dynamic string
(upstream Linux drivers routinely pass EDID/connector-derived names —
attacker-influenced bytes from a malicious monitor) into a kernel
format-string primitive: `%n` = write-through-pointer (live, subr_prf.c:688),
`%s` = wild read (panic, proven), `%d/%x/%p` = kernel slot leak into the
seq buffer.

Severity Low (latent hardening / contract divergence). Proof is nonetheless
complete: disassembly of the shipped drm.ko (disasm.txt) + live-kernel
differential (run.log, run_c.log, panic.txt, fix_run.log).
