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

sp->sw_nblks is a 32-bit int but swaponvp() stores a 64-bit block count β€” swap devices >= 2^31 pages (8 TiB) truncate to negative/garbage bounds while vm_swap_size still counts the full size

Field Value
ID DF-2880
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H
CWE CWE-197 Integer Overflow or Wraparound
File sys/vm/vm_swap.c
Lines 344, 367 (struct conf.h:181)
Area vm
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket base:vm
Reported pending
Known CVE none
CVE match novel

Summary

struct swdevt.sw_nblks is int while aligned_nblks is swblk_t=int64_t; the assignment silently truncates. The :344 guard only bounds nblks by BLIST_MAXBLKS/nswdev = 2^59 pages β€” no guard for real hardware. An β‰₯8TiB swap device (trivially creatable, incl. sparse NFS file) makes sw_nblks negative/wrapped: swapdev_strategy then rejects all I/O to the device while nswap/vm_swap_size/vm_swap_max still count petabytes of "available" swap β€” kernel overcommits against a device that can never take a page, degrading into swap-full/OOM-kill churn; blist_create for nswapβ‰₯2^33 additionally attempts a multi-GB M_WAITOK kmem allocation. Root-only and β‰₯8TiB, hence Low; no OOB enabled (truncation only makes the bound more restrictive). Fix: widen sw_nblks/sw_nused to swblk_t or reject nblks > INT_MAX pages at registration.

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-2880 Β· 3 files
FileTypeDescriptionSize
run.log β€” 1.1 KB view raw
memhog.c β€” 384 B view raw
fix.diff β€” 459 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

int64 sw_nblks: 8TiB swapon accounts fully and vn0s0 accepts pageouts with ZERO EINVAL console spam under the same memory pressure that killed the stock kernel (baseline: endless error 22 pageout failures).

df2880_ab2.log: no I/O error lines; watermarks 85941283/128911925
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Sat Oct 24 17:14:55 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (v26 fix-all kernel; sys_generic.c fix-v2 rebuild pending A/B)

Confirmed kernel references

Detail

Exploit chain

8TiB swap file -> swapon accepted (ineffective BLIST bound) -> sw_nblks int truncates to INT_MIN -> swapdev_strategy EINVALs every pageout (error-22 spam) while vm.swap_size counts 8TiB -> swap-death wedge

Evidence (decisive lines)

df2880_baseline.log: error-22 spam + Used=0 on vn0s0 under pressure + 8TiB accounting; df2880_ab2.log: patched kernel zero I/O errors

PoC changes

swapon on a regular file refused (Block device required) - used vnconfig on a sparse file instead

Verified recommended fix

--- sys/sys/conf.h 2026-06-29 12:51:19.000000000 +0000

Verdict

Reproduced with an 8TiB sparse swap file (vnconfig + swapon) on the stock kernel. struct swdevt.sw_nblks is int (sys/sys/conf.h:181) while aligned_nblks is swblk_t (int64); the :344 BLIST_MAXBLKS/nswdev check is ineffective because BLIST_MAXBLKS with 64-bit swblk_t is 0x2000000000000000 - the 8TiB device (2^31 pages) was accepted and the global blist resized in ~33s. sw_nblks truncated to INT_MIN: pstat displays the size as |INT_MIN|x8 pages unsigned, vm.swap_size counted the full 8TiB (2148532096 pages - overcommit against a device that can never take a page), and under memory pressure swapdev_strategy rejected EVERY pageout to the device with EINVAL - the serial console filled with thousands of swap_pager: I/O error - pageout failed; ... error 22 lines and the guest wedged in swap-death (pageout spinning on rejected I/O) while the real 4GB device took pages. Fix A/B on the patched kernel (int64 sw_nblks): the same 8TiB swapon produced full accounting (watermarks 85941283/128911925), ZERO I/O-error lines under the same pressure, and vn0s0 accepted pageouts (the guest thrashed normally on file-backed swap instead of wedging on rejected I/O).