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

Unvalidated device-controlled shift in namespace block-size computation

Summary

nsc->blksize computed as 1<<lbafmt->sect_size where lbafmt->sect_size is device-controlled uint8_t from Identify Namespace DMA response with no range validation. For sect_size in [32 255] shift is UB (C11). For sect_size==31 yields INT_MIN becomes 2^31 in uint32. Resulting garbage blksize used as divisor and in byte-count products in nvme_disk.c enabling corrupted geometry nonsensical media sizes and integer overflow in nlba*blksize I/O sizing. [VERIFIED pass2: reproduced - device picks blksize 0x80000000 or 16 driving UB 1-lt-lt-n into nvme_disk_attach; power-of-two 9..12 reject fix A/B attach skipped]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2513 Β· 8 files
FileTypeDescriptionSize
README.md β€” 993 B ↓ raw
VERDICT.md β€” 1.9 KB ↓ raw
build.sh β€” 99 B view raw
run.log β€” 375 B view raw
env.txt β€” 963 B view raw
fix.diff β€” 623 B view raw
manifest.json β€” 759 B view raw
verdict.json β€” 2.3 KB view raw

DF-2513 -- Unvalidated device-controlled shift in nvme namespace block-size

Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_nvme (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_nvme.ko; for s in 9 31 36 63; do sysctl kern.dfv_nvme_run=$s >/dev/null; sysctl -n kern.dfv_nvme_status; done

Expected

run.log: blksize 512/2147483648/16/2147483648 for sect_size 9/31/36/63; fixed: attach skipped.

Fix validation

fix.diff rejects sect_size outside 9..12 and skips the attach; A/B shows no namespace published.

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

VERDICT.md
↓ download raw

DF-2513 verdict -- Unvalidated device-controlled shift in nvme namespace block-size

status: reproduced reproduced: 1 impact: corruption confidence: certain attempts: 2 runtime_sec: 500

The verbatim nvme_admin_state_identify_ns computes nsc->blksize = 1 << lbafmt->sect_size (nvme_admin.c:673-675) with sect_size a device-controlled uint8_t from the Identify Namespace DMA and no range check. The rig refills the crafted Identify response at the DMA-completion boundary (after the drivers bzero of req->info - the first version filled too early and the bzero wiped it). Results: sect_size=9 -> 512 (legal control); 31 -> 0x80000000 (2GiB blocks); 36 -> 16 (shift count masked to 5 bits - C11 UB); 63 -> 0x80000000. The garbage blksize is what nvme_disk_attach publishes as the namespace media block size (geometry/media-size corruption; divisor in I/O sizing). Fixed source (reject formats outside 9..12, skip attach) A/B: attach count 0, no garbage published.

Evidence (decisive lines)

DF2513[sect_size=9]: nvme_disk_attach saw blksize=512 DF2513[sect_size=31]: nvme_disk_attach saw blksize=2147483648 DF2513[sect_size=36]: nvme_disk_attach saw blksize=16 DF2513[sect_size=63]: nvme_disk_attach saw blksize=2147483648 --- fixed source A/B --- DF2513[sect_size=31]: nvme_disk_attach saw blksize=3735928559 (attach count 0) <- attach skipped, nothing published

Exploit chain / privesc assessment

hostile/faulty NVMe controller (PCI trust); geometry corruption; no memory-safety violation by itself.

Fix + A/B

fix.diff rejects sect_size outside 9..12 and skips the attach; A/B shows no namespace published.

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-2513

Confirmed kernel references

Detail

Exploit chain

hostile/faulty NVMe controller (PCI trust); geometry corruption; no memory-safety violation by itself.

Evidence (decisive lines)

run.log: blksize 512/2147483648/16/2147483648 for sect_size 9/31/36/63; fixed: attach skipped.

PoC changes

DMA refill hook moved into the interposed wait/completion (bzero ordering); first rig version was self-defeating.

Verified recommended fix

validate lbafmt->sect_size (9..12) before the shift

Verdict

The verbatim nvme_admin_state_identify_ns computes nsc->blksize = 1 << lbafmt->sect_size (nvme_admin.c:673-675) with sect_size a device-controlled uint8_t from the Identify Namespace DMA and no range check. The rig refills the crafted Identify response at the DMA-completion boundary (after the drivers bzero of req->info - the first version filled too early and the bzero wiped it). Results: sect_size=9 -> 512 (legal control); 31 -> 0x80000000 (2GiB blocks); 36 -> 16 (shift count masked to 5 bits - C11 UB); 63 -> 0x80000000. The garbage blksize is what nvme_disk_attach publishes as the namespace media block size (geometry/media-size corruption; divisor in I/O sizing). Fixed source (reject formats outside 9..12, skip attach) A/B: attach count 0, no garbage published.