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

Missing payload length validation in sbp_recv1 allows stale/uninitialized heap data to drive control flow and leak to userspace

Summary

sbp_recv1 casts FireWire receive payload to struct sbp_status (32 bytes) and reads all fields without verifying actual bytes received >= sizeof(struct sbp_status). recv buffer allocated without M_ZERO. Malicious device sending short WREQB status block causes tail of sbp_status to contain stale data. Stale fields influence control flow (src resp dead len orb_lo) and copied to userspace via SCSI sense buffer. [VERIFIED pass2: honest downgrade - rig fault reproduces with zeroed payload too so rig artifact; source chain stands firewire kmalloc no M_ZERO plus unvalidated 32-byte cast feeding pass sense; M_ZERO fix compile-validated]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2516 Β· 8 files
FileTypeDescriptionSize
README.md β€” 968 B ↓ raw
VERDICT.md β€” 2.4 KB ↓ raw
build.sh β€” 99 B view raw
run.log β€” 364 B view raw
env.txt β€” 963 B view raw
fix.diff β€” 622 B view raw
manifest.json β€” 699 B view raw
verdict.json β€” 3.1 KB view raw

DF-2516 -- sbp_recv1 reads unvalidated payload tail (short WREQB status blocks)

Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_sbp (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_sbp.ko; sysctl kern.dfv_sbp_run=2

Expected

source chain + partial rig observations (stale tail retained; stale-driven debug path taken); no mechanical end-to-end capture.

Fix validation

fix.diff allocates recv payloads with M_ZERO (kills the stale class at the root); firewire module compiled clean.

Verdict: not_reproduced / impact=none (see verdict.json, VERDICT.md)

VERDICT.md
↓ download raw

DF-2516 verdict -- sbp_recv1 reads unvalidated payload tail (short WREQB status blocks)

status: not_reproduced reproduced: 0 impact: none confidence: likely attempts: 3 runtime_sec: 900

Honest downgrade after deeper rig work: the rig kernel fault observed during sbp_recv1 dispatch reproduces with a fully ZEROED payload as well, i.e. that crash is a rig fabrication artifact (a NULL in the fabricated firewire graph reached by the verbatim epilogue), NOT the stale-data effect - it is not usable as reproduction evidence. What stands is source-certain and was partially observed: fw_xfer_alloc_buf allocates recv.payload with kmalloc(recv_len, type, M_INTWAIT) and NO M_ZERO (firewire.c:963-964 - recycled heap); sbp_recv1 casts the payload to struct sbp_status (32 bytes) and consumes every field with no delivered-length check (the rig showed the non-zeroed payload retain its 0xA5 tail across the call and the stale-driven SBP_DEBUG prints); sbp_scsi_status then copies stale-derived bytes into ocb->ccb->csio.sense_data, which pass(4) exports to userland - a hostile FireWire device sending a short WREQB discloses stale kernel heap bytes through the sense path. The mechanism is real; the end-to-end mechanical demonstration was not achieved (rig state depth). Fix (M_ZERO at allocation) compile-validated in the firewire module build.

Evidence (decisive lines)

rig observations (buggy arm): payload bytes 1..31 remain 0xA5 after delivery of 1 byte; verbatim sbp_recv1 runs the stale-driven debug prints and sbp_dequeue_ocb walk; kernel fault follows - but the SAME fault occurs with a zeroed payload, so the fault is a rig artifact (fake firewire graph NULL), not the defect. firewire module build with M_ZERO fix: BUILD OK.

Exploit chain / privesc assessment

hostile FireWire device -> stale kernel heap bytes drive control flow and reach the CCB sense buffer toward userland; ceiling: limited info leak.

Fix + A/B

fix.diff allocates recv payloads with M_ZERO (kills the stale class at the root); firewire module compiled clean.

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

Confirmed kernel references

Detail

Exploit chain

hostile FireWire device -> stale kernel heap bytes drive control flow and reach the CCB sense buffer toward userland; ceiling: limited info leak.

Evidence (decisive lines)

source chain + partial rig observations (stale tail retained; stale-driven debug path taken); no mechanical end-to-end capture.

PoC changes

the crash artifact is documented rather than claimed; sbp struct-extraction required several rounds (unbalanced #if 0 in a line-range extract; col-0 inner braces defeating naive brace matching).

Verified recommended fix

allocate fw_xfer recv payloads with M_ZERO

Verdict

Honest downgrade after deeper rig work: the rig kernel fault observed during sbp_recv1 dispatch reproduces with a fully ZEROED payload as well, i.e. that crash is a rig fabrication artifact (a NULL in the fabricated firewire graph reached by the verbatim epilogue), NOT the stale-data effect - it is not usable as reproduction evidence. What stands is source-certain and was partially observed: fw_xfer_alloc_buf allocates recv.payload with kmalloc(recv_len, type, M_INTWAIT) and NO M_ZERO (firewire.c:963-964 - recycled heap); sbp_recv1 casts the payload to struct sbp_status (32 bytes) and consumes every field with no delivered-length check (the rig showed the non-zeroed payload retain its 0xA5 tail across the call and the stale-driven SBP_DEBUG prints); sbp_scsi_status then copies stale-derived bytes into ocb->ccb->csio.sense_data, which pass(4) exports to userland - a hostile FireWire device sending a short WREQB discloses stale kernel heap bytes through the sense path. The mechanism is real; the end-to-end mechanical demonstration was not achieved (rig state depth). Fix (M_ZERO at allocation) compile-validated in the firewire module build.