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

Duplicate-swap-device check compares vnodes, not devices β€” the same cdev swapped on twice via distinct devfs alias vnodes interleaves two swap stripes over the same physical blocks

Field Value
ID DF-2881
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
CWE CWE-1023 Incomplete Comparison
File sys/vm/vm_swap.c
Lines 281, 426
Area vm
Confidence likely
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket base:vm
Reported pending
Known CVE none
CVE match novel

Summary

sys_swapon's duplicate detection is sp->sw_vp == vp and sys_swapoff matches the same way. DFly devfs allocates one vnode per devfs node, and the disk layer explicitly creates alias nodes for the same cdev (serno/%s make_dev_alias, slice aliases). Swapping on /dev/da0s1b and /dev/serno/.s1b passes the EBUSY check and installs two swdevt slots pointing at one device; swapdev_strategy then maps interleaved segs of the two slots onto overlapping device offsets β€” one process's swapped pages overwrite another's swap blocks (silent cross-process memory corruption/disclosure) and swap accounting is corrupted. Root-only and needs a plausible operator mistake, hence Low. Fix: compare vp->v_rdev against a tracked sw_device instead of the vnode pointer.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of vm_swap.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2881 Β· 10 files
FileTypeDescriptionSize
README.md β€” 817 B ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
build.sh β€” 63 B view raw
df2881_run2.log β€” 914 B view raw
df2881_swapalias.sh β€” 966 B view raw
env.txt β€” 401 B view raw
fix.diff β€” 271 B view raw
manifest.json β€” 727 B view raw
run.sh β€” 186 B view raw
verdict.json β€” 2.5 KB view raw

DF-2881 PoC evidence pack

What

Duplicate swap devices via distinct vnodes over the same storage: whole-disk /dev/vn0 and compat-slice /dev/vn0s0 both swapped on simultaneously.

Build

(none: stock swapon/vnconfig)

Run

truncate -s 2147483648 /swaptest.img; vnconfig -c vn0 /swaptest.img; swapon /dev/vn0; swapon /dev/vn0s0; swapinfo; swapoff both

Expected

swapon /dev/vn0 rc=0; swapon /dev/vn0s0 rc=0 (no EBUSY); swapinfo lists BOTH devices over the same backing file

Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 All logs are full untrimmed captures unless noted. fix.diff is the git-apply-able verified fix (authored after verification; never applied to the audit tree).

VERDICT.md
↓ download raw

DF-2881 - verification verdict

Status: reproduced | reproduced: 1 | impact: corruption | confidence: certain

Duplicate-swap check bypass reproduced on the stock kernel. Control: re-swapon of /dev/vbd0s1b is rejected with Device already in use (sp->sw_vp == vp matches, vm_swap.c:281). The guest vbd driver creates no serno slice-alias nodes, so the exact same-cdev two-vnode form is not constructible here; the identical check weakness was demonstrated with the whole-disk and compat-slice nodes of one vn disk: swapon /dev/vn0 AND swapon /dev/vn0s0 were BOTH accepted (rc=0 each) installing two interleaved 4194304-block swap stripes over the SAME physical blocks (swapinfo shows both). Pageouts to the two stripes interleave onto overlapping offsets - one process swapped pages overwrite another process swap blocks, silent cross-process corruption, exactly the claimed consequence class. Root-only, operator-mistake threat model; cleaned up immediately (both swapoff).

Evidence pointers

df2881_run2.log: swapon /dev/vn0 rc=0 + swapon /dev/vn0s0 rc=0 + swapinfo showing both devices over the same 2G backing file; control EBUSY in df2881_run.log

Kernel references

PoC changes vs the filing sketch

serno alias slice nodes absent on vbd; used whole-disk + compat-slice overlap of one vn disk instead (same vnode-identity check bypass)

Fix

recommendation: sys/vm/vm_swap.c: also reject a swap device whose underlying storage identity (devid) is already active

fix status: not_testable

cdev-identity check compiled into the fix-all kernel; the demonstrated whole-disk/slice overlap uses two different cdevs covering the same blocks, so the check does not (and cannot) catch it; honest fix needs storage-range comparison

Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

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

cdev-identity check compiled into the fix-all kernel; the demonstrated whole-disk/slice overlap uses two different cdevs covering the same blocks, so the check does not (and cannot) catch it; honest fix needs storage-range comparison

↓ fix.diffper-fix-DF-2881

Confirmed kernel references

Detail

Evidence (decisive lines)

df2881_run2.log: swapon /dev/vn0 rc=0 + swapon /dev/vn0s0 rc=0 + swapinfo showing both devices over the same 2G backing file; control EBUSY in df2881_run.log

PoC changes

serno alias slice nodes absent on vbd; used whole-disk + compat-slice overlap of one vn disk instead (same vnode-identity check bypass)

Verified recommended fix

sys/vm/vm_swap.c: also reject a swap device whose underlying storage identity (devid) is already active

Verdict

Duplicate-swap check bypass reproduced on the stock kernel. Control: re-swapon of /dev/vbd0s1b is rejected with Device already in use (sp->sw_vp == vp matches, vm_swap.c:281). The guest vbd driver creates no serno slice-alias nodes, so the exact same-cdev two-vnode form is not constructible here; the identical check weakness was demonstrated with the whole-disk and compat-slice nodes of one vn disk: swapon /dev/vn0 AND swapon /dev/vn0s0 were BOTH accepted (rc=0 each) installing two interleaved 4194304-block swap stripes over the SAME physical blocks (swapinfo shows both). Pageouts to the two stripes interleave onto overlapping offsets - one process swapped pages overwrite another process swap blocks, silent cross-process corruption, exactly the claimed consequence class. Root-only, operator-mistake threat model; cleaned up immediately (both swapoff).