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

Signed int len assigned from u_int32_t dxfer_len signedness overflow in INQUIRY bzero and odd-length kmalloc/bcopy

Summary

atapi_action declares int len and assigns len=csio->dxfer_len (u_int32_t). For dxfer_len>INT_MAX len becomes negative. INQUIRY special-case bzero(buf len) sign-extends to huge size_t catastrophic overwrite. Odd-length READ path kmalloc(++len) wraps to 0 completion bcopy uses original huge value against tiny kmalloc(0) slab slot massive OOB read. CDB length check at :509 only bounds cdb_len never dxfer_len. [VERIFIED pass2: reproduced - int len from u32 dxfer_len 0x80000000 issues bzero of 18446744071562067968 unbounded kernel wipe captured live; INT_MAX reject fix A/B zero bzero calls]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2502 Β· 9 files
FileTypeDescriptionSize
README.md β€” 1019 B ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
build.sh β€” 99 B view raw
run.log β€” 352 B view raw
env.txt β€” 963 B view raw
fix.diff β€” 994 B view raw
manifest.json β€” 771 B view raw
verdict.json β€” 2.9 KB view raw
panic.txt β€” 252 B view raw

DF-2502 -- Signed int len from u_int32 dxfer_len: sign-extended bzero/kmalloc/bcopy in atapi_action

Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_acam (rig.c = harness, gen.sh = verbatim extractor, dfv_*_extract.h = generated production text). The decisive outputs below were captured live on the guest during the 2026-10-24 verification session; full session logs are summarized per run.

Build

sh build.sh            # extracts verbatim driver text + builds the KLD

Run (guest, root)

kldload ./dfv_rig.ko

kldload dfv_acam.ko; sysctl kern.dfv_acam_run=0 (control); reset; sysctl kern.dfv_acam_run=2 (hostile)

Expected

run.log: DF2502 bzero(len=18446744071562067968) hostile vs bzero(len=512) control; fixed: zero bzero calls.

Fix validation

fix.diff rejects dxfer_len > 0x7fffffff with CAM_REQ_INVALID before any use; A/B shows zero bzero calls.

Verdict: reproduced / impact=corruption (see verdict.json, VERDICT.md)

VERDICT.md
↓ download raw

DF-2502 verdict -- Signed int len from u_int32 dxfer_len: sign-extended bzero/kmalloc/bcopy in atapi_action

status: reproduced reproduced: 1 impact: corruption confidence: certain attempts: 3 runtime_sec: 900

The verbatim atapi_action assigns int len = csio->dxfer_len (u_int32) at :565; the INQUIRY special case then calls bzero(buf, len) with an INQUIRY CDB whose length field exceeds SHORT_INQUIRY_LENGTH. With a pass(4)-shaped CCB (dxfer_len=0x80000000, INQUIRY cdb byte4=255) the verbatim code issued bzero(buf, 18446744071562067968) - the sign-extended INT_MIN length captured live from the verbatim call (the rig intercepts the actual memset; in production this is an unbounded kernel-memory zeroing starting at the CDB data buffer). Control (dxfer_len=512) issued bzero(512) exactly. Both runs continue past the INQUIRY case into a rig-fabrication-depth crash at 0x64(%rcx) (present in control and hostile alike - documented as a rig artifact; the bzero length is the discriminator). Fixed source A/B (reject dxfer_len > 0x7fffffff before the special cases): NO bzero is issued at all and the guest survives the run cleanly. The first fix draft used (u_int)len != dxfer_len, which the round-trip makes always-false - caught by the A/B rerun and corrected.

Evidence (decisive lines)

control: DF2502 bzero(len=512) hostile: DF2502 bzero(len=18446744071562067968) <- 0xffffffff80000000, sign-extended INT_MIN (subsequent rig-state crash at 0x64(%rcx) occurs in BOTH runs - rig artifact, not the defect) --- fixed source A/B --- dmesg | grep -c "DF2502 bzero" -> 0 <- no bzero issued; run completes, guest survives

Panic

Fatal user address access from kernel mode from sysctl at ffffffff82600aad
Stopped at      df_run2502+0x84d:  cmpl $0xfffe,0x64(%rcx)
(rig fabrication-depth artifact present in control and hostile runs; the defect evidence is the captured bzero length)

Exploit chain / privesc assessment

catastrophic kernel memory wipe from a CCB the CAM pass path can carry; on this system the raw-device permission gate bounds who can build it; impact class: corruption.

Fix + A/B

fix.diff rejects dxfer_len > 0x7fffffff with CAM_REQ_INVALID before any use; A/B shows zero bzero calls.

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
↓ fix.diffper-fix-DF-2502

Confirmed kernel references

Detail

Exploit chain

catastrophic kernel memory wipe from a CCB the CAM pass path can carry; on this system the raw-device permission gate bounds who can build it; impact class: corruption.

Evidence (decisive lines)

run.log: DF2502 bzero(len=18446744071562067968) hostile vs bzero(len=512) control; fixed: zero bzero calls.

PoC changes

bzero interposed (rig) to capture the length; the first fix draft was refuted by A/B and corrected to the > INT_MAX form.

Verified recommended fix

reject dxfer_len > INT_MAX before assigning to int len

Verdict

The verbatim atapi_action assigns int len = csio->dxfer_len (u_int32) at :565; the INQUIRY special case then calls bzero(buf, len) with an INQUIRY CDB whose length field exceeds SHORT_INQUIRY_LENGTH. With a pass(4)-shaped CCB (dxfer_len=0x80000000, INQUIRY cdb byte4=255) the verbatim code issued bzero(buf, 18446744071562067968) - the sign-extended INT_MIN length captured live from the verbatim call (the rig intercepts the actual memset; in production this is an unbounded kernel-memory zeroing starting at the CDB data buffer). Control (dxfer_len=512) issued bzero(512) exactly. Both runs continue past the INQUIRY case into a rig-fabrication-depth crash at 0x64(%rcx) (present in control and hostile alike - documented as a rig artifact; the bzero length is the discriminator). Fixed source A/B (reject dxfer_len > 0x7fffffff before the special cases): NO bzero is issued at all and the guest survives the run cleanly. The first fix draft used (u_int)len != dxfer_len, which the round-trip makes always-false - caught by the A/B rerun and corrected.