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

Unvalidated FCP response/sense lengths cause OOB read from kernel stack (24XX FC)

Summary

isp_intr 24XX FC response-queue path: rlen=((isp24xx_statusreq_t*)sp)->req_response_len read from firmware DMA without validation (isp.c:5225). snsp=((isp24xx_statusreq_t*)sp)->req_rsp_sense; snsp+=rlen at :5238-5240 - req_rsp_sense[28] is last field of 64-byte stack qe[QENTRY_LEN]. If rlen>=28, snsp points past qe. XS_SAVE_SENSE at :5307-5308 memcpy(&xs->sense_data, snsp, imin(32, slen)) reads up to 32 bytes from wild pointer. Remote FC target sends FCP_RSP IU with FCP_RSP_LEN>=28 -> kernel panic (unmapped page) or kernel stack info leak via CAM autosense. No auth needed beyond FC fabric presence. Fix: clamp rlen to sizeof(req_rsp_sense) and slen to sizeof(req_rsp_sense)-rlen.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1125 Β· 12 files
FileTypeDescriptionSize
harness.c trigger-source userspace replica of isp.c 24XX FCP response/sense arithmetic + WITH-FIX pass 6.9 KB view raw
build.sh build-script cc -O2 -Wall -Wextra -o harness harness.c 114 B view raw
run.sh run-script ./harness 60 B view raw
build.log build-log final build, full output 8 B view raw
run.log run-log decisive run incl CONFIRMED + FIX VALIDATED 1.7 KB view raw
env.txt environment uname, cc, pciconf (no FC HW) 241 B view raw
fix.diff suggested-fix clamp rlen<=28 and slen<=28-rlen in isp.c 1.4 KB view raw
fix_validation.txt fix-validation apply-check + compile (rc=0) + harness fix-demo 1.7 KB view raw
VERDICT.md verdict full narrative 2.9 KB ↓ raw
README.md readme summary + repro 2.6 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
README.md readme summary + repro
↓ download raw

DF-1125 β€” isp 24XX FCP response/sense length OOB read

Verdict

CONFIRMED (source-trace + harness) β€” INCONCLUSIVE on-guest (HW-gated). The bug is real; it cannot be triggered on the audit QEMU guest because the guest has no QLogic 24XX Fibre Channel HBA and no FC fabric (virtio net/display only).

Bug (one line)

isp_handle_intr_response() reads req_response_len straight from firmware DMA and does snsp = req_rsp_sense + rlen without clamping; req_rsp_sense[28] is the last field of the 64-byte stack queue entry, so any rlen >= 28 drives snsp past the entry, and XS_SAVE_SENSE memcpy()s up to 32 bytes from the wild pointer.

Mechanism (path:line)

  • sys/dev/disk/isp/isp.c:5042 β€” uint8_t qe[QENTRY_LEN]; (QENTRY_LEN = 64), a stack buffer; sp = (isp24xx_statusreq_t *)qe.
  • ispmbox.h:654-668 β€” isp24xx_statusreq_t is exactly 64 bytes; req_rsp_sense[28] is the final field (offset 36), filling the entry to the boundary.
  • isp.c:5225 β€” rlen = sp->req_response_len; (read from firmware DMA, unvalidated).
  • isp.c:5238-5240 β€” snsp = sp->req_rsp_sense; snsp += rlen; slen = sp->req_sense_len; with no bound; rlen >= 28 => snsp >= qe+64.
  • isp.c:5307-5308 β€” XS_SAVE_SENSE(xs, snsp, slen) => isp_freebsd.h:483 memcpy(&xs->sense_data, snsp, imin(XS_SNSLEN, slen)) reads up to 32 bytes from the wild pointer.

Trigger / threat model

A remote Fibre Channel target sending an FCP_RSP IU with FCP_RSP_LEN (mapped to req_response_len) >= 28. amdgpu_ucode_validate-class guards do not apply; there is no length validation anywhere on the 24XX path. Small rlen => kernel stack info leak into CAM autosense (readable via REQUEST SENSE); large rlen => panic from unmapped page. CVSS AV:A (adjacent fabric), no auth beyond fabric presence.

Reproduction on the audit guest

Not possible β€” no QLogic 24XX FC HBA on the QEMU guest. harness.c is a faithful userspace replica of the kernel arithmetic, built and run as the unprivileged maxx user. It proves the OOB read primitive concretely and also runs the fixed (clamped) logic to show 0 OOB remain.

Build / run

cc -O2 -Wall -Wextra -o harness harness.c   # build.sh
./harness                                      # run.sh

Expected: DF-1125: CONFIRMED OOB read primitive then DF-1125 FIX: VALIDATED - clamps eliminate all OOB reads.

Fix

fix.diff clamps rlen to sizeof(req_rsp_sense) (28) and slen to sizeof(req_rsp_sense) - rlen so snsp + slen cannot exceed the entry. Applies cleanly (git apply --check OK); compiles in the GENERIC kernel build (rc=0).

VERDICT.md verdict full narrative
↓ download raw

DF-1125 β€” VERDICT

Verdict: CONFIRMED via source-trace + userspace harness. On-guest: INCONCLUSIVE (HW-gated β€” no QLogic 24XX FC HBA on the QEMU guest).

Root-cause confirmation

The bug is unambiguous in the audited source. The 24XX (QLogic FC) response-queue path in isp_handle_intr_response() consumes two lengths β€” req_response_len and req_sense_len β€” directly from firmware DMA with no validation, then performs pointer arithmetic snsp = sp->req_rsp_sense + rlen where req_rsp_sense[28] is the final field of a 64-byte stack queue entry (uint8_t qe[QENTRY_LEN] at isp.c:5042). Because isp24xx_statusreq_t is exactly 64 bytes (ispmbox.h:654-668), any firmware-supplied rlen >= 28 advances snsp to/past the end of qe. The subsequent XS_SAVE_SENSE(xs, snsp, slen) (isp.c:5307) expands to memcpy(&xs->sense_data, snsp, imin(XS_SNSLEN, slen)) (isp_freebsd.h:483), reading up to 32 bytes from the wild pointer into CAM autosense.

Evidence

  • harness.c (run as unprivileged maxx on the audit guest) replicates the exact arithmetic and shows that 7/7 firmware-controlled rlen values (including rlen=28, the boundary) read past the 64-byte entry; a leak demo copies 32 bytes of an adjacent-stack canary (all 0xA1) into the "sense" buffer β€” a kernel-stack info leak of up to 32 bytes (and, for large rlen, a guaranteed unmapped-page panic).
  • Full run: run.log. Build: build.log. Environment (no FC HW): env.txt.

Exploit chain / impact

This is an OOB read (CWE-125), not a write primitive, so there is no escalation chain to uid=0. Realistic impact ceiling: kernel stack info leak up to 32 bytes (sufficient for KASLR/text-pointer defeat and stack-residue disclosure) when a malicious FC target chooses a small rlen; a large rlen yields a panic (DoS) from an unmapped page. AV:A, no auth beyond FC fabric presence.

Fix validation

fix.diff clamps rlen to sizeof(req_rsp_sense) (28) and slen to sizeof(req_rsp_sense) - rlen at the two points the lengths enter use. - git apply --check -p1 => OK. - Compiles in the GENERIC kernel build (full nativekernel, rc=0; isp.o built clean). - Harness "WITH FIX" pass: 0/7 OOB cases remain. - Boot-test of the stripped patched kernel could not complete (loader-format artifact on kernel.stripped, unrelated to the fix); the bug path is HW-gated regardless. - fix_status: not_testable (HW-gated runtime; apply-check + compile + harness fix-demo + trace all pass).

PoC changes

The evidence pack was authored from scratch (the seeded dir was empty): harness.c (userspace replica of the kernel path + a "WITH FIX" pass), build.sh, run.sh, fix.diff (git-apply-able), VERDICT.md, manifest.json, full logs.

Kernel refs (confirmed during verification)

sys/dev/disk/isp/isp.c:5042, :5225, :5238, :5239, :5240, :5307; sys/dev/disk/isp/ispmbox.h:654-668, :290 (QENTRY_LEN); sys/dev/disk/isp/isp_freebsd.h:480-484.

Fix verification

not_testable

compile+harness validated

module build rc=0 + harness 0 OOB

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed+harness. isp_handle_intr_response rlen unvalidated -> snsp OOB -> 32B stack info leak. No QLogic FC HBA.