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)
PoC verification
Evidence pack
findings/poc/DF-0682 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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 12atl2cap_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
fixedvalidated
see evidence pack
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.
No comments yet.