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

mv family queue-pointer registers used unmasked: device-fed outbound_tail/inbound_head index inbound_q/outbound_q[512] MMIO β€” OOB bus-space read and 8-byte OOB MMIO write up to ~32GB past BAR2

Summary

The mv out/inbound helpers use the device registers unmasked as queue-array MMIO offsets; arrays are 512 entries; wrap checks occur only after use. A device value >=512 steers OOB MMIO access - machine-check/panic or neighbouring-BAR corruption. Distinct from DF-1312 (srb[] index); these are the queue-pointer registers themselves. Mask-at-read fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3963 Β· 11 files
FileTypeDescriptionSize
README.md β€” 1.3 KB ↓ raw
VERDICT.md β€” 1.6 KB ↓ raw
trigger.c β€” 2.0 KB view raw
build.sh β€” 67 B view raw
run.sh β€” 91 B view raw
run.log β€” 12 B view raw
env.txt β€” 1021 B view raw
fix.diff β€” 907 B view raw
build.log β€” 7.1 KB view raw
manifest.json β€” 370 B view raw
verdict.json β€” 3.1 KB view raw

DF-3963 β€” mv queue-pointer registers trusted raw: device-fed OOB MMIO read and write off the 512-entry queue arrays

File: sys/dev/raid/hptiop/hptiop.c (pass 2; knowns excluded β€” DF-1312 covers the hba->srb[] index, not these queue-pointer registers)

What

hptiop_mv_outbound_read (229–250) reads the device register outbound_tail and uses it unmasked as the index outbound_q[outbound_tail] β€” an MMIO read at BAR2+0x1040+8tail. hptiop_mv_inbound_write (252–265) likewise writes 8 host-chosen bytes at BAR2+0x40+8inbound_head, unmasked. The arrays are 512 entries (MVIOP_QUEUE_LEN, hptiop.h:96–107). A device value β‰₯ 512 (or a device-DMA attacker flipping the register) steers the access up to ~32 GB past BAR2. Host RAM is not behind the BAR, so impact is machine-check/panic (unmapped MMIO) or corruption of a neighbouring mapped BAR β€” a device-fed DoS /MMIO-corruption primitive. Complements DF-1312 (which is the srb[] index fed from the popped queue values); these are the queue pointer registers themselves.

Build / Run

Hardware-gated (mv-family RR3020/3120 HBA required; guest has none β€” env.txt).

Expected

Fatal trap / machine check on the OOB MMIO access, or silent corruption of an adjacent device's register file.

VERDICT.md
↓ download raw

DF-3963 verdict β€” certain source gap, device-fed, Low impact on host memory

Reproduced?

No: no mv-family HighPoint HBA on the guest (env.txt).

Analysis (path:line)

  • hptiop.c:232 β€” outbound_tail = BUS_SPACE_RD4_MV2(outbound_tail) device register; :236-239 uses it unmasked as outbound_q[outbound_tail] offset (BAR2 + 0x1040 + 8*tail) for bus_space_read_region_4(..., 2).
  • hptiop.c:254 β€” inbound_head = BUS_SPACE_RD4_MV2(inbound_head); :260-262 writes 8 bytes of p (host phys addr | MVIOP flags) at inbound_q[inbound_head] (BAR2 + 0x40 + 8*head), unmasked.
  • Wrap checks (:243, :257) happen only AFTER use and only for the +1 value β€” the device-provided base is never bounded by MVIOP_QUEUE_LEN (512, hptiop.h:96; arrays at hptiop.h:105-106).
  • Both call sites run on the I/O path (hptiop_drain_outbound_queue_mv :722-733, hptiop_post_* mv writes) β€” reachable from ordinary I/O once the register is bad.

Why Low

The corrupted window is PCI MMIO space, not host RAM: realistic outcomes are a bus error/panic (DoS) or clobbering another device's registers. Same device-trust threat model as DF-1312 (rated Low). Fix is a two-line mask.

Fix

fix.diff masks both device-fed pointers with & (MVIOP_QUEUE_LEN - 1).

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

not_testable
baseline no→ patch + rebuild →patched clean

Two-line mask keeps all accesses inside the 512-entry arrays; git apply --check clean. Runtime validation impossible without the HBA fix.diff applied to the guest /usr/src copy and the hptiop.ko module rebuilt clean with -Werror (build.log; combined with the other four hptiop fix.diffs of this batch); runtime validation still impossible without the HBA.

fix.diff; fix.diff applied to the guest /usr/src copy and the hptiop.ko module rebuilt clean with -Werror (build.log; combined with the other four hptiop fix.diffs of this batch); runtime validation still impossible without the HBA.
↓ fix.diffper-fix-DF-3963

Confirmed kernel references

Detail

Exploit chain

wedge/emulated mv HBA sets inbound_head=0x400 -> next hptiop_mv_inbound_write posts 8 bytes at BAR2+0x40+0x2000 (outside the 4KB queue window) -> unmapped-MMIO machine check (panic/DoS) or corruption of an adjacent mapped BAR; symmetric OOB read path via outbound_tail in the interrupt drain.

Evidence (decisive lines)

VERDICT.md (offset math + path:line), trigger.c, env.txt, fix.diff (mask both registers, git apply --check clean)

PoC changes

Informational trigger only; hardware-gated.

Verified recommended fix

Mask device-fed queue pointers: & (MVIOP_QUEUE_LEN - 1) in hptiop_mv_outbound_read/hptiop_mv_inbound_write (fix.diff).

Verdict

Source-certain device-fed OOB bus-space access: hptiop_mv_outbound_read (hptiop.c:232,236-239) and hptiop_mv_inbound_write (:254,260-262) use the device-maintained outbound_tail / inbound_head registers unmasked as indices into the 512-entry inbound_q/outbound_q arrays (hptiop.h:96-107); a value >=512 steers an MMIO read (and an 8-byte MMIO WRITE of a host phys|flags value) up to ~32GB past BAR2. Impact bounded to MMIO space (machine-check/panic or neighbouring-BAR corruption), not host RAM -> Low, same device-trust class as the known DF-1312 (which covers the srb[] index, not the queue-pointer registers). Not reproduced: no mv-family HBA on the guest (env.txt).