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

pipe_size sysctl lacks power-of-two validation: non-pow2 buffer size silently aliases FIFO slots (in-bounds cross-process data corruption) on misconfigured systems

Field Value
ID DF-2752
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:N
CWE CWE-20 Improper Input Validation
File sys/kern/sys_pipe.c
Lines 125-127, 367-371 (indexing :539, :794)
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

The FIFO maps logical byte counters to physical slots with rindex & (size-1) / windex & (size-1), which is modulo only when size is a power of two. pipespace() page-aligns and clamps size to [16384, 1MB] but never validates the power-of-two property, and the writable sysctl kern.pipe.size accepts any int (e.g. 20480). With a non-pow2 size two distinct live offsets map to the same slot (e.g. 0x5000 & 0x4FFF == 0x4000), so a writer overwrites unread data β€” silent data corruption between the pipe's users. All accesses remain in-bounds, so integrity-only; requires a privileged sysctl write (an administrator misconfiguration, not attacker-reachable). Sibling FreeBSD code guards this with a power-of-two check.

In pipespace(), after the clamps: if (size & (size - 1)) size = 1UL << (fls(size) - 1); if (size < 16384) size = 16384; and optionally guard the sysctl writer with the same check.

Timeline

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

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2752 Β· 5 files
FileTypeDescriptionSize
pipefinal.c β€” 780 B view raw
pipetest2.c β€” 1.4 KB view raw
pipemap.c β€” 1.0 KB view raw
run.log β€” 921 B view raw
fix.diff β€” 595 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched kernel: pipespace rounds non-power-of-2 sizes up; with kern.pipe.size=20480 the previously-corrupting round trip now passes byte-perfect (0/20480 corrupted; baseline 8192/20480).

pipefinal on patched kernel at size=20480: corrupted bytes: 0 / 20480 PASS
↓ 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

root sysctl kern.pipe.size=20480 -> pipe() -> write 20480 -> read 20480 -> 8192 bytes silently replaced by other in-flight data

Evidence (decisive lines)

pipefinal.c; df2752_pipefinal.log: 8192/20480 corrupted at 20480 and 24576, 0 at 32768; block-map aliasing proof

PoC changes

first two test patterns were degenerate (period 256 dividing the 10240 alias distance); prime-period pattern made the corruption deterministic

Verified recommended fix

--- sys/kern/sys_pipe.c 2026-06-29 12:51:19.000000000 +0000

Verdict

Reproduced live as root-only sysctl misconfiguration. kern.pipe.size accepts any int (sys/kern/sys_pipe.c:126-127); pipespace clamps only to [16384, 1MB] (:367-371) and never enforces power-of-two, but the FIFO index masks are windex & (size - 1) / rindex & (size - 1) (:539, :794). With kern.pipe.size=20480 a single full-pipe write/read round-trip corrupted 8192 of 20480 bytes (range [2048..10239]); with 24576 likewise 8192 bytes (range [4096..12287]); the 32768 control was byte-perfect. A block-map run shows the aliasing directly: reader blocks 8..17 receive source blocks 40..49 because absolute offsets 10240..20479 wrap onto slots 2048..12287 - the mask 20479 (0x4FFF) is not a modulo mask, so distinct live offsets alias the same slot and the writer overwrites unread data. All accesses stay in-bounds (silent cross-process data corruption, not memory unsafety). Root-gated; a user with kern.pipe.size write access corrupts every new pipe on the system. First test used a 256-periodic byte pattern that collided with the alias distance (10240*7 % 256 == 0) and falsely passed; prime-period pattern exposed the corruption.