amd_Reselect indexes target arrays with -1 via ffs(0)-1 on malicious reselect ID
Summary
amd_Reselect at amd.c:1829-1831: cur_target=amd_read8(FIFO)^HostID_Bit then ffs(x)-1. If x==0 after XOR -> ffs(0)=0 -> cur_target=-1. Indexes tinfo[-1], untagged_srbs[-1][lun], disc_count[-1][lun]. OOB read/write within softc, wild active_srb ptr dereferenced by phase engine. Malicious SCSI target drives bogus reselect ID. Sibling of DF-1238 (trm reselect). Fix: validate x!=0 and power-of-2 before ffs.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1347 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.c | trigger-source | function-level harness: ffs(0)-1 = -1 OOB target array index | 2.0 KB | view raw |
| fix.diff | suggested-fix | git-apply-able diff that adds the guard verified at the function level | 624 B | 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 | 198 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 | 1.9 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-1347 β amd_Reselect indexes target arrays with -1 via ffs(0)-1
Summary
Validate cur_target != 0 and is a power of 2 before ffs(); otherwise reset the SCSI bus and return.
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-1347 β VERDICT
REPRODUCED at the function level (impact: panic).
Mechanism
amd_Reselect() at amd.c:1829-1831: cur_target = amd_read8(FIFO) ^ HostID_Bit; cur_target = ffs(cur_target) - 1. If a malicious SCSI target drives a bogus reselect ID such that XOR with HostID_Bit yields 0 (or any non-power-of-2), ffs(0) returns 0, so cur_target = -1. The subsequent tinfo[-1], untagged_srbs[-1][lun], disc_count[-1][lun] accesses are OOB within the softc (wild active_srb pointer then dereferenced by the phase engine).
Why not live-reproduced on the QEMU guest
The AMD 53C974 SCSI HBA driver (amd.c) attaches only to AMD/Tekram DC-390(T) hardware, absent from QEMU guest. The bug requires a malicious/buggy SCSI target on the bus, reachable by a compromised external device.
Recommended fix
After computing 'cur_target ^= HostID_Bit', check 'if (cur_target == 0 || (cur_target & (cur_target-1)) != 0)'. If invalid, log the bad ID, reset the SCSI bus (RST_SCSI_BUS_CMD) and return without indexing the target arrays.
Kernel references (confirmed during verification)
- sys/dev/disk/amd/amd.c:1829 (cur_target = amd_read8(FIFO))
- sys/dev/disk/amd/amd.c:1830 (cur_target ^= HostID_Bit)
- sys/dev/disk/amd/amd.c:1831 (cur_target = ffs(cur_target) - 1 -> -1 when 0)
- sys/dev/disk/amd/amd.c:1833-1835 (tinfo[cur_target], untagged_srbs, disc_count OOB)
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: amd.c (in GENERIC kernel β device amd is in X86_64_GENERIC) compiles cleanly with the corrected fix (rc=0). Initial draft used undeclared RESET_COMMAND which the first build rejected; corrected to RST_SCSI_BUS_CMD and rebuilt with rc=0. Harness BEFORE-FIX shows cur_target=-1 OOB; AFTER-FIX shows guarded reset.
baseline #0 BEFORE-FIX: cur_target=-1 -> OOB tinfo[-1]/untagged_srbs[-1][0]/disc_count[-1][0]. patched #1 cc6aa06b AFTER-FIX: invalid reselect id rejected, RST_SCSI_BUS_CMD issued; amd.c built into /boot/kernel/kernel rc=0.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- a
- m
- d
- /
- a
- m
- d
- .
- c
- :
- 1
- 8
- 2
- 9
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- a
- m
- d
- /
- a
- m
- d
- .
- c
- :
- 1
- 8
- 3
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- a
- m
- d
- /
- a
- m
- d
- .
- c
- :
- 1
- 8
- 3
- 3
Detail
Exploit chain
none β OOB softc write/read initiated by a malicious SCSI target on the bus, not reachable from a guest user. The primitive corrupts the local softc (tinfo/untagged_srbs/disc_count arrays); wild active_srb pointer subsequently dereferenced. Requires compromised external HW on the SCSI chain. Not a maxx-reachable syscall.
Evidence (decisive lines)
BEFORE-FIX (amd_resel.c): cur_target=-1 (ffs(0)-1) -> indexed tinfo[-1], untagged_srbs[-1][0], disc_count[-1][0] OOB. AFTER-FIX: invalid reselect id rejected, cur_target untouched, RST_SCSI_BUS_CMD issued. Patched-kernel build rc=0. See findings/poc/DF-1347/run.log and fix_build.log.
PoC changes
Wrote trigger.c (amd_resel.c) harness demonstrating ffs(0)-1 = -1 -> OOB softc index. The fix.diff was corrected during validation: original used undeclared 'RESET_COMMAND' (which doesn't exist in amd.h); replaced with the correct 'RST_SCSI_BUS_CMD' from amd.h:363. Re-validated to compile cleanly.
Verified recommended fix
fix.diff adds 'if (amd->cur_target == 0 || (amd->cur_target & (amd->cur_target - 1)) != 0) { device_printf(...); amd_write8(amd, SCSICMDREG, RST_SCSI_BUS_CMD); return; }' before ffs(). Matches finding proposal (validate x!=0 and power-of-2 before ffs). Full diff in findings/poc/DF-1347/fix.diff.
Verdict
REPRODUCED at function level. amd_Reselect() at amd.c:1829-1831 computes 'cur_target = amd_read8(FIFO) ^ HostID_Bit; cur_target = ffs(cur_target) - 1'. A malicious SCSI target driving a bogus reselect ID that XORs to 0 (or any non-power-of-2) makes ffs(0)=0 -> cur_target=-1. The subsequent tinfo[-1], untagged_srbs[-1][lun], disc_count[-1][lun] accesses are OOB within the softc (wild active_srb pointer dereferenced by the phase engine). Harness amd_resel.c demonstrates cur_target=-1 before fix; fixed path rejects the invalid ID and resets the bus. AMD 53C974 SCSI HBA absent from guest.
No comments yet.