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

Unvalidated GETCOMMPREF geometry: aac_max_fib_size of zero divides by zero at attach (boot panic); sizes below the 28-byte FIB header make the unconditional header copyin overflow the undersized FIB slots

Summary

aac_check_firmware() stores (options & 0xFFFF) from the adapter mailbox into aac_max_fib_size, clamps only an upper bound, then computes aac_max_fibs_alloc = PAGE_SIZE / aac_max_fib_size: zero panics (#DE) at attach; 1..511 yields FIB slots smaller than the 28-byte struct aac_fib_header that aac_ioctl_sendfib() unconditionally copyins into the last slot - up to 27 bytes of user-controlled data past the bus_dmamem_alloc'd pool. Buggy firmware revision -> machine panics at boot when the driver attaches; malicious firmware -> user-content heap overflow adjacent to the FIB DMA pool. Clamp into [sizeof(struct aac_fib), PAGE_SIZE].

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3821 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.3 KB ↓ raw
VERDICT.md β€” 2.7 KB ↓ raw
fix.diff β€” 842 B view raw
env.txt β€” 323 B view raw
verdict.json β€” 2.2 KB view raw

DF-3821 β€” aac(4) trusts the adapter-reported FIB geometry from GETCOMMPREF: zero max_fib_size panics at attach (divide by zero), sub-header sizes overflow FIB slots

  • File: sys/dev/raid/aac/aac.c
  • Lines: 1800-1811 (geometry), 3010-3011 (28-byte header copyin)
  • Class: CWE-20/CWE-369 (unvalidated external value β†’ divide-by-zero panic) + CWE-787 (undersized DMA heap slots overflowed by fixed-size header copy)
  • Reach: attach-time only, driven by adapter mailbox data.

Analysis

aac_check_firmware() (aac.c:1800-1811):

1800:   if (!aac_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) {
1801:       options = AAC_GET_MAILBOX(sc, 1);
1802:       sc->aac_max_fib_size = (options & 0xFFFF);
...
1809:   if (sc->aac_max_fib_size > PAGE_SIZE)
1810:       sc->aac_max_fib_size = PAGE_SIZE;
1811:   sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size;

Only an upper clamp exists.

  • options & 0xFFFF == 0 (firmware returns 0 in the low mailbox word): line 1811 executes PAGE_SIZE / 0 β†’ x86 #DE β†’ kernel panic during device attach (boot-time DoS on machines whose (buggy) card reports that).
  • 0 < max_fib_size < sizeof(struct aac_fib) (512): FIB slots are allocated aac_max_fibs_alloc * aac_max_fib_size bytes with each cm->cm_fib spaced aac_max_fib_size apart (aac.c:1455-1476). With a sub-512 report (e.g. 64), aac_ioctl_sendfib() still does an unconditional copyin(ufib, cm->cm_fib, sizeof(struct aac_fib_header)) (28 bytes, aac.c:3010-3011) β€” for the last slot of the final aac_alloc_commands() block this writes past the end of the bus_dmamem_alloc'd FIB pool (heap/DMA memory corruption, up to 27 bytes past for max_fib_size == 1, up to 12 bytes past for 16, …).
  • Note sc->aac_max_fibs = (options & 0xFFFF) (aac.c:1807) is likewise unvalidated but only drives an allocation size (≀ 65535 Γ— ~120 bytes), not an overflow.

Both require misbehaving/compromised controller firmware β€” the classic device-trust precondition β€” but the divide-by-zero is a boot-time panic on a merely buggy firmware revision, which elevates it above pure hardening.

Verdict

inconclusive β€” no AAC hardware on the audit guest (see ../DF-3819/env.txt); the geometry values come from the adapter mailbox. Source-level: certain.

VERDICT.md
↓ download raw

VERDICT β€” DF-3821

Status: inconclusive (device-gated: the offending values come from the adapter's GETCOMMPREF mailbox reply; no AAC HBA on the audit guest). Source-level confidence: certain.

Proof path

  1. aac_check_firmware() sends AAC_MONKER_GETCOMMPREF (aac.c:1800). On success it stores (options & 0xFFFF) from mailbox 1 into sc->aac_max_fib_size (aac.c:1802) and from mailbox 3 into sc->aac_max_fibs (aac.c:1807) β€” 16-bit, unvalidated values produced by the controller firmware.
  2. The only sanitization is if (sc->aac_max_fib_size > PAGE_SIZE) sc->aac_max_fib_size = PAGE_SIZE; (aac.c:1809-1810).
  3. sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size; (aac.c:1811): - zero β†’ integer division by zero β†’ #DE β†’ panic in the boot/attach path (aac_attach β†’ aac_alloc β†’ … the division happens inside aac_check_firmware before aac_alloc). - 1..511 β†’ FIB slots smaller than sizeof(struct aac_fib).
  4. Slot layout: aac_alloc_commands() carves aac_max_fibs_alloc Γ— aac_max_fib_size bytes and sets cm->cm_fib = base + i*aac_max_fib_size (aac.c:1470-1476); bus_dmamem_alloc sizes the whole pool accordingly (aac.c:1455).
  5. aac_ioctl_sendfib() unconditionally copies the 28-byte struct aac_fib_header into cm->cm_fib before size negotiation (aac.c:3010-3011). For the last slot of the last block this writes (28 - aac_max_fib_size) bytes beyond the allocation whenever aac_max_fib_size < 28 (and stays inside otherwise). The copyin target is the FIB slot, not user memory: heap DMA-pool overflow with fully user-controlled header bytes (the FIB header is user-supplied content), i.e. adjacent kernel heap corruption on a sub-28 report.

The FreeBSD ancestor carries the same code; this is shared-hardening plus a realistic buggy-firmware panic.

Guest evidence

No AAC controller: pciconf -l vendor scan β†’ 0 hits, no /dev/aacd0, driver compiled into X86_64_GENERIC but never attaches (../DF-3819/env.txt). GETCOMMPREF cannot be answered by anything, so the geometry values cannot be steered on this guest.

Fix

fix.diff: clamp aac_max_fib_size into [sizeof(struct aac_fib), PAGE_SIZE] β€” kills both the divide-by-zero and the sub-header slot under-sizing in one check.

Fix validation

not_testable β€” same hardware precondition.

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

Cannot be exercised without an AAC controller answering GETCOMMPREF; absent on the guest. fix.diff adds the floor clamp.

fix.diff
↓ fix.diffper-fix-DF-3821

Confirmed kernel references

Detail

Exploit chain

faulty/compromised firmware reports max_fib_size in 1..27 -> aac_ioctl_sendfib copyin writes 28 user-controlled bytes into the final slot -> up to 27 bytes past the FIB DMA pool -> adjacent kernel heap corruption.

Evidence (decisive lines)

VERDICT.md (five-step proof with line refs); fix.diff (floor clamp); ../DF-3819/env.txt (guest has no AAC hardware)

PoC changes

No seed; analysis pack (attach-time device data cannot be synthesized on the guest).

Verified recommended fix

Clamp aac_max_fib_size into [sizeof(struct aac_fib), PAGE_SIZE] after GETCOMMPREF (aac.c:1809).

Verdict

aac_check_firmware() takes sc->aac_max_fib_size directly from the adapter's GETCOMMPREF mailbox (options & 0xFFFF, aac.c:1802) and only clamps an upper bound (aac.c:1809) before dividing PAGE_SIZE by it (aac.c:1811). A zero report panics the kernel at attach (integer divide-by-zero, #DE); a report below sizeof(struct aac_fib) (512) creates FIB slots smaller than the 28-byte struct aac_fib_header that aac_ioctl_sendfib() unconditionally copyins into the last slot of the DMA pool (aac.c:3010), overflowing the bus_dmamem_alloc allocation with user-controlled bytes. Device-gated: no AAC HBA on the audit guest (no /dev/aacd0, zero matching PCI vendors), so the mailbox values cannot be produced; source analysis is certain.