# DF-4764 VERDICT

## Status: REPRODUCED (mechanism, source+harness on guest) — kernel panic (SIGFPE) via type confusion on an unprivileged sysctl read path; end-to-end detonation needs hptmv hardware (absent from guest), statically established.

## The bug

`sys/dev/raid/hptmv/hptproc.c:515-549` — `hpt_get_info()` walks the VBus
logical-device list. For each `_vbus_p->pVDevice[i]` that is not itself an
array but has been *added to a RAID-1 after driver load* (the driver's own
comment at :530-533: "it may be add to an array after driver loaded, check
it"), the code jumps into the array-display branch:

```c
534:  if (pVDev->pParent->VDeviceType==VD_RAID_1 && pVDev==pVDev->pParent->u.array.pMember[0])
535:      goto is_array;            /* ← passes the member DISK */
...
523:  is_array:
524:      hpt_copy_array_info(pinfo, j, pVDev);
```

The label invokes `hpt_copy_array_info()` **with the member disk**, not with
`pVDev->pParent`. `VDevice.u` is a union of `RaidArray` and `Device`
(vdevice.h:71-76), so the disk's fields are re-parsed as array fields
(LP64 layout proven by CTASSERT in the harness, matching array.h:54-99 /
global.h:70-88 / vdevice.h):

| punned array field | overlaid disk field | value for an online disk |
|---|---|---|
| `bArnMember` (u+0) | `df_on_line` (u+0) | **1** |
| `rf_broken` (bit16 of u+26) | bit16 of `dDeRealCapacity` (u+24) | disk-size dependent |
| `rf_rebuilding` (bit21), `rf_verifying` (bit22) | bits 21/22 of `dDeRealCapacity` | disk-size dependent |
| `RebuildSectors` (u+32) | `HeadPosition`/`QueueLength` | junk |
| `pMember[0]` (u+40) | `Device.mv` (u+40) | **valid kernel pointer** (`MV_SATA_CHANNEL*`) |

In `hpt_copy_array_info`, with `sStatus` still NULL (online, bit16=0), the
member loop runs `bArnMember` = 1 time; `mIsArray(pMember[0])` dereferences
the `mv` channel pointer (valid, so no fault), reading its first byte
(`channelNumber` ≤ 3 on RR18xx → false) so `pTmpArray = pArray` (the disk);
then bit21/bit22 of the disk capacity is read as `rf_rebuilding` /
`rf_verifying` and the "Rebuilding/Verifying %" `ksprintf` at
hptproc.c:457/462 divides by

```c
(pTmpArray->VDeviceCapacity / (pTmpArray->u.array.bArnMember - 1)) >> 11
```

with `bArnMember` = `df_on_line` = **1** → `x / 0` → **integer-divide trap
(#DE) in kernel mode → panic**.

Disk sizes with bit16=0 and bit21 or bit22 set detonate; e.g. the common
1 TB sector count 0x7470DA70 (bit16=0, bit21=1, bit22=1), 500 GB
0x3A38A5D0 (bit16=0, bit21=1) — roughly half of all capacities. Sizes with
bit16=1 instead display "Critical" (no crash); sizes with bits 21/22 clear
print a garbage row (misparse of live kernel objects, plus `ArrayName`
read from `u+104` past the 48-byte disk union tail).

## Trigger

Unprivileged: `sysctl hptmv.status` (world-readable sysctl; confirmed on the
guest — unprivileged read works, unprivileged *write* is EPERM via
kern_sysctl.c:1446). Precondition: a RocketRAID 18xx system whose admin
built a RAID-1 whose source disk remains enumerated (the state
hptproc.c:530-535 explicitly handles).

## Reproduction (guest, DragonFly 6.5-DEVELOPMENT #0, cc 8.3)

- `h1harness.ko` built against `/usr/src/sys` with the real driver headers;
  CTASSERT layout proof passed at load.
- `sysctl h1harness.trigger=1` → exact predicted state printed, then
  `Fatal trap 18: integer divide fault while in kernel mode`,
  `Stopped at hpt_copy_array_info.constprop.3+0x130: divq %rdi,%eax`
  (run.log, panic.txt). Guest down.
- Fix variant (trigger 4, `pVDev = pVDev->pParent` per fix.diff): renders
  the proper RAID-1 row and survives (run.fixed.log).

## Reachability gap (honest)

The live path `sysctl hptmv.status → hpt_status → hpt_get_info → (arrays
present)` requires hptmv controller hardware. The audit guest has none
(`hptmv.status` prints only the driver-version line — gIal_Adapter is
NULL), so the final hop (real RAID-1 on real hardware) is static:
hptproc.c:530-533's comment documents that member disks stay listed in
`pVDevice[]`; the whole `pParent`/`pMember[0]` special-case exists only
because of it. Everything downstream of that state was executed verbatim
in the guest kernel via the harness.

## Kernel references

- sys/dev/raid/hptmv/hptproc.c:523-535 (buggy goto), :444-463 (loop + div)
- sys/dev/raid/hptmv/vdevice.h:71-76 (union), :80-81 (sizes)
- sys/dev/raid/hptmv/array.h:54-99 (RaidArray layout incl. rf bit order)
- sys/dev/raid/hptmv/global.h:70-88 (Device layout)
- sys/kern/kern_sysctl.c:1446-1447 (write privilege; read unrestricted)

## Recommended fix

Pass the parent array (fix.diff): `pVDev = pVDev->pParent; goto is_array;`.
Validated in-harness: trigger 4 survives. Also harden the % (DF-4765) and
the name terminator (DF-4766).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
