# DF-0941 — Kernel divide-by-zero panic in `vm_get_pg_color` via writable CPU-topology sysctls

## Summary

`vm_get_pg_color()` divides `PQ_L2_SIZE` (1024) by the CPU-topology counts
`cpu_topology_phys_ids`, `cpu_topology_core_ids`, and `cpu_topology_ht_ids`
(`sys/vm/vm_page.c:1225-1227`). Those three variables are exposed as
`CTLFLAG_RW` sysctls (`sys/kern/subr_cpu_topology.c:81-86`) writable by root
with **no validation**. Setting any to zero — or inflating one enough to
collapse the derived `cpuscale` to zero — triggers an unconditional
divide-by-zero panic on the next `vm_page_alloc()`. Because the write requires
`PRIV_SYSCTL_WRITE`, this is a **root→kernel DoS** (Low severity); an
unprivileged user gets `Operation not permitted`.

## Build

The PoC is a pure shell script — no compilation required:

```
./build.sh     # no-op (just confirms poc.sh is present)
```

## Run

Run as **root** on the guest (the sysctl requires `PRIV_SYSCTL_WRITE`):

```
./run.sh
# equivalent to:  sysctl hw.cpu_topology_phys_ids=0
```

## Expected

### On the UNPATCHED kernel (#0)
The sysctl write returns, then the next page allocation traps:

```
Stopped at      vm_get_pg_color+0x76:   idivl   0x61ef6c(%rip),%eax
db>
```

The guest is **down** (DDB debugger). This is the divide at
`sys/vm/vm_page.c:1225` (`PQ_L2_SIZE / cpu_topology_phys_ids` with
`phys_ids=0`).

### On the FIXED kernel (#1)
The sysctl is now read-only:

```
sysctl: oid 'hw.cpu_topology_phys_ids' is read only
```

No panic; the guest stays up.

## Variants

- `sysctl hw.cpu_topology_phys_ids=0`   → div0 at `vm_page.c:1225`
- `sysctl hw.cpu_topology_core_ids=0`   → div0 at `vm_page.c:1226`
- `sysctl hw.cpu_topology_core_ids=2000` (1-socket guest) → `grpscale=0`,
  `cpuscale=0` → div0 at the modulo `vm_page.c:1232`

## Files

| file            | purpose                                                        |
|-----------------|----------------------------------------------------------------|
| `poc.sh`        | minimal trigger (root sysctl write)                            |
| `run.sh`        | run wrapper                                                    |
| `build.sh`      | no-op build wrapper                                            |
| `panic.txt`     | divide-by-zero signature from the baseline panic               |
| `env.txt`       | guest uname / cc / topology values                             |
| `fix.diff`      | two-part fix: `CTLFLAG_RD` sysctls + `vm_get_pg_color` guards  |
| `fix_build.log` | full single-fix kernel build output                            |
| `fix_run.log`   | patched-kernel PoC re-run (read-only, no panic)                |
| `VERDICT.md`    | full analysis: mechanism, privilege, fix, validation           |
| `manifest.json` | machine-readable catalog                                       |
