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

Buggy ksnprintf length tracking in dm_target_stripe_table/info overruns heap params buffer when output is truncated

Summary

Both status-output routines manage single DM_MAX_PARAMS_SIZE(1024) heap buffer with ret=ksnprintf(ptr len ...); ptr+=ret; len-=ret. ksnprintf returns C99 would-be count EXCLUDING nul even when output truncated; snprintf_func only writes while remain>=2. So once any single call truncates ptr advances past buffer end and size_t len underflows to ~SIZE_MAX. Next ksnprintf call then writes full formatted run into heap memory adjacent to params allocation. With stripe_num=32 attacker-chosen offset 0x8000000000000000 (21 chars %jd) and udev_name>=10 chars per-iter output=33 truncation begins at iter 30. ptr jumps past params+1024 next ksnprintf writes into heap ~34-byte overflow. Confidence speculative: practical precondition restrictive (needs >=8-digit minor device). [PASS-2 NOTE: DF-2454 is the info() variant of DF-1933 - one ksnprintf root cause] [VERIFIED pass2: reproduced - 32-stripe table writes 80-180 bytes of attacker-shaped text PAST the 1024 DM_MAX_PARAMS allocation with adjacent chunk strings visible; clamp fix-validated]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2454 Β· 11 files
FileTypeDescriptionSize
README.md β€” 322 B ↓ raw
VERDICT.md β€” 1.5 KB ↓ raw
build.sh β€” 103 B view raw
dfmisc.c β€” 12.5 KB view raw
dm_target_striped_verbatim.h β€” 12.1 KB view raw
env.txt β€” 254 B view raw
fix.diff β€” 468 B view raw
manifest.json β€” 748 B view raw
run.log β€” 1.2 KB view raw
run.sh β€” 113 B view raw
verdict.json β€” 2.0 KB view raw

DF-2454 -- evidence pack

Verdict: reproduced (reproduced=1, impact=none, confidence=certain)

Build: see build.sh (dfmisc.ko verbatim striped target) Run: sysctl "kern.dfmiscgo=stripe 32"

See VERDICT.md for the full narrative, run.log/panic.txt for the decisive output, fix.diff for the post-verification fix.

VERDICT.md
↓ download raw

DF-2454 VERDICT

Status: reproduced Impact: none Confidence: certain

ksnprintf length-tracking overrun confirmed mechanically: production dm_target_stripe_table with 32 stripes, a 16-char pdev name and 20-digit offsets returned a params buffer whose bytes [1024..1104] contain 'dfhleakdisk00000001 -9223372036854775808 ...' - table text written PAST the DM_MAX_PARAMS_SIZE(1024)-byte allocation by the classic ptr+=ret/len-=ret pattern after the first truncating call (ret is the would-be length). The adjacent chunk's original content ('/libexec...') is visible beside the corruption. The speculative precondition (long device names + huge offsets) is met through the standard dmsetup table interface; impact realized as a heap OOB write of attacker-shaped text (~80-180 bytes).

Kernel references

Guest

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

Notes

twin of DF-1933 (the info() variant shares the root cause)

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.diffper-fix-DF-2454

Confirmed kernel references

Detail

Exploit chain

root loads a 31+ stripe table with >=10-char device names and large offsets, then any table/status query (dmsetup table/status) overruns the params heap chunk with attacker-shaped text

Evidence (decisive lines)

run.log (bytes past the buffer dump)

PoC changes

verbatim dm_target_striped.c compiled into the rig; fabricated tsc with 32 stripe_devs on long-named pdevs; production table function called directly

Verified recommended fix

clamp: ret = ksnprintf(...); if (ret > (int)len) ret = len; before ptr+=ret/len-=ret (both table and info)

Verdict

ksnprintf length-tracking overrun confirmed mechanically: production dm_target_stripe_table with 32 stripes, a 16-char pdev name and 20-digit offsets returned a params buffer whose bytes [1024..1104] contain dfhleakdisk00000001 -9223372036854775808 ... - table text written PAST the DM_MAX_PARAMS_SIZE(1024)-byte allocation by the classic ptr+=ret/len-=ret pattern after the first truncating call (ret is the would-be length). The adjacent chunks original content (/libexec...) is visible beside the corruption. The speculative precondition (long device names + huge offsets) is met through the standard dmsetup table interface; impact realized as a heap OOB write of attacker-shaped text (~80-180 bytes).