# DF-5147 — dcons_load_buffer() trusts wire-supplied buffer geometry with an overflowable sanity check (latent, dead code in-tree)

- File: `sys/dev/misc/dcons/dcons.c:157-191`
- Severity: Info (latent — no in-tree caller)
- Confidence: certain (code defect), reachability: none in DragonFly tree today

## What the code does

`dcons_load_buffer(buf, size, sc)` adopts an externally-initialized dcons buffer
(the header fields are network-order words in the FireWire-shared buffer, i.e.
data a peer can DMA-write). It loads, with no bounds validation:

- `dc->o.size / dc->i.size` from `buf->osize[]/isize[]` (dcons.c:169-170)
- `dc->o.buf / dc->i.buf` = `(char *)buf + ntohl(buf->ooffset[]/ioffset[])`
  (dcons.c:171-172) — free-form u32 displacement from `buf`
- `dc->o.pos / dc->i.pos` = wire ptr word & `DCONS_POS_MASK` (24-bit)
  (dcons.c:175-176)

The only sanity check (dcons.c:184-186):

```c
s = DCONS_HEADER_SIZE;
for (port ...) { ...; s += dc->o.size + dc->i.size; }
if (s > size) return (-1);
```

fails to constrain the geometry:

1. **Integer wrap**: `s` is `int`, `dc->o.size + dc->i.size` is u32 arithmetic.
   `osize[0]=isize[0]=0x80000000` (and likewise for port 1) makes every
   per-port addend 0 → `s` stays ~56 → passes while each channel claims a
   2 GB size.
2. **Offsets never checked**: `ooffset/ioffset` can be any u32 (up to +4 GB
   from `buf`); the check sums only sizes.
3. **pos never checked**: `pos` up to 0xFFFFFF is accepted against whatever
   size was loaded.

## Consequence if ever wired to a peer-controlled buffer

- `dcons_putc()` writes `ch->buf[ch->pos] = c` (dcons.c:118) →
  **single-byte kernel write at peer-chosen displacement (buf + u32 offset +
  24-bit pos)**, content = console output stream; pos auto-increments →
  linear scribing.
- `dcons_checkc()` reads `ch->buf[ch->pos]` (dcons.c:101) and the byte is
  delivered to the console/gdb reader → **kernel memory disclosure to the
  peer** (same sink as the known, live DF-2142 read bug, but reachable via
  baked-in geometry rather than the live wire ptr word).

## Why Info / no PoC

`rg` over the whole `sys/` tree finds **no caller** of `dcons_load_buffer`
(only the prototype at dcons.h:105 and the definition). The live attach path
(`dcons_os.c:453/464`) always uses `dcons_init()`, which computes all geometry
from the fixed `DCONS_BUF_SIZE` (16 KB). The function is, however, compiled
into the loadable `dcons` module and globally exported, and the family's
threat model (FireWire peers DMA-writing the published buffer — DF-2142,
DF-5116..5118) makes it a live trap if any future change re-wires it.

Per the audit contract, Low/Info hardening findings are not PoC-verified;
this pack records the defect and the hardening fix only.

## Reproduce (analysis-level)

Read-only; the decisive references are:
- `sys/dev/misc/dcons/dcons.c:169-186` (unvalidated loads + wraparound-able check)
- `sys/dev/misc/dcons/dcons.c:101` / `:118` (read/write sinks at `ch->buf[ch->pos]`)
- caller search: `rg -n 'dcons_load_buffer' sys/` → only dcons.c:158 and dcons.h:105

## Fix

Apply `fix.diff` in this directory (bounds + 64-bit accumulation + pos<size).
