β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4765

Divide-by-zero (SIGFPE) in hpt_copy_array_info rebuild/verify percentage for arrays with bArnMember==1 or per-member capacity < 2MB

Summary

The % ksprintf divides by ((VDeviceCapacity/(bArnMember-1))>>11) unguarded; the driver itself creates bArnMember==1 arrays (1-member RAID-1/JBOD starting states). REPRODUCED on guest harness: Fatal trap 18; guarded-divisor fix survives. Root plants, any user detonates via sysctl read.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4765 Β· 11 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 1.5 KB ↓ raw
h1_harness.c β€” 18.7 KB view raw
build.sh β€” 97 B view raw
run.sh β€” 140 B view raw
build.log β€” 1.2 KB view raw
run.log β€” 774 B view raw
run.fixed.log β€” 692 B view raw
panic.txt β€” 1.0 KB view raw
env.txt β€” 258 B view raw
fix.diff β€” 2.4 KB view raw

DF-4765 β€” divide-by-zero panic in hpt_copy_array_info rebuild/verify percentage (bArnMember==1 / tiny per-member capacity)

File: sys/dev/raid/hptmv/hptproc.c (pass 2)

Build & run (same KLD harness as DF-4764)

./build.sh          # builds /root/h1/h1harness.ko on the guest
kldload /root/h1/h1harness.ko
sysctl h1harness.trigger=2     # baseline: panics (Fatal trap 18)
# after reset + rebuild: sysctl h1harness.trigger=5  -> fix: survives

Expected baseline (run.log)

=== H2 (DF-4765) bArnMember==1 -> div/0 ===
H2: real RAID-1 array, bArnMember=1 rf_verifying=1
Fatal trap 18: integer divide fault while in kernel mode
Stopped at hpt_copy_array_info.constprop.3+0x187:  divq %rdi,%eax

Why it is reachable

  • gui_lib.c:614 accepts AT_RAID1 with nDisk <= 2 β€” a 1-member mirror (bArnMember = pParam->nDisk = 1 at :618) is the create-and-duplicate starting state.
  • hptproc.c:182-183 lets the verify start N sysctl command run on any RAID-1/RAID-5 array regardless of member count.
  • The percentage ksprintf at hptproc.c:457/462 divides by (VDeviceCapacity/(bArnMember-1))>>11 with no guard.
  • The outer divisor is also 0 whenever per-member capacity < 2 MB.

Detonator: any local user reading sysctl hptmv.status (world-readable). Precondition: hptmv hardware with such an array (absent from audit guest; verified via verbatim-code harness β€” see VERDICT.md in DF-4764).

Fix: fix.diff F2 hunk (guarded divisor) β€” validated in-harness (trigger 5 survives, prints Verifying 0%).

VERDICT.md
↓ download raw

DF-4765 VERDICT

Status: REPRODUCED (source+harness). See ../DF-4764/VERDICT.md for the shared harness methodology; this trigger uses a REAL RaidArray (no type confusion) with bArnMember==1, rf_verifying==1:

  • run.log: Fatal trap 18: integer divide fault while in kernel mode, Stopped at hpt_copy_array_info.constprop.3+0x187: divq %rdi,%rax.
  • run.fixed.log (trigger 5, fix.diff F2): prints 1 RAID 1 ... Verifying 0% and survives.

Reachability chain (static): HPT_IOCTL_CREATE_ARRAY AT_RAID1 nDisk=1 β†’ gui_lib.c:614 (nDisk <= 2) + :618 (bArnMember = pParam->nDisk) β†’ 1-member mirror; verify start accepted at hptproc.c:182-183 (type check only) β†’ rf_verifying set by the rebuild DPC β†’ any unprivileged sysctl hptmv.status read β†’ hptproc.c:462 x / 0 β†’ SIGFPE panic.

The 1-member flag state is set by the closed-source raid library (x86_64-elf.raid.o.uu) β€” flagged as the one unverifiable link; confidence 'likely'. The alternative trigger (per-member capacity < 2 MB β‡’ zero denominator with bArnMember==2) needs no flag logic beyond capacity.

Kernel refs: hptproc.c:457, hptproc.c:462, gui_lib.c:614, gui_lib.c:618, hptproc.c:182.

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.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Harness-level A/B: unpatched verbatim copy panics (run.log); guarded version prints 'Verifying 0%' and survives (run.fixed.log trigger 5). Kernel-rebuild validation impossible on this guest (no hptmv hardware).

run.log vs run.fixed.log; fix.diff F2 hunk applies cleanly to pristine sys/dev/raid/hptmv/hptproc.c.
↓ fix.diffper-fix-DF-4765

Confirmed kernel references

Detail

Exploit chain

root/admin creates a 1-member RAID-1 (or JBOD) via HPT_IOCTL_CREATE_ARRAY (accepted) and starts rebuild/verify (sysctl command or HPT_SET_ARRAY_STATE MIRROR_REBUILD_START) -> array sits with bArnMember==1 and rf_rebuilding/rf_verifying set -> any unprivileged user reads sysctl hptmv.status -> hpt_copy_array_info ksprintf divides by zero -> SIGFPE -> kernel panic (local DoS).

Evidence (decisive lines)

run.log (H2 banner then Fatal trap 18, Stopped at hpt_copy_array_info.constprop.3+0x187: divq %rdi,%rax); run.fixed.log trigger 5 (guarded divisor renders 'Verifying 0%', survives); fix.diff F2 hunk.

PoC changes

Trigger 2 of the DF-4764 KLD harness (verbatim function, real driver structs).

Verified recommended fix

Guard the percentage divisor: only divide when bArnMember>1 and the shifted per-member capacity is nonzero (fix.diff F2).

Verdict

REPRODUCED at source+harness (guest, same KLD as DF-4764): trigger 2 drives the VERBATIM hpt_copy_array_info (hptproc.c:402-478) with a REAL RAID-1 array object (no type confusion) having bArnMember==1 and rf_verifying==1, i.e. the exact state the driver itself allows: gui_lib.c:614 accepts AT_RAID1 with nDisk<=2 (1-member mirrors are the create-and-duplicate starting state) and the sysctl 'verify start N' command at hptproc.c:182-183 gates only on VDeviceType being RAID-1/RAID-5. The ksprintf at hptproc.c:462 divides by (VDeviceCapacity/(bArnMember-1))>>11 with bArnMember-1==0 -> 'Fatal trap 18: integer divide fault' at hpt_copy_array_info+0x187 divq (run.log). The same expression zeroes for per-member capacities < 2MB ((cap/(n-1))>>11 == 0). Detonator in production: any local user reading 'sysctl hptmv.status' while such an array is rebuilding/verifying. Preconditions (hptmv hardware + 1-member RAID-1 or tiny-capacity array being rebuilt/verified) cannot exist on the audit guest; everything downstream was executed verbatim in-kernel via the harness. Fix (guarded divisor, fix.diff) validated in-harness: trigger 5 prints 'Verifying 0%' and survives (run.fixed.log).