Missing break in sco_ctloutput PRCO_GETOPT causes NULL-deref panic via getsockopt (DF-0682 pattern)
Summary
sco_ctloutput PRCO_GETOPT (sco_socket.c:109-121): m=m_get(M_WAITOK) :111 m->m_len=sco_getopt(pcb,sopt_name,mtod(m)) :112. sco_getopt returns 0 for unknown opt name (default :356-357) AND SO_SCO_HANDLE on unconnected socket (sp_link NULL :354). Lines 113-117 if(m->m_len==0){m_freem(m);m=NULL;err=ENOPROTOOPT;} NO break. Falls through :120 sopt_from_kbuf(sopt,mtod(m,void*),m->m_len) m==NULL mtod(NULL)->NULL->m_data -> PANIC. Comment :119 "XXX There are possible memory leaks (Griffin)" maintainer suspected. Triggers: getsockopt(s,BTPROTO_SCO,0xFFFF,...) unknown opt OR getsockopt(s,BTPROTO_SCO,SO_SCO_HANDLE,...) unconnected socket. Unpriv local. Fix: add break after ENOPROTOOPT.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0687 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.c | trigger-source | minimal socket+getsockopt trigger (unknown opt OR unconnected SO_SCO_HANDLE) | 2.1 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? | 3.2 KB | β raw |
| run.log | run-log | baseline decisive run with full panic signature | 880 B | view raw |
| panic.txt | panic-signature | Fatal trap 12 at sco_ctloutput.cold.2+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 | 384 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-0687 β SCO sco_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/sco_socket.c:109-121 sco_ctloutput case PRCO_GETOPT:
m = m_get(M_WAITOK, MT_DATA); // :111
m->m_len = sco_getopt(pcb, sopt->sopt_name, mtod(m, uint8_t *)); // :112
if (m->m_len == 0) { // unknown opt OR unconnected
m_freem(m); m = NULL; err = ENOPROTOOPT; // :113-117
} // *** MISSING break ***
/* *opt = m; */
/* XXX There are possible memory leaks (Griffin) */ // :119
sopt_from_kbuf(sopt, mtod(m, void *), m->m_len); // :120 mtod(NULL) deref
Mechanically identical to DF-0680 (RFCOMM) and DF-0682 (L2CAP).
sco_getopt (sys/netbt/sco_upper.c / sco_socket.c:354-357) returns 0 in two
cases: (a) unknown option name (the default: case), and (b)
SO_SCO_HANDLE=2 when the socket is unconnected (sp_link == NULL). In both
cases m_len == 0, the block runs m_freem(m); m=NULL; err=ENOPROTOOPT;, and
falls through to sopt_from_kbuf(sopt, mtod(m, void *), m->m_len) with
m==NULL β page fault at offsetof(struct mbuf, m_data) == 0x18 β panic.
The maintainer flagged this code path years ago β the comment at line 119
/* XXX There are possible memory leaks (Griffin) */ β but the more serious
bug (the NULL deref) was missed.
sco_sattach performs no privilege check, so any unprivileged local user
can create the socket. Precondition: netbt.ko loaded.
Triggers
socket(AF_BLUETOOTH=33, SOCK_SEQPACKET, BTPROTO_SCO=4)
getsockopt(fd, BTPROTO_SCO, 0xFFFF /* unknown */, &buf, &len)
OR
getsockopt(fd, BTPROTO_SCO, SO_SCO_HANDLE=2, &buf, &len) /* unconnected */
β 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 sco_ctloutput.cold.2+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.
Fix
sys/netbt/sco_socket.c: add break; after the ENOPROTOOPT block, and
m_freem(m) the buffer on the success path. The maintainer's
XXX β¦ memory leaks (Griffin) comment is resolved by the success-path free
(we removed the now-stale comment).
See fix.diff (git-apply-able).
Fix validation (Phase 8)
- Baseline (
with-src#0):./triggerβFatal trap 12atsco_ctloutput.cold.2+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). sco_ctloutput missing break -> mtod(NULL) fault 0x18. Twin of DF-0680/0682.
No comments yet.