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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0687 Β· 10 files
FileTypeDescriptionSize
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
VERDICT.md verdict full narrative: reproduced? mechanism? fix? validation?
↓ download 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 12 at sco_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

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). sco_ctloutput missing break -> mtod(NULL) fault 0x18. Twin of DF-0680/0682.