smb_vc_create error path NULL-deferences vc_iod via smb_vc_disconnect (trivially-triggerable kernel panic via invalid charset name)
| Field | Value |
|---|---|
| ID | DF-0599 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-476 NULL Pointer Dereference |
| File | sys/netproto/smb/smb_conn.c |
| Lines | 460-514 (create), 550-556 (gone), 678-684 (disconnect) |
| Area | netproto/smb (Netsmb in-kernel SMB client) |
| Confidence | certain |
| Discovered | 2026-07-02 |
| Reported | pending |
Summary
If smb_vc_create fails on any step before smb_iod_create() succeeds
(most easily: any of the four iconv_open() calls at lines 486/489/494/498
returning ENOENT for an attacker-supplied charset name), the error
cleanup at line 513 calls smb_vc_put(vcp) β smb_co_put β smb_co_gone
β cp->co_gone = smb_vc_gone β smb_vc_disconnect β
smb_iod_request(vcp->vc_iod, ...). vc_iod is still NULL because
smb_zmalloc zeroed it and smb_iod_create never ran.
smb_iod_request (sys/netproto/smb/smb_iod.c:394-415) immediately executes
SMB_IOD_EVLOCK(iod) = spin_lock(&iod->iod_evlock), dereferencing address
~offset_of(iod_evlock) and panicking the kernel. A second, related variant
exists if smb_iod_create itself fails at kthread_create_compat
(smb_iod.c:704-710): it kfrees iod but does NOT clear vcp->vc_iod,
so the same call chain UAFs the freed iod.
Root cause
Trace the error path in smb_vc_create (sys/netproto/smb/smb_conn.c:460-514):
460: do {
461: error = ENOMEM;
462: vcp->vc_paddr = dup_sockaddr(vcspec->sap);
...
486: error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); /* user-controlled */
487: if (error != 0)
488: break;
...
504: error = smb_iod_create(vcp); /* only place vc_iod is set */
505: if (error != 0)
506: break;
507: *vcpp = vcp;
509: error = 0;
510: } while (0);
511:
512: if (error)
513: smb_vc_put(vcp, scred);
smb_vc_put β smb_co_put (lines 338-364) drops usecount from 1 to 0,
sets SMBO_GONE, releases+reacquires the lockmgr lock, and invokes
smb_co_gone (lines 250-268). smb_co_gone line 254 dispatches
cp->co_gone, which for a VC is smb_vc_gone (lines 550-556):
550: static void
551: smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
552: {
553: struct smb_vc *vcp = CPTOVC(cp);
554:
555: smb_vc_disconnect(vcp); /* unconditional */
556: }
smb_vc_disconnect (lines 678-684) unconditionally dereferences vc_iod:
678: static int
679: smb_vc_disconnect(struct smb_vc *vcp)
680: {
681:
682: smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL);
683: return 0;
684: }
iconv_open (sys/libiconv/iconv.c:233-267) returns ENOENT for any charset
pair that has no registered converter. The from argument here is
vcspec->localcs, which is the user-supplied ioc_localcs field of
smbioc_ossn (smb_usr.c:74, 92) β only validated as non-empty (smb_usr.c:69).
smb_usr_opensession / smb_usr_lookup do NOT validate that
localcs/servercs correspond to a real kernel iconv cspair, so an invalid
name like "BogusCS-1" reaches smb_vc_create and reliably triggers the
panic.
The UAF variant is the same except vc_iod points at freed memory instead
of NULL (smb_iod.c:708 kfrees iod without vcp->vc_iod = NULL).
Threat model & preconditions
- Attacker position: local attacker with
/dev/nsmb*access (mode0700 root:rootper smb_dev.c:356 β so root, or any process able to reach the ioctl via a setuid helper likemount_smbfs). Indirectly, any unprivileged user who can convince a privileged process to mount an SMB share from a server whose negotiated parameters causeiconv_opento fail (e.g. kernel built without the requested charset module loaded) panics the host. - Privileges gained or impact: kernel panic (NULL deref β page fault in kernel mode β fatal trap). Local DoS of the entire kernel. Because the crash happens during connection setup before any data is exchanged with the server, no authentication or server-side cooperation is required for the local trigger β only the attacker's choice of charset string matters.
- Required config or capabilities: Netsmb kernel module loaded,
/dev/nsmb*device accessible. The iconv module for the requested charset must not be loaded (the default on most systems). - Reachability:
open("/dev/nsmb0")βioctl(fd, SMBIOC_OPENSESSION, &ssn)withssn.ioc_localcsset to any string that does not match a registered kiconv cspair.
Proof of concept
PoC source: findings/poc/DF-0599/panic.c
Build & run
cc -I/usr/src/sys -I/usr/src/sys/netproto/smb -o panic panic.c sudo ./panic
Expected output
Immediate kernel panic with a trap 12 (page fault) at smb_iod_request+0xNN
while dereferencing NULL->iod_evlock:
Fatal trap 12: page fault while in kernel mode
cpuid = 0; apic id = 01
fault virtual address = 0x40 (offset of iod_evlock inside struct smb_iod)
[code] smb_iod_request+0x...: mov ...
db> tr
smb_iod_request+0x...
smb_vc_disconnect+0x...
smb_vc_gone+0x...
smb_co_gone+0x...
smb_co_put+0x...
smb_vc_put+0x...
smb_vc_create+0x...
The "THIS_CHARSET_DOES_NOT_EXIST_9" string simply must not match any
registered kiconv cspair; on a stock kernel without every iconv module
loaded, even reasonable-looking names like "KOI8-R" will reproduce the
bug if the corresponding module is not present.
Impact
- Blast radius: any DragonFly system using the in-kernel SMB client
with
/dev/nsmb*accessible to a privileged or confused-deputy process. - Severity rationale: Medium. Trivially-triggerable, deterministic
local kernel panic. Requires
/dev/nsmb*access (root). No info leak or code execution (it's a NULL-deref at a fixed small offset, andmap_at_zerois off by default on DragonFlyBSD, so no privilege escalation primitive). CVSS 3.1 base β 6.2. - Reliability: 100% β straight-line code path, no race.
Recommended fix
Make the gone hook tolerant of an absent iod, AND have smb_iod_create
leave vc_iod NULL on its own failure:
--- a/sys/netproto/smb/smb_conn.c
+++ b/sys/netproto/smb/smb_conn.c
@@ -549,7 +549,9 @@ static void
smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
{
struct smb_vc *vcp = CPTOVC(cp);
-
- smb_vc_disconnect(vcp);
+ /* vc_iod may be NULL if smb_vc_create failed before smb_iod_create
+ * succeeded; nothing to disconnect in that case. */
+ if (vcp->vc_iod != NULL)
+ smb_vc_disconnect(vcp);
}
@@ -678,6 +680,7 @@ smb_vc_disconnect(struct smb_vc *vcp)
{
+ KKASSERT(vcp->vc_iod != NULL);
smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL);
return 0;
Companion fix in smb_iod.c (required for the UAF variant):
--- a/sys/netproto/smb/smb_iod.c
+++ b/sys/netproto/smb/smb_iod.c
@@ -706,6 +706,7 @@
if (error) {
SMBERROR("can't start smbiod: %d", error);
+ vcp->vc_iod = NULL;
kfree(iod, M_SMBIOD);
return error;
}
Optionally also add early validation of localcs/servercs against the
registered iconv cspairs in smb_usr_vc2spec (smb_usr.c) so the failure
happens before any allocation, but the defensive checks above are the real
fix.
References
sys/netproto/smb/smb_iod.c:394-415(smb_iod_request) β where the NULL deref actually panics.sys/netproto/smb/smb_usr.c:69-92β the user-suppliedioc_localcs/ioc_servercsfields that flow unchecked intoiconv_open.sys/libiconv/iconv.c:233-267(iconv_open) β returnsENOENTfor unregistered cspairs.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0599 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| panic.c | trigger-source | minimal SMBIOC_OPENSESSION trigger with bogus localcs (NULL vc_iod panic) | 5.1 KB | view raw |
| build.sh | build-script | cc -Wall -O2 -o panic panic.c | 167 B | view raw |
| run.sh | run-script | kldload smbfs + ./panic (root) | 441 B | view raw |
| build.log | build-log | userland trigger build, full output | 63 B | view raw |
| run.log | run-log | baseline (unpatched) decisive run + panic signature | 1.3 KB | view raw |
| panic.txt | panic-signature | fatal trap 12 at smb_iod_request+0x58, fault 0x58 (from boot.log) | 776 B | view raw |
| env.txt | environment | uname, kern.version, cc, kldstat, patched smbfs.ko sha256 | 554 B | view raw |
| fix.diff | suggested-fix | guard smb_vc_gone (NULL vc_iod) + clear vc_iod in smb_iod_create failure | 867 B | view raw |
| fix_build.log | fix-build-log | patched smbfs.ko module build, full output (-Werror, rc=0) | 19.8 KB | view raw |
| fix_run.log | fix-run-log | patched-module decisive run (ENOENT, no panic) + 3x determinism | 1.3 KB | view raw |
| VERDICT.md | verdict | full narrative: mechanism, evidence, PoC changes, fix validation | 7.3 KB | β raw |
| README.md | readme | human-facing build/run/expected + reproduce notes | 2.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-0599 β PoC: smb_vc_create NULL-iod panic via invalid charset
Privileged local deterministic kernel panic. iconv_open returns ENOENT
for any charset pair that has no registered converter; smb_vc_create's error
path invokes smb_vc_gone β smb_vc_disconnect β
smb_iod_request(vcp->vc_iod=NULL, ...), which executes
SMB_IOD_EVLOCK(NULL) = smb_sl_lock(&NULL->iod_evlock) and page-faults.
Files
panic.cβ minimal reproducer (SMBIOC_OPENSESSIONwith an unregisteredioc_localcs).build.sh/run.shβ exact build/run commands.fix.diffβ git-apply-able fix (NULL-guard insmb_vc_gone+ clearvc_iodinsmb_iod_createfailure path).VERDICT.mdβ full root-cause confirmation + before/after fix validation.manifest.jsonβ machine-readable artifact catalog.- Logs:
build.log,run.log(baseline panic),panic.txt,fix_build.log,fix_run.log(patched no-panic),env.txt.
Preconditions
- DragonFly master DEV kernel
6.5-DEVELOPMENT #0(unpatched baseline). - The netsmb stack is a loadable module:
kldload smbfs(pulls inlibmchain.ko+libiconv.ko) creates/dev/nsmb. - No iconv converter modules loaded (the default) β any charset name yields
ENOENTfromiconv_open. We use"BOGUSCS-9"(β€15 chars;ioc_localcsis 16 bytes). - Must run as root β
/dev/nsmbis mode0700 root:wheel(smb_dev.c:356). On this guestmount_smbfsis0555 root:wheel(not setuid), so the direct ioctl trigger is the repro path. The realistic unprivileged vector is a privileged confused-deputy mounting with a negotiated charset whose module is absent.
Build & run
./build.sh # cc -Wall -O2 -o panic panic.c sudo ./run.sh # kldload smbfs ; ./panic (run as root)
Expected outcome
Unpatched #0 + original smbfs.ko β immediate kernel panic (serial
console captured in panic.txt / run.log):
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x58 Stopped at smb_iod_request+0x58: lock xaddl %edx,0x58(%rbx) db>
(%rbx = 0 β vc_iod == NULL; 0x58 = offset of iod_evlock in
struct smbiod.)
Patched smbfs.ko (rebuilt from fix.diff) β the ioctl returns cleanly:
[*] DF-0599: issuing SMBIOC_OPENSESSION with localcs="BOGUSCS-9" (size=488) [!] ioctl returned rc=-1 errno=2 (m) # ENOENT, no panic, guest stays up
Reproducing the fix validation
Because DF-0599 lives in the loadable smbfs.ko module (not the base kernel),
the fix is validated by rebuilding only that module:
# on the guest, with fix.diff applied to /usr/src: cd /usr/src/sys/vfs/smbfs && make cp /usr/obj/usr/src/sys/vfs/smbfs/smbfs.ko /boot/kernel/smbfs.ko && sync kldload smbfs ./panic # now returns ENOENT, no panic
See VERDICT.md for the full mechanism trace and before/after evidence.
DF-0599 β VERDICT
Verdict: REPRODUCED (panic), then FIX VALIDATED
The bug is real and deterministically triggerable on the audited
DragonFly master DEV kernel (6.5-DEVELOPMENT #0). A single
SMBIOC_OPENSESSION ioctl with an unregistered ioc_localcs charset name
panics the kernel via a NULL vc_iod dereference in smb_iod_request. The
authored fix.diff closes it: on the patched smbfs.ko the ioctl returns
ENOENT cleanly with no panic.
Mechanism (confirmed, every hop cited)
-
Trigger (root) β
open("/dev/nsmb")thenioctl(fd, SMBIOC_OPENSESSION, &ssn)withioc_localcs="BOGUSCS-9"./dev/nsmbis mode0700 root:wheel(sys/netproto/smb/smb_dev.c:356), so the direct trigger needs root (or a privileged confused-deputy such as a hypothetical setuidmount_smbfsβ on this guestmount_smbfsis0555 root:wheel, not setuid). The netsmb stack is a loadable module:kldload smbfsbrings insmbfs.ko+libmchain.ko+libiconv.koand creates/dev/nsmb. -
Dispatch β
SMBIOC_OPENSESSION(sys/netproto/smb/smb_dev.c:187-191) βsmb_usr_opensession(sys/netproto/smb/smb_usr.c:163-180) βsmb_usr_vc2spec(only validatesioc_user[0],ioc_server!=NULL,ioc_localcs[0]!=0; does not validate the charset against a real kiconv cspair βsmb_usr.c:65-72) βsmb_sm_lookupwithSMBV_CREATE. -
Create + charset failure β
smb_sm_lookup(smb_conn.c:202) callssmb_vc_create. Atsmb_conn.c:486it doesiconv_open("tolower", vcspec->localcs, &vcp->vc_tolower). With zero iconv converter modules loaded (only thelibiconv.koframework; no cspairs registered),iconv_open(sys/libiconv/iconv.c:233-267) finds no matching cspair and returnsENOENT(iconv.c:266). Thedo/whilebreaks. -
Error cleanup β NULL iod deref β
smb_vc_createerror path (smb_conn.c:512-513) callssmb_vc_put(vcp)βsmb_co_put(smb_conn.c:337-364) drops usecount to 0 βsmb_co_gone(smb_conn.c:249-268) β dispatchescp->co_gone = smb_vc_gone(smb_conn.c:550-556) βsmb_vc_disconnect(vcp)(smb_conn.c:678-684) βsmb_iod_request(vcp->vc_iod, ...). Butvc_iodis still NULL:smb_zmalloczeroed it (smb_conn.c:437) andsmb_iod_create(smb_conn.c:504) never ran. -
PANIC β
smb_iod_request(NULL, ...)executesSMB_IOD_EVLOCK(iod) = smb_sl_lock(&iod->iod_evlock)(sys/netproto/smb/smb_iod.c:403, macro atsmb_iod.c:58), which dereferencesNULL + offsetof(struct smbiod, iod_evlock)(=0x58on this build) and takes a page fault.
Evidence β baseline (unpatched #0 + original smbfs.ko)
Serial console (dfbsd-qemu/boot.log):
Fatal user address access from kernel mode from panic at ffffffff82609ab8 Fatal trap 12: page fault while in kernel mode cpuid = 0; lapic id = 0 fault virtual address = 0x58 fault code = supervisor write data, page not present instruction pointer = 0x8:0xffffffff82609ab8 kernel: type 12 trap, code=2 Stopped at smb_iod_request+0x58: lock xaddl %edx,0x58(%rbx) db>
IP 0xffffffff82609ab8 = smbfs.ko base 0xffffffff82600000 + smb_iod_request@0x9a60
+ 0x58. %rbx=0 β vc_iod==NULL. The fault address 0x58 is the offset of
iod_evlock inside struct smbiod (sys/netproto/smb/smb_conn.h:448), exactly
as predicted. Deterministic β straight-line code, no race.
(The finding predicted fault addr β 0x40; the actual offset is 0x58 because
iod_evlock sits after several pointer/int fields. Same bug, same function,
same root cause.)
PoC changes (what I authored and why)
The finding shipped no trigger source β only a README. I authored
panic.c from the cited path. One precondition fix during iteration:
- First run panicked at
dup_sockaddr+0x18(fault addr0x0), not atsmb_iod_request. Root cause: my first PoC leftioc_local=NULL, andsmb_vc_createcallsdup_sockaddr(vcspec->lap)unconditionally atsmb_conn.c:466β before reaching theiconv_openat line 486 β so it NULL-deref'dlapfirst. Fix: supply a validioc_localsockaddr so the flow reaches the charset error path. (This is an incidental additional latent NULL-deref in the same function βsmb_usr_vc2specskips settinglapwhenioc_local==NULLbutsmb_vc_createassumes it is set β same class, same trigger surface; noted but not the subject of DF-0599.) - Used a 9-char bogus charset (
"BOGUSCS-9") becauseioc_localcsis only 16 bytes; the README's"THIS_CHARSET_DOES_NOT_EXIST_9"would not fit. - Set
ioc_servercs[0]='\0'so only the twotolower/touppericonv_opencalls run (the trigger), skipping thetoserver/tolocalblock. - Defined the
smbioc_ossnstruct field-for-field fromsys/netproto/smb/smb_dev.h:65-83so the ioctlcopyinsize matches the kernel exactly (computed via_IOW('n',100,struct smbioc_ossn)).
Exploit chain
Not a memory-corruption primitive β it is a NULL-deref page fault at a fixed
small offset (0x58). map_at_zero is off by default on DragonFly, so the
fault address is unmappable and yields only a local kernel panic / DoS
(whole-host). No info leak, no code execution, no privilege escalation. The
finding's severity (Medium, CVSS 6.2, C:N/I:N/A:H) is accurate.
Fix (verified)
fix.diff (git-apply-able, plain unified diff so DragonFly patch -p1 also
accepts it) makes two minimal changes:
-
smb_vc_gone(sys/netproto/smb/smb_conn.c) β guard the disconnect:if (vcp->vc_iod != NULL) smb_vc_disconnect(vcp);. This is the real fix for the NULL-deref: whensmb_vc_createfailed beforesmb_iod_createsucceeded, there is no iod to disconnect from. -
smb_iod_create(sys/netproto/smb/smb_iod.c) β onkthread_create_compatfailure, setvcp->vc_iod = NULLbeforekfree(iod). This closes the related UAF variant the finding describes (smb_iod.c:699setsvc_iod=iod, then:708freesiodwithout clearingvc_iod, so the same gone-hook would UAF the freed iod).
This matches the finding's ## Recommended fix proposal (gone-hook guard
+ iod_create NULL-clear). It omits the optional KKASSERT in
smb_vc_disconnect (the guard makes it unreachable with NULL) and omits the
optional early localcs/servercs validation in smb_usr.c (defense in
depth, not required to close the panic).
Why I rebuilt the module, not nativekernel
smbfs is optional netsmb in sys/conf/files and ships as a loadable
module (sys/vfs/smbfs/Makefile, .PATH netproto/smb) β it is not
compiled into the X86_64_GENERIC kernel (confirmed: no smb symbols /
/dev/nsmb until kldload smbfs). Therefore make nativekernel would not
have picked up the fix. The correct validation unit was rebuilding only
smbfs.ko from patched source (cd /usr/src/sys/vfs/smbfs && make), replacing
/boot/kernel/smbfs.ko, and kldload-ing it. The base kernel stays #0
(unchanged) β the fix lives entirely in the module.
Before / after
| kernel | smbfs.ko | result | |
|---|---|---|---|
| baseline | #0 (unpatched) |
original | panic: smb_iod_request+0x58, fault 0x58, guest DOWN |
| patched | #0 (unchanged) |
rebuilt w/ fix | ioctl β ENOENT (errno 2), guest UP, no panic, deterministic Γ3 |
Fix verification
fixedVALIDATED. The PoC panicked deterministically on the unpatched #0 baseline + original smbfs.ko (fatal trap 12 at smb_iod_request+0x58, fault 0x58, guest DOWN), and does NOT panic on the single-fix smbfs.ko: the ioctl returns ENOENT (errno 2), the guest stays UP, and there are zero panic lines in the current boot.log across 3 repeat runs. The NULL vc_iod guard in smb_vc_gone() makes smb_vc_create's iconv_open(ENOENT) error path return gracefully instead of dereferencing NULL->iod_evlock. fix closes the bug. (NOTE: smbfs is a loadable module, so nativekernel would NOT have exercised the fix; the validation rebuilt the smbfs.ko module, which is the correct unit for this code.)
BASELINE (unpatched): Stopped at smb_iod_request+0x58: lock xaddl %edx,0x58(%rbx) ; fault virtual address = 0x58 ; guest DOWN. PATCHED (smbfs.ko rebuilt w/ fix.diff, base kernel still #0): [!] ioctl returned rc=-1 errno=2 (m) ; guest UP ; no panic ; deterministic x3. fix build: patched smbfs.ko built with -Werror, MODULE_BUILD_RC=0 (full log in fix_build.log).
Confirmed kernel references
- sys/netproto/smb/smb_dev.c:356
- sys/netproto/smb/smb_dev.c:187
- sys/netproto/smb/smb_usr.c:65
- sys/netproto/smb/smb_usr.c:163
- sys/netproto/smb/smb_conn.c:202
- sys/netproto/smb/smb_conn.c:437
- sys/netproto/smb/smb_conn.c:466
- sys/netproto/smb/smb_conn.c:486
- sys/netproto/smb/smb_conn.c:504
- sys/netproto/smb/smb_conn.c:512
- sys/netproto/smb/smb_conn.c:249
- sys/netproto/smb/smb_conn.c:550
- sys/netproto/smb/smb_conn.c:678
- sys/netproto/smb/smb_iod.c:403
- sys/netproto/smb/smb_iod.c:58
- sys/netproto/smb/smb_iod.c:699
- sys/netproto/smb/smb_iod.c:706
- sys/libiconv/iconv.c:266
Detail
Exploit chain
none. Not a memory-corruption primitive: it is a NULL-pointer page fault at a fixed small offset (0x58, the iod_evlock spinlock). map_at_zero is off by default on DragonFly so the fault address is unmappable; the only achievable effect is a local whole-host kernel panic (DoS). No info leak, code execution, or privilege escalation derivable.
Evidence (decisive lines)
BASELINE (unpatched #0 + original smbfs.ko), from dfbsd-qemu/boot.log: Fatal user address access from kernel mode from panic at ffffffff82609ab8 Fatal trap 12: page fault while in kernel mode fault virtual address = 0x58 instruction pointer = 0x8:0xffffffff82609ab8 Stopped at smb_iod_request+0x58: lock xaddl %edx,0x58(%rbx) (IP = smbfs.ko@0xffffffff82600000 + smb_iod_request@0x9a60 + 0x58; %rbx=0 => vc_iod==NULL) PATCHED (rebuilt smbfs.ko with fix.diff): [*] DF-0599: issuing SMBIOC_OPENSESSION with localcs="BOGUSCS-9" (size=488) [!] ioctl returned rc=-1 errno=2 (m) # ENOENT, no panic, guest UP (deterministic x3; 0 panic lines in current boot.log)
PoC changes
The finding shipped only a README (no trigger source). I authored panic.c: open(/dev/nsmb) + ioctl(SMBIOC_OPENSESSION) with ioc_localcs="BOGUSCS-9", ioc_opt=SMBVOPT_CREATE, and valid ioc_server/ioc_local sockaddrs (the struct smbioc_ossn is defined field-for-field from smb_dev.h so the ioctl copyin size matches). First build/run panicked at dup_sockaddr(vcspec->lap) (smb_conn.c:466) because ioc_local was NULL; fixed by supplying a valid ioc_local so the flow reaches the iconv_open ENOENT path at smb_conn.c:486. Authored build.sh/run.sh. Authored fix.diff (NULL-guard in smb_vc_gone + clear vc_iod in smb_iod_create failure path). Full narrative + before/after in findings/poc/DF-0599/VERDICT.md.
Verified recommended fix
In sys/netproto/smb/smb_conn.c smb_vc_gone(), guard the disconnect: 'if (vcp->vc_iod != NULL) smb_vc_disconnect(vcp);' β when smb_vc_create failed before smb_iod_create succeeded (e.g. iconv_open ENOENT) there is no iod to disconnect from. Plus in sys/netproto/smb/smb_iod.c smb_iod_create(), set 'vcp->vc_iod = NULL;' before kfree(iod) on kthread_create_compat failure, closing the related UAF variant (smb_iod.c:699 sets vc_iod then :708 frees iod without clearing it). MATCHES the finding's ## Recommended fix proposal (gone-hook guard + iod_create NULL-clear). git-apply-able diff at findings/poc/DF-0599/fix.diff (plain unified form so DragonFly patch -p1 also accepts it).
Verdict
REPRODUCED. The bug is real and deterministic on the audited 6.5-DEVELOPMENT #0 kernel. A single SMBIOC_OPENSESSION ioctl with an unregistered ioc_localcs (e.g. "BOGUSCS-9") makes iconv_open return ENOENT inside smb_vc_create (smb_conn.c:486); the do/while error cleanup does smb_vc_put (smb_conn.c:513) -> smb_co_put -> smb_co_gone -> smb_vc_gone (smb_conn.c:550) -> smb_vc_disconnect (smb_conn.c:678) -> smb_iod_request(vcp->vc_iod=NULL) (smb_iod.c:403). vc_iod is still NULL because smb_zmalloc zeroed it (smb_conn.c:437) and smb_iod_create never ran, so SMB_IOD_EVLOCK(NULL)=smb_sl_lock(&NULL->iod_evlock) page-faults. Confirmed by the serial-console panic: 'Fatal trap 12 ... fault virtual address = 0x58 ... Stopped at smb_iod_request+0x58: lock xaddl %edx,0x58(%rbx)' with %rbx=0 (IP 0xffffffff82609ab8 = smbfs.ko base + smb_iod_request@0x9a60 + 0x58). It is a NULL-deref at a fixed small offset with map_at_zero off, so impact is local kernel panic/DoS only (no leak/code-exec/privesc) β the finding's Medium/CVSS-6.2/C:N/I:N/A:H rating is accurate. The finding shipped NO trigger source; I authored panic.c. One iteration was needed: my first PoC left ioc_local=NULL and panicked earlier at dup_sockaddr(vcspec->lap) (smb_conn.c:466) before reaching iconv_open β supplying a valid ioc_local reached the real trigger (this also exposed an incidental related NULL-deref in the same function, noted in VERDICT.md).
No comments yet.