DF-0083 — OOB write into cpu_topology_nodes[MAXCPU] at boot
============================================================

## Verdict: REPRODUCED at the harness / code level (boot-time, high-CPU-only)

## Mechanism

`build_topology_tree()` (`sys/kern/subr_cpu_topology.c:115-154`) bumps a global
cursor `*last_free_node` once per allocated child node with **no upper-bound
check** against the fixed static array `cpu_topology_nodes[MAXCPU]`
(`:63`, `MAXCPU=256`):

```c
/* subr_cpu_topology.c:139-141 */
for (i = 0; i < node->child_no; i++) {
    node->child_node[i] = *last_free_node;
    (*last_free_node)++;                /* <-- unbounded */
```

The cursor starts at `root+1` (`:185`). A topology tree needs
`1 + packages + packages*cores + N` nodes (root + package + core + leaf
levels). For a single-package SMT2 system that is `2 + 3*cores = 2 + 3N/2`,
exceeding 256 at **N >= 170 logical CPUs** (386 nodes for N=256). Each node is
`2112` bytes (`sizeof(cpu_node_t)`, incl. `child_node[MAXCPU]`), so N=256
overruns by **130 nodes (~268 KB of kernel BSS)**.

**Reachability:** boot-time `SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, ...)`
(`:823`) → `build_cpu_topology()` (`:819`) → `build_topology_tree()`. The
parameters come from CPUID on each CPU — firmware/hypervisor controlled. A
malicious hypervisor presenting >=170 vCPUs in one package, or bare-metal
many-core silicon (>=256 threads), triggers it **deterministically every boot**.
Writes at `:125-132,143,152` then stomp adjacent BSS with topology pointers.

## Proof (harness)

This guest has only 6 vCPUs, so the bug **cannot fire at runtime here** (the
guest topology needs just 11-14 nodes). A faithful userspace replica of
`build_topology_tree`'s cursor logic (same struct layout, same increment — see
`topo_oob.c`) deterministically reports the overflow:

```
N(cpu)   topology   nodes_needed   vs MAXCPU(256)   OOB_writes
6        1pkg SMT2  11             ok               0
169      1pkg SMT2  255            ok               0
170      1pkg SMT2  257            OVERFLOW         1
256      1pkg SMT2  386            OVERFLOW         130
512      1pkg SMT2  770            OVERFLOW         514

Threshold: nodes_needed > 256 first at N=170 (2 + 3*85 = 257).
For N=256: 386 nodes -> 130 OOB writes (~268 KB BSS corruption at boot).
This guest: hw.ncpu=6 -> tree needs 14 nodes (no OOB); dormant here.
RESULT: OOB_CONFIRMED
```

This matches the finding's threshold (>=170) exactly.

## Impact / realism & escalation assessment

* **Not runtime-exploitable by an unprivileged local user.** The trigger is
  the boot-time CPU topology (CPUID, firmware/hypervisor controlled), not any
  syscall surface. There is no privilege boundary for a local user to cross
  here, so no `uid=0` chain applies (this is a valid hard-blocker per the
  "root-only/firmware-only reachability" category — the attacker is the
  hypervisor/firmware, not a local process).
* Realistic impact: on a >=170-CPU system the kernel corrupts its own BSS at
  boot — boot crash or potentially exploitable corruption of adjacent static
  data (the overrun is into whatever follows `cpu_topology_nodes` in BSS).
  Medium severity because it needs unusual hardware/a malicious hypervisor.
* On THIS guest (6 vCPUs) it is dormant — no runtime effect, hence
  `guest_dirty=false`.

## Fix

`fix.diff`: bound the cursor in `build_topology_tree` — when
`*last_free_node >= &cpu_topology_nodes[MAXCPU]`, print a warning and truncate
the topology (`node->child_no = i; return;`) instead of overflowing. This is
safer than the finding's suggested `panic()` (a boot panic on a legitimate
high-CPU box would be worse than a partial topology) and more robust than
enlarging the array to a guessed worst-case (the true worst case on multi-level
topologies can exceed `2*MAXCPU`, so a fixed bound check is the correct fix).
Supersedes the finding's proposal (graceful truncation instead of panic).

## Reproduce
```
cc -O2 -o topo_oob topo_oob.c     # build.sh
./topo_oob                         # run.sh — prints the overflow table
```
(The harness proves the OOB cursor primitive; runtime firing needs >=170 CPUs,
absent on this 6-vCPU guest.)
