# VERDICT — DF-1120

## Verdict: REPRODUCED (primitive) / NOT REACHABLE on guest (HW-gated)

The cited bug is **real and confirmed** by source trace + userspace
allocator replica. It is a **latent heap-corruption** primitive: reachable
only when a SCSI sym HBA is attached and two buddy-adjacent 4096-byte
allocations are freed in succession, which this audit guest has no HW to
trigger.

## Mechanism (confirmed path:line)
1. `___sym_mfree` (`sys/dev/disk/sym/sym_hipd.c:454-497`) frees a block.
   The block size walks up via `while (size > s) { s <<= 1; ++i; }` to
   find its buddy bucket. For a 4096-byte block: `i` reaches 8, `s` = 4096
   = `MEMO_CLUSTER_SIZE`.
2. The merge loop at `:476-497` is `while (1)` with no upper bound on `i`.
   The `MEMO_FREE_UNUSED` early-exit (`:477-481`) is wrapped in
   `#if 0 ... #endif` at `:366-368`, so it does **not** compile in.
3. When a buddy is found at level `i`, the loop unlinks it, sets
   `a = a & b; s <<= 1; ++i;` and continues. For two buddy-adjacent
   4096-byte blocks: merge at `i=8` succeeds (`s` becomes 8192, `i=9`).
4. Next loop top: `q = &h[i]` = `&h[9]`. But `h[]` is declared at `:408`
   as `h[MEMO_CLUSTER_SHIFT - MEMO_SHIFT + 1]` = `h[9]` (indices 0–8
   valid). `h[9]` is an **8-byte OOB write** past `m_pool_s.h[]` into
   whatever follows in kernel heap.

## Reproduction (userspace harness)
The harness allocates two page-aligned buddy-adjacent 4096-byte regions,
replicates the exact merge-loop bookkeeping with `h[9]` + canary layout,
and demonstrates the OOB write:
```
*** OOB WRITE at h[9] -- canary corrupted ***
    canary before = 0xdeadbeefcafebabe
    canary after  = 0x00000008004ae000  (OVERWRITTEN)
```

## Impact ceiling
- **Per-trigger**: 8-byte heap write at a fixed offset past `m_pool_s`.
  Content is a kernel heap address (the merged buddy pointer) — partially
  attacker-influenced via prior allocation patterns.
- **Slab-bucket**: `m_pool_s` is `kmalloc`'d from `M_DEVBUF`. The 8 bytes
  after `h[8]` land in the adjacent heap object — type depends on what was
  allocated next. On the default GENERIC kernel (INVARIANTS ON), the
  `WEIRD_ADDR` (0xdeadc0de) poisoning and slab magic checks would likely
  catch cross-type corruption and panic before exploitation lands; this is
  a DoS on GENERIC, not a clean `uid=0`.
- **Privilege**: requires the ability to cause sym driver attach/detach
  cycles (hot-pluggable PCI SCSI HBA or `kldunload sym`). The
  unprivileged path is narrow (requires operator on the device node).
- **Realistic**: SCSI sym (SYM53C8XX) is legacy hardware.

## Fix
`fix.diff` adds an unconditional upper-bound guard at the top of the merge
loop:
```c
if (s >= MEMO_CLUSTER_SIZE)
    break;
```
This fires before any `h[i]` access when `i` would exceed the array,
regardless of whether `MEMO_FREE_UNUSED` is compiled in. Minimal,
targeted, one logical change.

Validated: `sym.ko` builds with `RC=0` after applying the fix.

## Fix validation
- Patch applies cleanly: `Hunk #1 succeeded at 474`.
- `make` in `sys/dev/disk/sym/` → `sym.ko` linked, `RC=0`.
- Cannot boot-test (no SCSI sym HBA); `fix_status: not_testable`.

## PoC changes
- `harness.c` written from scratch. Uses `posix_memalign` for real
  page-aligned buddy addresses so the linked-list manipulation is valid;
  places a canary after `h[8]` to demonstrate the OOB write.
