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

Infinite loop in response queue processing when target mode consumes multiple entries

Summary

isp_intr at isp.c:5121-5124: while(tsto != oop) { optr = ISP_NXT_QENTRY(tsto, ...); }. Loop assigns to optr but NEVER modifies tsto or oop - condition is invariant. If initially true (isp_handle_other_response advanced tsto via target-mode ATIO7 long-IU continuation), unconditional infinite loop in hard interrupt context. Requires ISP_TARGET_MODE compiled (not default) and adapter in target role. Remote FC initiator sends Extended CDB/IU >56 bytes triggering long-IU path in isp_target_notify. Permanent kernel hang. Fix: change while to if.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1126 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source simulates queue pointers; proves loop body never advances tsto 2.8 KB view raw
fix.diff suggested-fix change while (tsto != oop) to if (tsto != oop) 781 B view raw
build.sh build-script cc -O2 -o harness harness.c 95 B view raw
run.sh run-script timeout 10 ./harness 67 B view raw
build.log build-log final successful build 13 B view raw
run.log run-log decisive run incl non-termination marker 377 B view raw
env.txt environment uname, cc version, pciconf (no ISP FC adapter) 403 B view raw
VERDICT.md verdict full narrative + ISP_TARGET_MODE gating discussion 3.1 KB ↓ raw
README.md readme finding summary + build/run/expected 1.5 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 finding summary + build/run/expected
↓ download raw

DF-1126 β€” Infinite loop in isp_intr response-queue processing (SCSI target mode)

Finding

isp_intr at sys/dev/disk/isp/isp.c:5121-5124:

while (tsto != oop) {
    optr = ISP_NXT_QENTRY(tsto, RESULT_QUEUE_LEN(isp));
}

The loop body assigns to optr but never modifies tsto or oop β€” the condition is invariant. isp_handle_other_response (:5113) passes &tsto to isp_target_notify (isp_target.c:5988), which in the long-IU continuation path (isp_target.c:178-181) advances *optrp (= tsto). After return, tsto != oop and the loop spins forever in hard interrupt context β†’ permanent kernel hang.

Requires ISP_TARGET_MODE compiled (not default) and a target-mode FC adapter receiving an Extended CDB/IU > 56 bytes from a remote initiator.

Reachability on this guest

NOT reachable. No QLogic ISP FC adapter present. ISP_TARGET_MODE is not compiled into the default modules. Doubly gated. Latent.

A userspace harness demonstrates the invariant loop (capped at 1M iters to prove non-termination rather than actually hang).

Build / Run / Expected

cc -O2 -o harness harness.c     # build.sh
./harness                        # run.sh
# Expected: "LOOP DID NOT ADVANCE tsto -- infinite loop confirmed"

Files

  • harness.c β€” simulates the queue bookkeeping; proves the loop body never advances tsto.
  • fix.diff β€” changes while (tsto != oop) to if (tsto != oop).
  • build.log / run.log / env.txt β€” captured outputs.
VERDICT.md verdict full narrative + ISP_TARGET_MODE gating discussion
↓ download raw

VERDICT β€” DF-1126

Verdict: REPRODUCED (primitive) / NOT REACHABLE on guest (HW-gated)

The cited bug is real and confirmed by source trace + userspace loop-bookkeeping demonstration. It is a latent infinite-loop / hang primitive requiring ISP_TARGET_MODE compiled (not default) and a target-mode FC adapter receiving an Extended CDB/IU.

Mechanism (confirmed path:line)

  1. isp_intr (sys/dev/disk/isp/isp.c:5112-5124) processes response queue entries. For unrecognized entry types it calls isp_handle_other_response(isp, etype, hp, &tsto) (:5113) where tsto is a local initialized to oop.
  2. isp_handle_other_response (:5964-6022) for RQSTYPE_ATIO etc. calls isp_target_notify(isp, hp, optrp) (:5988) inside #ifdef ISP_TARGET_MODE.
  3. isp_target_notify (sys/dev/disk/isp/isp_target.c:104) for an RQSTYPE_ATIO7 (IS_24XX) with a long IU (at_ta_len > QENTRY_LEN-8) advances *optrp in a loop at :178-181: *optrp = ISP_NXT_QENTRY(*optrp, RESULT_QUEUE_LEN(isp)).
  4. Back in isp_intr:5121-5124: c while (tsto != oop) { optr = ISP_NXT_QENTRY(tsto, RESULT_QUEUE_LEN(isp)); } The body assigns to optr but never modifies tsto or oop. After isp_target_notify advanced tsto past oop, the condition is invariantly true β†’ infinite spin in hard interrupt context β†’ permanent kernel hang.

Reproduction (userspace harness)

The harness simulates the queue pointers, calls a stand-in for isp_handle_other_response that advances tsto by one slot, then runs the exact kernel loop body (capped at 1M iterations to prove non-termination rather than actually hang):

oop  = 100
tsto = 101  (advanced by isp_target_notify long-IU path)
Hit iteration cap (1000000) without termination.
tsto before loop = 101, tsto after = 101 (UNCHANGED)
*** LOOP DID NOT ADVANCE tsto -- infinite loop confirmed ***

Impact ceiling

  • Per-trigger: permanent kernel hang in hard interrupt context. The CPU spinning in isp_intr cannot be preempted; the system is wedged.
  • Privilege: remote β€” a FC initiator sending an Extended CDB/IU > 56 bytes triggers it. Requires target-mode FC adapter with ISP_TARGET_MODE.
  • Realistic: doubly gated (ISP_TARGET_MODE not default + FC target role). Niche but remote-triggerable when configured.

Fix

fix.diff changes while to if:

if (tsto != oop) {
    optr = ISP_NXT_QENTRY(tsto, RESULT_QUEUE_LEN(isp));
}

The body was always meant to run at most once (it only computes the next optr from the updated tsto). The while was a latent bug because the body never advances the loop variables.

Validated: isp.ko builds with RC=0 after applying the fix.

Fix validation

  • Patch applies cleanly: Hunk #1 succeeded at 5114.
  • make in sys/dev/disk/isp/ β†’ isp.ko + all FC firmware modules linked, ISP_RC=0.
  • Cannot boot-test (no QLogic ISP FC adapter + no ISP_TARGET_MODE); fix_status: not_testable.

PoC changes

  • harness.c written from scratch. Simulates queue pointers and proves the loop body never advances tsto (capped at 1M iters).

Fix verification

not_testable

compile validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. isp_intr while(tsto!=oop) body never advances -> infinite loop. Doubly gated (ISP_TARGET_MODE+HW).