# DF-5665 — FRAMEUP unwinder heuristic accepts non-monotonic, non-aligned, non-contained frame pointers (hardening)

File audited (pass 2): `sys/cpu/x86_64/misc/ktr.c` (95 LOC, sha256
`a86d4a65a53feb7ba6324f018049986eac1c99c13224fa1b73a8a58a3754e2ee`).

## What the finding is

`cpu_ktr_caller()` (sys/cpu/x86_64/misc/ktr.c:78-95) walks the saved-%rbp
chain of the running kernel stack to record two caller return addresses into
the per-CPU KTR ring entry.  Each step goes through `FRAMEUP()`
(sys/cpu/x86_64/misc/ktr.c:60-70), whose only validation is

```c
if (((uintptr_t)newframeptr ^ (uintptr_t)frameptr) & ~16383ULL)
        newframeptr = frameptr;      /* reject: differs outside low 14 bits */
```

i.e. the candidate next frame must lie within the same ~16 KiB neighborhood
of the current frame pointer.  There is **no** check that the chain moves
monotonically *up* the stack, that the pointer is aligned, or that it stays
inside the current kernel stack.  A stale kernel `%rbp` (left in a
saved-rbp slot by an assembly stub / trampoline that does not maintain frame
pointers — e.g. fork/execve trampoline context, hand-written exception or IPI
entry paths) that happens to land within the ±16 KiB window is accepted, and
the *next* step then reads `frameptr[0]` / `frameptr[1]`
(sys/cpu/x86_64/misc/ktr.c:66,92,94) at that address — which can be unmapped
kernel VA (e.g. an adjacent, freed per-thread kstack) → kernel page fault →
**panic**.

## Why this is only Info

- **No attacker control of the walked value.** The user-controlled-%rbp
  route (interrupt/syscall taken *from user mode* leaves the user's `%rbp`
  in a saved slot) is killed *by construction*: the acceptance baseline is
  always a genuine kernel-stack address obtained from `cpu_ktr_caller`'s own
  frame (ktr.c:86-88), and a user-space candidate (< 0x0000800000000000)
  XOR a kernel kstack address (>= 0xffff800000000000) always has high bits
  set → rejected every step. The walk can never cross into user space, and
  every read address is within ±16 KiB of an address already proven
  readable.
- **Panic-only ceiling** even in the stale-kernel-rbp case: the function
  only performs reads (`frameptr[0]`, `frameptr[1]`) and two writes into the
  pre-allocated ring entry (ktr.c:92,94).
- **Debug-only facility**: compiled only under `options KTR`
  (`sys/platform/pc64/conf/files:110` — `cpu/x86_64/misc/ktr.c optional ktr`;
  `sys/conf/files:1377` — `kern/kern_ktr.c optional ktr`). The stock
  X86_64_GENERIC kernel does **not** include it (live-verified below), and
  the only caller is `ktr_finish_write_entry()` gated by `ktr_stacktrace`
  (`sys/kern/kern_ktr.c:531-532`).
- No demonstrated trigger from any real kernel call path; the precondition
  (KTR_LOG site within 2 frames of a stub that leaves a *kernel* rbp within
  16 KiB of the live frame) is contrived.

## Why no PoC was run (status: untested)

Per the review contract, PoC verification is waived for Info-severity
hardening findings. Additionally the target code is absent from the audit
guest's kernel, so the path cannot be exercised without a custom
`options KTR` kernel build, which is not warranted for a speculative,
panic-only, debug-facility hardening note.

Live guest evidence collected anyway (see env.txt):

```
$ sysctl debug.ktr
sysctl: unknown oid 'debug.ktr'          # KTR not compiled into X86_64_GENERIC
$ sysctl -a | grep -c "debug.ktr"
0
$ ls -l /dev/mem /dev/kmem
crw-r-----  1 root kmem 13, 0x00000001 ... /dev/kmem    # ktrdump(1) path is root-only
crw-r-----  1 root kmem 13, 0x00000000 ... /dev/mem
```

## Fix

`fix.diff` (git-apply checked against `sys/`): require the candidate frame
to be strictly above the current frame and 8-byte aligned, in addition to
the existing ~16 KiB proximity test.  Genuine x86-64 frame chains always
grow upwards, so no legitimate walk is affected.

## Reproduce the analysis (no build required)

1. `rg -n "cpu_ktr_caller" sys/` → only caller `sys/kern/kern_ktr.c:532`.
2. Read `sys/cpu/x86_64/misc/ktr.c:60-95` (FRAMEUP + walk).
3. `grep -n "ktr" sys/platform/pc64/conf/files` → `optional ktr`.
4. On the guest: `sysctl debug.ktr` → unknown oid (facility absent).
