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

NULL-deref panic in l2cap_ctloutput getsockopt path (l2cap_socket.c:126-131) β€” same defect class as DF-0680

Summary

Root cause in caller l2cap_socket.c:126-131 (surfaced via l2cap_getopt API contract). l2cap_getopt (l2cap_upper.c:521-555) returns 0 for unknown option names (not SO_L2CAP_IMTU=1..SO_L2CAP_LM=6). Caller l2cap_ctloutput PRCO_GETOPT: m=m_get :120 m->m_len=l2cap_getopt(pcb,sopt_name,mtod(m)) :125 if(m->m_len==0){m_freem(m);m=NULL;error=ENOPROTOOPT;} :126-129 NO break falls through to sopt_from_kbuf(sopt,mtod(m,void*),m->m_len) :131 m==NULL mtod(NULL) reads offsetof(mbuf,m_data) ~0x10 unmapped -> kernel page fault -> PANIC. Mechanically identical to DF-0680 (rfcomm_socket.c:121-126). Same pattern ALSO in sco_socket.c:110-121 (sco_ctloutput) third instance. Trigger: any unprivileged local user socket(AF_BLUETOOTH,SOCK_SEQPACKET,BTPROTO_L2CAP) then getsockopt(fd,BTPROTO_L2CAP,99,...) instant panic 100% reliable no Bluetooth hardware needed. l2cap_sattach no priv_check. Bonus: success path also leaks m (never freed after sopt_from_kbuf). Fix: add break after ENOPROTOOPT + m_freem on success path in l2cap_socket.c + same fix for rfcomm_socket.c (DF-0680) and sco_socket.c.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0682 Β· 10 files
FileTypeDescriptionSize
trigger.c trigger-source minimal socket+getsockopt trigger 2.0 KB view raw
build.sh build-script cc -Wall -o trigger trigger.c 82 B view raw
run.sh run-script ./trigger 41 B view raw
VERDICT.md verdict full narrative: reproduced? mechanism? fix? validation? 2.7 KB ↓ raw
run.log run-log baseline decisive run with full panic signature 882 B view raw
panic.txt panic-signature Fatal trap 12 at l2cap_ctloutput.cold.5+0x8 651 B view raw
fix_run.log run-log patched-kernel run: ENOPROTOOPT, no panic 556 B view raw
fix.diff suggested-fix git-apply-able: add break; after ENOPROTOPT + m_freem on success 319 B view raw
fix_build.log build-log single-fix-combined kernel build (shared with DF-0680) 5.6 MB ↓ download
env.txt environment uname, cc version, sysctls, modules 339 B view raw
VERDICT.md verdict full narrative: reproduced? mechanism? fix? validation?
↓ download raw

DF-0682 β€” L2CAP l2cap_ctloutput PRCO_GETOPT missing-break NULL-deref panic

Verdict

REPRODUCED on baseline 6.5-DEVELOPMENT #0 (build Thu Jul 2 06:02:54 UTC 2026). FIXED on single-fix-combined kernel 6.5-DEVELOPMENT #1 (build Sun Jul 19 06:56:50 UTC 2026, sha256 32765f70…).

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

sys/netbt/l2cap_socket.c:118-132 l2cap_ctloutput case PRCO_GETOPT:

m = m_get(M_NOWAIT, MT_DATA);
if (m == NULL) { error = ENOMEM; break; }
m->m_len = l2cap_getopt(pcb, sopt->sopt_name, mtod(m, void *)); // :125
if (m->m_len == 0) {                          // unknown opt -> 0
    m_freem(m); m = NULL; error = ENOPROTOOPT; // :126-129
}                                              // *** MISSING break ***
sopt_from_kbuf(sopt, mtod(m, void *), m->m_len); // :131  mtod(NULL) deref

Mechanically identical to DF-0680 (RFCOMM) and DF-0687 (SCO). l2cap_getopt (sys/netbt/l2cap_upper.c:521-555) returns 0 for any option name other than SO_L2CAP_IMTU=1..SO_L2CAP_LM=6. The missing break after m_freem(m); m=NULL; error=ENOPROTOOPT; lets execution fall through into sopt_from_kbuf(sopt, mtod(NULL, void *), m->m_len) β†’ page fault at offsetof(struct mbuf, m_data) == 0x18 β†’ panic.

l2cap_sattach performs no privilege check, so any unprivileged local user can create the socket. Precondition: netbt.ko loaded.

Trigger

socket(AF_BLUETOOTH=33, SOCK_SEQPACKET, BTPROTO_L2CAP=2)
getsockopt(fd, BTPROTO_L2CAP, 99 /* unknown */, &buf, &len)

β†’ deterministic kernel panic, 100 % reliable, no Bluetooth hardware needed.

Panic signature (baseline)

Fatal trap 12: page fault while in kernel mode
fault virtual address    = 0x18
fault code               = supervisor read data, page not present
Stopped at      l2cap_ctloutput.cold.5+0x8:     movl    0x18,%eax
db>

0x18 = offsetof(struct mbuf, m_data) β€” the NULL+offset deref from mtod(NULL).

Realistic impact ceiling

Deterministic local kernel panic / DoS. NULL-page read fault (no write), so no memory-corruption primitive and no escalation chain. Once netbt.ko is loaded, any local user can panic the machine instantly and repeatedly.

Fix

sys/netbt/l2cap_socket.c: add break; after the ENOPROTOOPT block, and m_freem(m) the buffer on the success path (the success path also leaked m β€” finding's bonus point).

See fix.diff (git-apply-able).

Fix validation (Phase 8)

  • Baseline (with-src #0): ./trigger β‡’ Fatal trap 12 at l2cap_ctloutput.cold.5+0x8: movl 0x18,%eax β‡’ guest DOWN (DDB).
  • Patched (#1): ./trigger β‡’ getsockopt returned -1 (errno=42 Protocol not available) (ENOPROTOOPT from the fixed path), guest UP.

Clean before/after. Fix closes the bug.

Fix verification

fixed

validated

see evidence pack
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Sun Jul 19 06:56:50 UTC 2026

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED (live panic). l2cap_ctloutput missing break -> mtod(NULL) fault 0x18. Twin of DF-0680.