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

smb_memdupin signed-length validation accepts zero (ZERO_LENGTH_PTR panic) and negative (huge kmalloc panic) lengths

Summary

smb_subr.c:137-148 smb_memdupin takes int len validated only with signed > 8*1024 check. len==0: DragonFly kmalloc(0) returns ZERO_LENGTH_PTR=((void*)-8) (kern_slaballoc.c:193,889-890) NOT NULL. Sole caller smb_usr_vc2spec (smb_usr.c:74-76) checks == NULL misses sentinel. spec->sap flows to dup_sockaddr (smb_conn.c:462) which reads sa->sa_len at address (void*)-8 non-canonical x86-64 GPF panic. len<0: signed check passes kmalloc casts to size_t wraps to huge panics in slab allocator. small positive len (e.g. 1) with byte 0xC8: dup_sockaddr reads sa_len=200 bcopy 200 bytes from 16-byte slab heap OOB read ~184 bytes into adjacent slab. Pre-auth fires BEFORE smb_suser (smb_conn.c:428). Device /dev/nsmb* is 0700 root-only (smb_dev.c:355-356). Fix: if(len<=0||len>8*1024) return NULL.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0715 Β· 14 files
FileTypeDescriptionSize
smb_memdupin_zero.c trigger-source opens /dev/nsmb, issues SMBIOC_OPENSESSION with ioc_svlen=0 to trigger ZERO_LENGTH_PTR panic 4.6 KB view raw
build.sh build-script cc -o smb_memdupin_zero smb_memdupin_zero.c 301 B view raw
run.sh run-script kldload smbfs + ./smb_memdupin_zero (root) 507 B view raw
fix.diff suggested-fix reject len<=0 in smb_memdupin and smb_memdup (smb_subr.c:140,157) 491 B view raw
build.log build-log trigger PoC build output 122 B view raw
run.log run-log unpatched run: panic signature from serial console 739 B view raw
fix_run.log fix-run-log patched run: ENOMEM, no panic 264 B view raw
fix_build.log fix-build-log patched kernel + smbfs.ko module build log 5.6 MB ↓ download
panic.txt panic-signature Fatal trap 12 at dup_sockaddr+0x18, fault addr 0xfffffffffffffff8 577 B view raw
env.txt environment uname, kern.version, cc version, kldstat, /dev/nsmb perms 380 B view raw
VERDICT.md verdict full narrative: mechanism, data flow, fix validation 5.4 KB ↓ raw
README.md readme summary + build/run/expected 1.9 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme summary + build/run/expected
↓ download raw

DF-0715 β€” smb_memdupin signed-length validation accepts zero

Summary

smb_memdupin (sys/netproto/smb/smb_subr.c:137) validates its int len parameter only with a signed > 8*1024 check. When len == 0, DragonFly's kmalloc(0) returns the sentinel ZERO_LENGTH_PTR = ((void*)-8) (not NULL), which the sole caller smb_usr_vc2spec (smb_usr.c:74-76) fails to catch with its == NULL check. The sentinel pointer flows to dup_sockaddr (smb_conn.c:462) which dereferences sa->sa_len at address 0xFFFFFFFFFFFFFFF8 β€” a non-canonical x86-64 address β€” causing a kernel panic (fatal trap 12 / GPF). When len < 0, the signed check passes and kmalloc receives a wrapped-to-huge size_t, panicking in the slab allocator.

Impact

Low — root→kernel DoS. The only path is /dev/nsmb (0700 root:wheel). No unprivileged escalation; no memory-corruption primitive (the GPF fires before any write).

Build

cc -o smb_memdupin_zero smb_memdupin_zero.c

Run (must be root; needs smbfs.ko loaded)

kldload smbfs        # admin precondition: load the SMB client module
./smb_memdupin_zero

Expected (bug present β€” unpatched)

Kernel panic:

Fatal trap 12: page fault while in kernel mode
fault virtual address = 0xfffffffffffffff8
Stopped at dup_sockaddr+0x18: movzbl (%rdi),%edi

Guest goes down (DDB prompt on serial console).

Expected (bug fixed β€” patched)

SMBIOC_OPENSESSION: Cannot allocate memory
ioctl returned -1 (errno=12)

Guest stays up; no panic.

Files

  • smb_memdupin_zero.c β€” trigger PoC
  • build.sh / run.sh β€” build/run scripts
  • fix.diff β€” git-apply-able fix (reject len <= 0 in smb_memdupin + smb_memdup)
  • VERDICT.md β€” full analysis
  • panic.txt β€” panic signature from serial console
  • run.log / fix_run.log β€” unpatched / patched run output
  • fix_build.log β€” patched kernel+module build log
  • env.txt β€” guest environment
VERDICT.md verdict full narrative: mechanism, data flow, fix validation
↓ download raw

DF-0715 β€” smb_memdupin signed-length validation accepts zero

Verdict

REPRODUCED — root→kernel panic (DoS). The bug is real and confirmed by a clean Fatal trap 12 at dup_sockaddr+0x18 reading address 0xfffffffffffffff8 (= ZERO_LENGTH_PTR). The fix (if (len <= 0 || len > 8*1024) return NULL) closes it: the patched kernel+module returns ENOMEM cleanly, no panic.

Severity / impact

Low — root→kernel DoS. The only path to smb_memdupin is via /dev/nsmb (the nsmb clone device), which is created 0700 root:wheel (smb_dev.c:355-356). An unprivileged user cannot open the device (vfs.usermount=0, no devfs rules loosening nsmb). An admin who has loaded smbfs.ko (normal SMB-client setup) presents the device to root, and root can then panic the kernel. No memory-corruption primitive is derived — the sentinel pointer (void*)-8 is dereferenced as a struct sockaddr *, causing a non-canonical-address GPF before any attacker-controlled write lands. This is a hardening gap, not an escalation vector.

Mechanism (trigger β†’ primitive β†’ effect)

Trigger

Open /dev/nsmb (root only) and issue SMBIOC_OPENSESSION with an smbioc_ossn struct where ioc_svlen == 0 (and ioc_server != NULL, ioc_user[0] != 0, ioc_localcs[0] != 0, ioc_opt = SMBVOPT_CREATE).

Data flow (every hop cited)

  1. nsmb_dev_ioctl (smb_dev.c:171) dispatches SMBIOC_OPENSESSION β†’ smb_usr_opensession (smb_usr.c:164).
  2. smb_usr_opensession β†’ smb_usr_vc2spec (smb_usr.c:171 β†’ smb_usr.c:60).
  3. smb_usr_vc2spec calls smb_memdupin(dp->ioc_server, dp->ioc_svlen) with ioc_svlen = 0 (smb_usr.c:74).
  4. THE BUG β€” smb_memdupin (smb_subr.c:137-148): c if (len > 8 * 1024) // 0 > 8192 == false β†’ passes return NULL; p = kmalloc(len, M_SMBSTR, M_WAITOK); // kmalloc(0) β†’ ZERO_LENGTH_PTR DragonFly kmalloc(0) returns ZERO_LENGTH_PTR = ((void*)-8) (kern_slaballoc.c:193,889-890), not NULL. copyin(umem, p, 0) returns 0 (zero-length copy), so smb_memdupin returns (void*)-8.
  5. Back in smb_usr_vc2spec: spec->sap = (void*)-8; if (spec->sap == NULL) return ENOMEM (smb_usr.c:75-76) β€” misses the sentinel (it is not NULL).
  6. smb_usr_opensession β†’ smb_sm_lookup (smb_usr.c:176) β†’ smb_sm_lookupint (empty VC list β†’ ENOENT) β†’ since SMBV_CREATE is set, smb_vc_create (smb_conn.c:202,417).
  7. THE PANIC β€” smb_vc_create calls dup_sockaddr(vcspec->sap) (smb_conn.c:462). dup_sockaddr (uipc_socket2.c:809) does: c sa2 = kmalloc(sa->sa_len, M_SONAME, M_INTWAIT); reading ((struct sockaddr *)-8)->sa_len at address 0xFFFFFFFFFFFFFFF8 β€” a non-canonical x86-64 address β†’ GPF β†’ Fatal trap 12.

Why the privilege check doesn't help

smb_suser(cred) (smb_conn.c:428) runs inside smb_vc_create, after smb_memdupin has already returned the sentinel. The check only gates uid/gid selection (smb_conn.c:431-434); it does not block the dup_sockaddr call at line 462. The real privilege boundary is the /dev/nsmb device open permission (0700 root:wheel).

The len < 0 variant

With ioc_svlen < 0, the signed check len > 8*1024 is false (negative is never > 8192), and kmalloc((size_t)negative, ...) wraps to a huge value, panicking in the slab allocator. The fix (len <= 0) closes both variants with one check.

Exploit chain

Not applicable — this is a pure DoS / hardening gap, not a memory-corruption primitive. The ZERO_LENGTH_PTR sentinel is dereferenced as a pointer, causing a GPF before any attacker-controlled data reaches a write target. No heap grooming, no victim object, no escalation chain is possible. The finding is correctly classified as Low severity (root→kernel DoS).

PoC changes

Authored from scratch (no prior PoC existed in findings/poc/DF-0715/): - smb_memdupin_zero.c β€” opens /dev/nsmb, fills smbioc_ossn with ioc_svlen=0 + SMBVOPT_CREATE, issues SMBIOC_OPENSESSION. Built with the guest's installed <netsmb/smb_dev.h>. - build.sh / run.sh β€” exact build/run commands. - fix.diff β€” git-apply-able unified diff: if (len <= 0 || len > 8*1024) return NULL in both smb_memdupin and smb_memdup (smb_subr.c:140,157).

Fix validation (Phase 8)

  • Baseline (unpatched #0 kernel + unpatched smbfs.ko): PoC β†’ Fatal trap 12: page fault while in kernel mode, fault virtual address = 0xfffffffffffffff8, Stopped at dup_sockaddr+0x18: movzbl (%rdi),%edi. Guest down. Reproduced twice from fresh with-src resets.
  • Patched (#1 kernel + rebuilt smbfs.ko): applied fix.diff to /usr/src, rebuilt smbfs.ko module (make in sys/vfs/smbfs/), installed to /boot/kernel/smbfs.ko, kldload smbfs. PoC β†’ SMBIOC_OPENSESSION: Cannot allocate memory (errno=12 = ENOMEM), exit 0, guest stays up. Reproduced twice (deterministic).
  • fix_status: fixed β€” clean before/after contrast.

fix.diff changes smb_memdupin and smb_memdup (both in sys/netproto/smb/smb_subr.c) from if (len > 8 * 1024) return NULL to if (len <= 0 || len > 8 * 1024) return NULL. This rejects zero (which causes kmalloc(0) β†’ ZERO_LENGTH_PTR sentinel confusion) and negative (which wraps to a huge size_t in kmalloc) with a single signed check. Matches the finding proposal (if(len<=0||len>8*1024) return NULL).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: baseline panic at dup_sockaddr+0x18 (fault 0xfffffffffffffff8); patched ENOMEM errno=12, guest up x2.

BEFORE #0: Fatal trap 12 at dup_sockaddr+0x18, guest down. AFTER #1: ENOMEM errno=12, guest up x2.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Mon Jul 13 13:17:21 UTC 2026 (smbfs.ko rebuilt with fix)

Confirmed kernel references

Detail

Exploit chain

none -- pure DoS. ZERO_LENGTH_PTR GPF before any write. Root-only (/dev/nsmb 0700).

Evidence (decisive lines)

BASELINE #0: Fatal trap 12 at dup_sockaddr+0x18, fault 0xfffffffffffffff8, guest down. PATCHED #1: ENOMEM errno=12, guest up x2.

PoC changes

Authored smb_memdupin_zero.c from scratch. Added fix.diff (len<=0 check), build.sh, run.sh, VERDICT.md, manifest.json.

Verified recommended fix

Change both smb_memdupin:140 and smb_memdup:157 from if (len > 8 * 1024) to if (len <= 0 || len > 8 * 1024). Matches finding proposal. Full git-apply-able diff in findings/poc/DF-0715/fix.diff.

Verdict

REPRODUCED. smb_memdupin validates len only with >81024 check, so len==0 passes. kmalloc(0) returns ZERO_LENGTH_PTR=((void)-8). Caller checks ==NULL (misses sentinel). dup_sockaddr reads sa->sa_len at 0xFFFFFFFFFFFFFFF8 -> GPF. Panic: Fatal trap 12 at dup_sockaddr+0x18.