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

brk/sbrk mappings are created RWX (VM_PROT_ALL as both prot and max_protection) β€” executable heap defeats future W^X policy

Field Value
ID DF-2965
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:N
CWE CWE-732
File sys/vm/vm_unix.c
Lines 137-141, 223-227
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

Both sys_sbrk and sys_obreak pass VM_PROT_ALL (RWX) as both the protection and max_protection arguments to vm_map_find, so every page of the malloc/brk heap is mapped read-write-execute. On amd64 the pmap honors VM_PROT_EXECUTE via the NX bit, so heap memory is genuinely executable. This is historical BSD behavior (FreeBSD's obreak does the same) and adds no exposure beyond what an unprivileged process already gets from mmap(PROT_READ|PROT_WRITE|PROT_EXEC); filed as defense-in-depth so the heap is not an implicit W^X escape hatch the day W^X enforcement lands β€” and so shellcode stashed by a memory-corruption bug can run directly from the heap without a second mprotect step. Fix: VM_PROT_RW (optionally keep max_protection=ALL as a compat escape hatch).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of vm_unix.c (GLM 5.3). DF-0961/0962 re-verified still present, not re-reported.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2965 Β· 10 files
FileTypeDescriptionSize
README.md β€” 640 B ↓ raw
VERDICT.md β€” 1.5 KB ↓ raw
brk_exec.c β€” 1.0 KB view raw
build.sh β€” 65 B view raw
env.txt β€” 509 B view raw
fix.diff β€” 604 B view raw
manifest.json β€” 498 B view raw
run.log β€” 51 B view raw
run.sh β€” 71 B view raw
verdict.json β€” 2.6 KB view raw

DF-2965 β€” brk/sbrk mappings RWX (Info-tier hardening verification)

What

brk/sbrk mappings RWX

Build

cc -Wall -O2 -o brk_exec brk_exec.c

Run

./brk_exec

Expected

baseline (stock kernel): see verdict below fix kernel (all-41-fixes build): the bad behavior is gone

Files

README.md VERDICT.md brk_exec.c build.sh env.txt fix.diff manifest.json run.log run.sh verdict.json

Shared harness: h41.c (combined KLD, sysctl debug.h41.cmd=N driven). Full batch context: the fix kernel contains all 41 hardening fixes of this batch compiled together (build RC=0) and passed the complete A/B suite plus a clean reboot.

VERDICT.md
↓ download raw

DF-2965 β€” VERDICT

status: reproduced | reproduced: 1 | impact: none | confidence: certain

Mechanically reproduced on the stock guest: sbrk(4096) + copying a 5-byte ret-42 thunk into the page + calling it printed 42 - the brk heap is genuinely executable (sys_sbrk vm_unix.c:141 and sys_obreak :227 pass VM_PROT_ALL as BOTH prot and max_protection; on amd64 the pmap honors VM_PROT_EXECUTE). A/B nuance: with the fix (prot=VM_PROT_RW, max_protection kept VM_PROT_ALL as a compat escape hatch) the brk vm_map entry now reports rw- (/proc/self/map: 0x402000-0x406000 rw- NCOW NNC none), but CALLING heap code still succeeds on this guest - a control mmap(PROT_READ|PROT_WRITE) is equally executable here, i.e. this QEMU vCPU configuration does not enforce NX for user pages despite machdep.pmap_nx_enable=1 and CPUID NX. The kernel-side mapping change is real and verified; hardware enforcement of the dropped EXEC bit cannot be demonstrated on this platform configuration. Filed as defense-in-depth (historical BSD behavior; adds nothing beyond mmap RWX which any process can request), so impact stays none.

Platform note

On the fix kernel the brk vm_map entry reports rw- (0x402000-0x406000 rw- NCOW NNC none) where stock reports rwx; calling heap code still succeeds because THIS QEMU vCPU configuration does not enforce NX for user pages (control: plain mmap RW is also executable despite machdep.pmap_nx_enable=1).

Baseline vs fix kernel

baseline: see verdict above / run log fix: behavior changed as designed (see verdict)

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched reproduced

vm_map-level fix verified (brk entry rw-); execution still succeeds because NX is not enforced by this vCPU configuration for ANY mapping (control experiment) - the max_protection=ALL escape hatch additionally permits mprotect back to X by design.

↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Oct 26 01:57:23 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (h41 fix kernel, all 41 hardening fixes applied)

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log baseline: ret=42 (heap exec); fix kernel: /proc/self/map brk entry rw- (was rwx) + NX-platform control (plain RW mmap also executable)

PoC changes

added /proc/self/map protection dump and an RW-mmap exec control to separate map-level from plevel enforcement

Verified recommended fix

Map brk VM_PROT_RW (optionally keep max VM_PROT_ALL as compat escape hatch)

Verdict

Mechanically reproduced on the stock guest: sbrk(4096) + copying a 5-byte ret-42 thunk into the page + calling it printed 42 - the brk heap is genuinely executable (sys_sbrk vm_unix.c:141 and sys_obreak :227 pass VM_PROT_ALL as BOTH prot and max_protection; on amd64 the pmap honors VM_PROT_EXECUTE). A/B nuance: with the fix (prot=VM_PROT_RW, max_protection kept VM_PROT_ALL as a compat escape hatch) the brk vm_map entry now reports rw- (/proc/self/map: 0x402000-0x406000 rw- NCOW NNC none), but CALLING heap code still succeeds on this guest - a control mmap(PROT_READ|PROT_WRITE) is equally executable here, i.e. this QEMU vCPU configuration does not enforce NX for user pages despite machdep.pmap_nx_enable=1 and CPUID NX. The kernel-side mapping change is real and verified; hardware enforcement of the dropped EXEC bit cannot be demonstrated on this platform configuration. Filed as defense-in-depth (historical BSD behavior; adds nothing beyond mmap RWX which any process can request), so impact stays none.