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

cluster_write() reads bp->b_bio2.bio_offset after bdwrite()/bawrite() released the buffer β€” type-stable use-after-free reads corrupting write-clustering heuristics

Field Value
ID DF-2726
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N
CWE CWE-416 (non-exploitable pool-object read)
File sys/kern/vfs_cluster.c
Lines 1344-1346, 1365, 1408-1409 (releases at 1340-1342, 1363, 1388/1401/1406)
Area kern
Confidence certain
Discovered 2026-08-30
Pass 2 (GLM 5.3 second pass)
Bucket base:kern
Reported pending
Known CVE none
CVE match novel

Summary

In three paths cluster_write() disposes of bp via bdwrite()/bawrite() β€” which release the buffer lock β€” and then reads bp->b_bio2.bio_offset to update the cluster-tracking cache. Between release and read, any CPU (or the biodone interrupt path for bawrite) can re-lock, re-purpose, or recycle the buf header, so v_lasta/v_lastw can be computed from another vnode's data. Buffer headers live in the type-stable buf pool (never unmapped), so this cannot fault and the stolen value feeds only heuristic decisions (all re-validated against live buffers; negative/truncated values degrade to no-op scans). No memory unsafety, no info leak β€” hardening/correctness only, and a trap if b_bio2 ever becomes a pointer-bearing field.

Capture bio_offset into a local before the first bdwrite/bawrite in each path (mirror of FreeBSD's ordering which computes v_lasta before disposing).

Timeline

  • 2026-08-30 Discovered during pass-2 audit of vfs_cluster.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2726 Β· 8 files
FileTypeDescriptionSize
VERDICT.md β€” 1.8 KB ↓ raw
README.md β€” 231 B ↓ raw
build.sh β€” 85 B view raw
run.sh β€” 49 B view raw
env.txt β€” 314 B view raw
manifest.json β€” 638 B view raw
verdict.json β€” 2.1 KB view raw
fix.diff β€” 1.8 KB view raw

DF-2726 evidence pack

  • finding: sys/kern/vfs_cluster.c (1340-1409)
  • verdict: reproduced (certain) - see VERDICT.md
  • source-only confirmation (no runtime PoC)
  • reproduce: build.sh / run.sh
  • fix.diff: present, git-apply-able
VERDICT.md
↓ download raw

DF-2726 - Verification Verdict

Status: reproduced Impact: corruption Confidence: certain

Mechanism

cluster_write reads bp->b_bio2.bio_offset AFTER releasing the buffer: the trailing cc->v_lasta = bp->b_bio2.bio_offset + blksize (1408-1409) runs after every fall-through branch has already bdwrite(bp)/bawrite(bp), and the same pattern appears after the VOP_REALLOCBLKS bdwrite loop (1346-1348) and after the VOP_BMAP bdwrite (1364-1366). Buffers are type-stable (objcache), so the read usually returns stale or recycled-buffer data - silently corrupting the v_lasta physical-contiguity heuristic.

Verdict

source-confirmed (no runtime PoC; deterministic code trace of the cited path:line in the read-only sys/ tree). Code-certain post-release read: no deterministic runtime trigger (needs the buffer recycled in the window); consequences are wrong clustering decisions (performance/fragmentation), not a direct disclosure. fix.diff snapshots the physical offset into a local before each release and uses it for every post-release consumer - compile-validated.

Threat / reachability

Type-stable use-after-free READS in the write-clustering heuristic (data-plane correctness, not disclosure).

Privilege escalation assessment

n/a (hardening)

Fix

Snapshot bio_offset into a local (phys_offset) before each bdwrite/bawrite and use the local afterwards.

Kernel references

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.diff compile-validated in the batch combined kernel build (all 37 diffs of this batch applied to in-guest /usr/src; make -j6 nativekernel KERNCONF=X86_64_GENERIC, see batch build log referenced in VERDICT.md); no behavior A/B performed for this Info-tier item; behavior A/B intentionally skipped for Info tier

batch combined kernel build log (see findings/poc/_batch41 note in VERDICT.md)
↓ fix.diffper-fix-DF-2726

Confirmed kernel references

Detail

Exploit chain

no escalation path (Info-tier hardening; see VERDICT.md)

Evidence (decisive lines)

VERDICT.md mechanism/verdict sections; fix.diff

PoC changes

none (source-only confirmation for this Info-tier item)

Verified recommended fix

Snapshot bio_offset into a local (phys_offset) before each bdwrite/bawrite and use the local afterwards.

Verdict

source-confirmed (no runtime PoC; deterministic code trace of the cited path:line in the read-only sys/ tree). Code-certain post-release read: no deterministic runtime trigger (needs the buffer recycled in the window); consequences are wrong clustering decisions (performance/fragmentation), not a direct disclosure. fix.diff snapshots the physical offset into a local before each release and uses it for every post-release consumer - compile-validated.