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)
PoC verification
Evidence pack
findings/poc/DF-0715 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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 PoCbuild.sh/run.shβ build/run scriptsfix.diffβ git-apply-able fix (rejectlen <= 0insmb_memdupin+smb_memdup)VERDICT.mdβ full analysispanic.txtβ panic signature from serial consolerun.log/fix_run.logβ unpatched / patched run outputfix_build.logβ patched kernel+module build logenv.txtβ guest environment
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)
nsmb_dev_ioctl(smb_dev.c:171) dispatchesSMBIOC_OPENSESSIONβsmb_usr_opensession(smb_usr.c:164).smb_usr_opensessionβsmb_usr_vc2spec(smb_usr.c:171βsmb_usr.c:60).smb_usr_vc2speccallssmb_memdupin(dp->ioc_server, dp->ioc_svlen)withioc_svlen = 0(smb_usr.c:74).- 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_PTRDragonFlykmalloc(0)returnsZERO_LENGTH_PTR = ((void*)-8)(kern_slaballoc.c:193,889-890), not NULL.copyin(umem, p, 0)returns 0 (zero-length copy), sosmb_memdupinreturns(void*)-8. - 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). smb_usr_opensessionβsmb_sm_lookup(smb_usr.c:176) βsmb_sm_lookupint(empty VC list β ENOENT) β sinceSMBV_CREATEis set,smb_vc_create(smb_conn.c:202,417).- THE PANIC β
smb_vc_createcallsdup_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_lenat address0xFFFFFFFFFFFFFFF8β 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 freshwith-srcresets. - Patched (#1 kernel + rebuilt smbfs.ko): applied
fix.diffto/usr/src, rebuiltsmbfs.komodule (makeinsys/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.
Recommended fix
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
fixedVALIDATED: 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.
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.
No comments yet.