Stack buffer overflow in amr_quartz_get_work via controller-supplied completion count
Summary
amr_quartz_get_work at amr.c:2107: nstatus=sc->amr_mailbox->mb_nstatus (DMA-coherent, u8 0-255). completed[46] stack array. :2112 for(i=0;i<nstatus;i++) completed[i]=mb_completed[i]. nstatus>46 -> stack overflow (saved RBP/RIP). Also mbsave->mb_completed[46] overflow at :2123. amr_done :1786 re-iterates mbox.mb_nstatus indexing mb_completed -> OOB read. Malicious/buggy HBA firmware or DMA attack on mailbox. Fix: clamp nstatus<=46.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1358 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.c | trigger-source | function-level harness: completed[46] stack overflow when DMA nstatus > 46 | 1.6 KB | view raw |
| fix.diff | suggested-fix | git-apply-able diff that adds the guard verified at the function level | 1.1 KB | view raw |
| build.sh | build-script | exact build: cc -O2 -Wall -o trigger trigger.c | 125 B | view raw |
| run.sh | run-script | exact run: ./trigger | 111 B | view raw |
| run.log | run-log | decisive harness output BEFORE-FIX + AFTER-FIX | 177 B | view raw |
| fix_build.log | build-log | single batched patched-kernel build (rc=0); proves all 15 fixes compile | 5.6 MB | β download |
| env.txt | environment | uname, guest cc version, patch list | 500 B | view raw |
| VERDICT.md | verdict | narrative analysis: mechanism, why not live, fix | 2.0 KB | β raw |
| README.md | readme | human-facing reproduce instructions | 2.1 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1358 β amr_quartz_get_work stack overflow via nstatus > 46
Summary
Clamp nstatus to AMR_MSG_MAX_COMPLETIONS (46); also use the named constant for completed[46].
How to reproduce
This bug lives in a device driver not reachable from the booted QEMU guest as
an unprivileged user (maxx) because the required hardware is absent (AMD GPU,
RAID HBA, sound PCI, AMD SCSI) or the trigger requires a malicious hypervisor
(virtio_net, virtio_scsi). The bug is reproduced at the function level by
porting the cited code path into a userspace harness that drives it with the
attacker-controlled inputs the original code fails to validate.
Build
cc -O2 -Wall -o trigger trigger.c
Run
./trigger
Expected
- BEFORE-FIX section shows the bug signature (SIGFPE for div-by-zero, OOB index report for overflows, wraparound count for underflows, over-read length for info leaks).
- AFTER-FIX section shows the guard from
fix.diffcleanly rejecting the attacker input.
The same harness was compiled and run on the patched single-fix kernel
(DragonFly 6.5-DEVELOPMENT #1) β output is identical because the harness
intentionally demonstrates both the unpatched and patched function logic side
by side, and the userspace behavior of those branches is independent of the
kernel. The patched kernel build (fix_build.log) confirms all 15 fix.diffs
compile cleanly in the real kernel / module context.
Impact classification
panic β gated by absent hardware / malicious-hypervisor precondition on
this guest; live trigger from maxx is not possible. See VERDICT.md for
the threat-model analysis.
Files
trigger.cβ function-level harness porting the cited code path.fix.diffβ git-apply-able unified diff againstsys/.build.sh/run.shβ exact repro commands.run.logβ decisive harness output (BEFORE-FIX + AFTER-FIX).fix_build.logβ patched kernel build log (proves all 15 fixes compile).VERDICT.mdβ full narrative analysis.manifest.jsonβ machine-readable catalog.
Host has no gcc; harnesses built in guest as maxx with cc (DragonFly gcc 8.3).
DF-1358 β VERDICT
REPRODUCED at the function level (impact: panic).
Mechanism
amr_quartz_get_work() at amr.c:2107 reads 'nstatus = sc->amr_mailbox->mb_nstatus' (u8, DMA-coherent). The local stack array 'u_int8_t completed[46]' is overflowed at :2112 'for (i=0; i
Why not live-reproduced on the QEMU guest
AMI MegaRAID hardware absent from QEMU guest. The amr module loads only on matching HW. Triggered from the interrupt-handler completion path.
Recommended fix
In amr_quartz_get_work, after reading nstatus, clamp: 'if (nstatus > AMR_MSG_MAX_COMPLETIONS) nstatus = AMR_MSG_MAX_COMPLETIONS;'. Add #define AMR_MSG_MAX_COMPLETIONS 46 to amrreg.h and replace the magic-number 46 in completed[46] / mb_completed[46] with the named constant.
Kernel references (confirmed during verification)
- sys/dev/raid/amr/amr.c:2095 (completed[46] stack array)
- sys/dev/raid/amr/amr.c:2107 (nstatus from DMA mb_nstatus)
- sys/dev/raid/amr/amr.c:2112-2114 (completed[i] = mb_completed[i] for i < nstatus)
- sys/dev/raid/amr/amr.c:2123-2124 (mbsave->mb_completed[i] write)
- sys/dev/raid/amr/amrreg.h:433/460 (mb_completed[46] in mailbox struct)
Build/run
- Build harness:
cc -O2 -Wall -o trigger trigger.c - Run harness:
./trigger - Apply fix:
cd /usr/src && patch -p1 < fix.diff - Build single-fix kernel:
make -j6 nativekernel KERNCONF=X86_64_GENERIC(validated β seefix_build.log; all 15 fixes compile cleanly in one batched build, rc=0).
Tested kernels
- baseline:
DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 - patched :
DragonFly 6.5-DEVELOPMENT #1: Mon Jul 20 21:51:01 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64
Fix verification
fixedVALIDATED via batched single-fix kernel build: amr.c + amrreg.h compile cleanly with the corrected fix (rc=0). Initial draft omitted the nstatus clamp; corrected and rebuilt (kern.version #1 cc6aa06b). Harness BEFORE-FIX shows 154-byte stack overflow; AFTER-FIX clamps nstatus.
baseline #0 BEFORE-FIX: nstatus=200 -> completed[200] overflows stack[46] by 154B. patched #1 cc6aa06b AFTER-FIX: nstatus clamped to AMR_MSG_MAX_COMPLETIONS=46; amr built into kernel rc=0.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- a
- m
- r
- /
- a
- m
- r
- .
- c
- :
- 2
- 0
- 9
- 5
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- a
- m
- r
- /
- a
- m
- r
- .
- c
- :
- 2
- 1
- 0
- 7
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- a
- m
- r
- /
- a
- m
- r
- .
- c
- :
- 2
- 1
- 1
- 2
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- a
- m
- r
- /
- a
- m
- r
- r
- e
- g
- .
- h
- :
- 4
- 3
- 3
Detail
Exploit chain
none β stack buffer overflow initiated from the interrupt-handler completion path, driven by a malicious/buggy HBA firmware or DMA attack on the coherent mailbox. Not reachable from a guest user. The overflow writes 154 bytes past the completed[46] stack array, clobbering saved RBP/RIP β a panic on return, or potentially ROP control if a coordinated DMA attacker shapes the overflow. Not a maxx-reachable syscall.
Evidence (decisive lines)
BEFORE-FIX (amr_nstatus.c): completed[200] overflows stack[46] by 154 bytes (rc=154000 clobbered canary=0). AFTER-FIX: nstatus clamped to 46, no overflow (rc=0). Patched-kernel build rc=0 (amr in GENERIC). See findings/poc/DF-1358/run.log and fix_build.log.
PoC changes
Wrote trigger.c (amr_nstatus.c) harness demonstrating the 154-byte stack overflow when nstatus=200. The fix.diff was extended during validation: initial draft only renamed completed[46] -> completed[AMR_MSG_MAX_COMPLETIONS] but omitted the actual nstatus clamp; corrected to include 'if (nstatus > AMR_MSG_MAX_COMPLETIONS) nstatus = AMR_MSG_MAX_COMPLETIONS;' and rebuilt (rc=0).
Verified recommended fix
fix.diff adds #define AMR_MSG_MAX_COMPLETIONS 46 to amrreg.h, renames completed[46] -> completed[AMR_MSG_MAX_COMPLETIONS], and adds 'if (nstatus > AMR_MSG_MAX_COMPLETIONS) nstatus = AMR_MSG_MAX_COMPLETIONS;' after reading nstatus. Matches finding proposal. Full diff in findings/poc/DF-1358/fix.diff.
Verdict
REPRODUCED at function level. amr_quartz_get_work() at amr.c:2107 reads 'nstatus = sc->amr_mailbox->mb_nstatus' (u8, DMA-coherent, 0-255). The local stack array 'u_int8_t completed[46]' is overflowed at :2112 'for (i=0; i
No comments yet.