# DF-0670 — VERDICT

## Verdict: REPRODUCED at source level; OOB read code path executed; 0 useful bytes leaked in this PoC run

The missing `sopt_valsize` validation in `ip_fw3_ctl_x` is **real**,
and the OOB read code path **is executed** by the PoC
(`setsockopt(...IP_FW_X, IP_FW_TABLE_CREATE, valsize=4)` returns
success). However, on this guest's slab state, the leaked bytes are
all zero — **no actual info leak was observed**. The bug exists; the
practical exploit ceiling (kernel-pointer leak via `IP_FW_TABLE_LIST`)
would require heap grooming to populate the adjacent slab chunks with
non-zero data.

This is a "real bug, low practical impact on this guest" result. The
honest verdict is **status=reproduced, impact=none** for the live PoC
run (no leak marker), with the source-level OOB read confirmed.

## Mechanism (cited path:line)

1. **`sys/kern/uipc_syscalls.c:1257` `sys_setsockopt`** allocates the
   kernel buffer with `kmalloc(sopt.sopt_valsize, M_TEMP, M_WAITOK)` —
   the buffer is exactly user-supplied `valsize` bytes (rounded up by
   the slab allocator). User controls `valsize`.
2. **`sys/netinet/raw_ip.c:385-386` `rip_ctloutput`** dispatches
   `IP_FW_X` to `ip_fw3_sockopt` → `ip_fw3_ctl_x` (requires SOCK_RAW,
   i.e. root). (`SOCK_DGRAM` returns `ENOPROTOOPT` — confirmed by
   first PoC attempt.)
3. **`sys/net/ipfw3/ip_fw3.c:1039-1046` `ip_fw3_ctl_x`** strips the
   4-byte `ip_fw_x_header`:
   ```c
   sopt->sopt_valsize -= sizeof(ip_fw_x_header);   // user valsize 4 -> 0
   bcopy(++x_header, sopt->sopt_val, sopt->sopt_valsize);   // bcopy 0 bytes
   return ip_fw3_ctl(sopt);
   ```
   **No check that the remaining `sopt_valsize` is large enough for
   the dispatched handler's expected struct.**
4. **`sys/net/ipfw3_basic/ip_fw3_table.c:88-98` `table_create_dispatch`**
   reads fields past the user-supplied buffer:
   ```c
   ioc_table = tbmsg->ioc_table;          // = sopt->sopt_val (kmalloc(4))
   int id = ioc_table->id;                // offset 0..3  (within chunk)
   table_ctx->type = ioc_table->type;     // offset 4..7  (within slab chunk but past user data)
   strlcpy(table_ctx->name, ioc_table->name, IPFW_TABLE_NAME_LEN);
                                         // offset 12..43 (past slab chunk -> adjacent slab)
   ```
5. The leaked bytes are stored in `table_ctx->name` and `table_ctx->type`.
6. **`sys/net/ipfw3_basic/ip_fw3_table.c:274-296` `ip_fw3_ctl_table_list`**
   dumps all 32 `table_ctx` (including any leaked `name`/`type`) back
   to user space via `getsockopt(IP_FW_X, IP_FW_TABLE_LIST)` — that's
   the exfiltration primitive.

## Reproduction (root-only)

PoC: `df0670_ipfw3_oob.c` —
1. `setsockopt(SOCK_RAW, IPPROTO_IP, IP_FW_X, {x_header.opcode=IP_FW_TABLE_CREATE, id=0}, valsize=4)` — triggers OOB read into `table_ctx[0].type` and `table_ctx[0].name`.
2. `getsockopt(SOCK_RAW, IPPROTO_IP, IP_FW_X, {x_header.opcode=IP_FW_TABLE_LIST}, ...)` — reads back `table_ctx[0]`.

Run output (3 iterations) in `run.log` shows `type=0x00000000` and
`name` all-zero. The OOB read IS happening but the slab state on this
guest returns zeroed bytes for the relevant adjacent chunks.

## Privilege / threat model

- **Trigger requires root** (raw socket for `IP_FW_X`). Matches CVSS `PR:H`.
- The exfiltrated data would be: up to 4 bytes (type) + up to 31 bytes
  (name, NUL-truncated) of kernel heap residue per affected slot.
- With heap grooming to populate adjacent kmalloc-8 chunks with live
  kernel pointers (e.g. by interleaving controlled small allocations),
  the leak would expose kernel heap addresses — defeating KASLR (which
  is OFF on this guest anyway) and potentially revealing adjacent
  victim-object content. On this guest, KASLR is already OFF so the
  practical ceiling of the leak is reduced.

## Recommended fix

`fix.diff` adds `sopt_valsize` validation in
`ip_fw3_ctl_table_sockopt` (rejecting any setsockopt whose post-strip
valsize is smaller than `sizeof(struct ipfw_ioc_table)`). This
**matches the finding proposal**.

Note: the bug is root-only and the leak is hard to demonstrate
practically on this guest's slab state. The fix is still correct
defense-in-depth.
