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

TOCTOU race: interrupt completion re-reads unchecked user buffer sizes from ioctl ucmd

Summary

iir_intr at iir.c:1600-1626: completion path re-reads ucmd->u.{ioctl.param_size,cache.BlockCnt,raw.sdlen} from live user memory (no copyin/snapshot). Validated at submit (:1015-1037) but tsleep window allows concurrent rewrite. Inflated size -> bcopy(gc_scratch,ucmd->data,cnt) OOB reads 3072B scratch into adjacent kernel heap -> user memory. Root only. Fix: snapshot validated cnt in gccb at submit, use snapshot at completion.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1424 Β· 8 files
FileTypeDescriptionSize
fix.diff suggested-fix Re-clamp cnt to GDT_SCRATCH_SZ at interrupt completion before bcopy. 1.2 KB view raw
VERDICT.md verdict Full source-trace analysis 2.0 KB ↓ raw
build.sh build-script Kernel build validation 527 B view raw
run.sh run-script PoC runner (not runnable on guest) 484 B view raw
fix_build.log build-log Full kernel build output (make nativekernel rc=0) 5.6 MB ↓ download
env.txt environment Guest environment 277 B view 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
VERDICT.md verdict Full source-trace analysis
↓ download raw

DF-1424 β€” Verification Verdict

Verdict: CONFIRMED-BY-SOURCE-TRACE (root-only)

Status: inconclusive (HW-gated / not reachable as unprivileged maxx) Impact: none (cannot reproduce on QEMU guest β€” no GPU/HW, or root/operator-only) Confidence: certain (source-trace confirmed bug is real)

Mechanism

iir_intr (:1600-1626): completion path re-reads ucmd->u.{ioctl.param_size, cache.BlockCnt, raw.sdlen} from live user memory (no copyin/snapshot). Validated at submit (:1015-1037) but tsleep window allows concurrent rewrite. Inflated size β†’ bcopy(gc_scratch,ucmd->data,cnt) OOB reads 3072B scratch into adjacent kernel heap β†’ user memory. /dev/iir0 mode 0600 (root only).

Source: sys/dev/raid/iir/iir.c:1600-1626

Why it cannot be reproduced on this guest

Root-only. /dev/iir0 is mode 0600 (root only). Also requires IIR RAID controller hardware.

Phase 6: Escalation Assessment

This is a Root-only + HW-gated (iir RAID) finding. The primitive is not reachable from the unprivileged maxx user on this guest (no hardware / module not loaded / root-only device). No escalation chain is possible because the trigger path is not exercisable.

For GPU findings: the module (radeon.ko/amdgpu.ko/i915.ko) is a loadable module not present in the GENERIC kernel and requires actual GPU hardware absent from the QEMU guest. For root/operator findings: the device node is mode 0600 or 0640 root:operator, and maxx (uid 1001) has no operator group membership.

Fix

Re-validate cnt against GDT_SCRATCH_SZ at completion before bcopy.

Fix description: Re-clamp cnt to GDT_SCRATCH_SZ at interrupt completion before bcopy.

The full git-apply-able diff is in fix.diff. It applies cleanly to the audit source tree and compiles as part of the kernel build (validated via make nativekernel rc=0).

Classification

  • status: inconclusive
  • reproduced: 0
  • impact: none
  • fix_status: not_testable (HW-gated: PoC cannot run on guest; diff applies + compiles verified)

Confirmed kernel references

Detail

Exploit chain

none (TOCTOU β†’ OOB read/write primitive β€” but trigger is root-only (/dev/iir0 0600) AND requires IIR RAID HW. Not reachable from unprivileged maxx.)

Evidence (decisive lines)

Source trace: iir.c:1610 'cnt = ucmd->u.ioctl.param_size' β€” re-read from live user memory at completion. :1612 'bcopy(gccb->gc_scratch, ucmd->data, cnt)' β€” uses re-read (possibly rewritten) cnt without re-validation.

PoC changes

Authored fix.diff: re-clamp cnt to GDT_SCRATCH_SZ at interrupt completion before bcopy (defense-in-depth against TOCTOU).

Verified recommended fix

Re-clamp cnt to GDT_SCRATCH_SZ at interrupt completion before bcopy. Simplified from finding proposal (snapshot-in-gccb requires struct change; re-clamp is minimal). Full diff in findings/poc/DF-1424/fix.diff.

Verdict

CONFIRMED BY SOURCE TRACE. iir_intr (:1600-1626): completion path re-reads ucmd->u.{ioctl.param_size, cache.BlockCnt, raw.sdlen} from live user memory. Validated at submit but tsleep window allows concurrent rewrite. Inflated size β†’ bcopy(gc_scratch,ucmd->data,cnt) OOB reads 3072B scratch. Bug is real but root-only and HW-gated.