# DF-5470 — DDB backtracer frame walk trusts stack content as frame pointers (INKERNEL() defined, never used); walk escapes into user/mapped/unmapped memory

**File:** `sys/platform/pc64/x86_64/db_trace.c` (pass 2)
**Severity:** Medium · **Bucket:** base:ddb / anti-forensics
**Status:** REPRODUCED on stock guest (see `run.log`, `leak_flood_sample.txt`)

## What

`db_stack_trace_cmd()`/`db_nextframe()` walk the frame-pointer chain with a
single sanity check — `(unsigned long)*fp < PAGE_SIZE` (db_trace.c:227). The
file *defines* `INKERNEL(va)` at db_trace.c:131 and **never uses it** — the
kernel-space validation that the ancestor FreeBSD code performs was dropped
in the x86-64 port. Adopted frames (`rbp` read from stack/memory content)
can therefore be:

* user-space addresses (the panicking/traced process's own mappings are
  live during panic and DDB traces), or
* arbitrary mapped kernel addresses (heap, other stacks), or
* unmapped addresses (→ fault inside `db_read_bytes`, see **DF-5471**).

The convenience scan for `trace <addr>` (db_trace.c:336-352) likewise reads
8-byte words at `addr..addr+4088` with no validation of `addr`.

## Proof (this pack)

A KLD harness (`src/df_trace_harness.c`) calls the **production**
`db_stack_trace_cmd()` from a live syscall context. An unprivileged user
(`uid 1001`, `src/user_chain.c`) sprays 40000 fake frames in its own address
space at `0x60000000`; the walk runs in that process's context (identical to
the context of a panic inside the attacker's syscall — `print_backtrace()`
runs on the panicking thread with its page table active):

* 30000 frames walked **through user memory**, one msgbuf line each;
* 22754+ lines (~1.07MB ≥ `MSGBUF_SIZE`=1MB) flood the message buffer;
* the walk's own start line **and the entire pre-existing msgbuf content**
  (boot context; on a real system: the panic message, trap registers, and
  the useful head of the backtrace) are **evicted** — forensic evidence
  destroyed, output fully attacker-steered.

`fix.diff` restores the check: frames must satisfy `INKERNEL()` and move
strictly up the stack; `trace <addr>` refuses non-kernel addresses.
Verified on a rebuilt kernel: the user-VA walk is refused
("0x60000000 is not in kernel space"), zero flood lines, msgbuf intact.

## Build / run

```
./build.sh          # push src/ to guest, build KLD + user_chain, load
./run.sh            # baseline sequence with expected outputs inline
```

Requires the `dfbsd-qemu` guest (stock `X86_64_GENERIC`: DDB, DDB_TRACE,
INVARIANTS). For the fix validation: apply `fix.diff` in the guest
`/usr/src`, `make nativekernel KERNCONF=X86_64_GENERIC && make installkernel`,
reboot, re-run `run.sh`.
