// https://syzkaller.appspot.com/bug?id=7931935cba7d2abad9aa84076d96537aa94e48a4 // Copyright 2026 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // IMPORTANT: Do not copy the macros or definitions below directly into your reproducer. // Instead, add the following line to your reproducer: // #include "race_toolkit.h" // --- Race Condition Toolkit --- // Macros and snippets for CPU pinning, memory barriers, and userfaultfd. #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Unbuffered I/O: Ensure logs are written immediately. #define SETUP_UNBUFFERED_IO() setvbuf(stdout, NULL, _IONBF, 0) // CPU Pinning: Pin the current thread to a specific CPU core. #define PIN_TO_CPU(cpu) \ do { \ cpu_set_t mask; \ CPU_ZERO(&mask); \ CPU_SET(cpu, &mask); \ if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { \ perror("sched_setaffinity"); \ } \ } while (0) // Memory Barrier: Ensure memory ordering. #define MB() __atomic_thread_fence(__ATOMIC_SEQ_CST) // Spin-wait Barrier: Wait until a memory location has a specific value. // Best for tight race windows (low latency, no context switches). #define WAIT_ON(addr, val) \ do { \ while (__atomic_load_n(addr, __ATOMIC_ACQUIRE) != (val)) \ ; \ } while (0) // Signal: Set a memory location to a specific value to release a WAIT_ON. #define SIGNAL(addr, val) __atomic_store_n(addr, val, __ATOMIC_RELEASE) // --- Timing Primitives --- // Robust timing loops in VM environments (using CLOCK_MONOTONIC to avoid time(NULL) jumps). static inline double timer_elapsed_sec(struct timespec* start) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { perror("clock_gettime(CLOCK_MONOTONIC) elapsed"); exit(1); } return (double)(now.tv_sec - start->tv_sec) + (double)(now.tv_nsec - start->tv_nsec) / 1e9; } // Initialize a monotonic timer variable. #define TIMER_START(t) \ struct timespec t; \ if (clock_gettime(CLOCK_MONOTONIC, &t) == -1) { \ perror("clock_gettime(CLOCK_MONOTONIC) start"); \ exit(1); \ } // Check if the elapsed time since 't' is less than 'sec' seconds. #define TIMER_NOT_EXPIRED(t, sec) (timer_elapsed_sec(&(t)) < (double)(sec)) // Futex-based Event: Shared with syzkaller executor. // Best for general synchronization or longer waits to save CPU. typedef struct { int state; } event_t; static void event_init(event_t* ev) { ev->state = 0; } static void event_reset(event_t* ev) { ev->state = 0; } static void event_set(event_t* ev) { if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) { fprintf(stderr, "event already set\n"); exit(1); } __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); } static void event_wait(event_t* ev) { while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); } // userfaultfd setup: Register a memory range for page fault handling. static int setup_uffd(void* addr, size_t len) { int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) return -1; struct uffdio_api api = {.api = UFFD_API, .features = 0}; if (ioctl(uffd, UFFDIO_API, &api) == -1) { close(uffd); return -1; } struct uffdio_register reg = { .range = {.start = (uintptr_t)addr, .len = len}, .mode = UFFDIO_REGISTER_MODE_MISSING}; if (ioctl(uffd, UFFDIO_REGISTER, ®) == -1) { close(uffd); return -1; } return uffd; } // --- Guidance on Usage --- // 1. Use WAIT_ON/SIGNAL for tight race conditions to avoid scheduling overhead. // 2. Use event_t (futexes) for general coordination or when waiting for longer periods. // 3. Always use PIN_TO_CPU to increase race probability on multi-core systems. // 4. Use setup_uffd to register a memory range for page fault handling. This allows you to // pause a thread accessing that memory until you handle the fault, creating a reliable // and controllable race window. // 5. Call SETUP_UNBUFFERED_IO() at the start of main() to ensure that logs are printed // immediately. This is essential for understanding the exact interleaving of events // when debugging race conditions. // 6. For timing-based loops (e.g., running a race for 10 seconds), do NOT use time(NULL) // or loops relying on real-time clocks, as VM clocks are highly unreliable and can fail or drift. // Instead, use the robust monotonic timing primitives TIMER_START and TIMER_NOT_EXPIRED: // TIMER_START(start); // while (TIMER_NOT_EXPIRED(start, 10.0)) { // // Your race logic here // } #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 #endif #ifndef BTPROTO_L2CAP #define BTPROTO_L2CAP 0 #endif #ifndef BTPROTO_HCI #define BTPROTO_HCI 1 #endif #ifndef BTPROTO_BNEP #define BTPROTO_BNEP 4 #endif #ifndef BDADDR_BREDR #define BDADDR_BREDR 0x00 #endif #ifndef HCIDEVUP #define HCIDEVUP _IOW('H', 201, int) #endif #ifndef HCIDEVDOWN #define HCIDEVDOWN _IOW('H', 202, int) #endif typedef struct { uint8_t b[6]; } __attribute__((packed)) bdaddr_t; struct sockaddr_l2 { sa_family_t l2_family; unsigned short l2_psm; bdaddr_t l2_bdaddr; unsigned short l2_cid; uint8_t l2_bdaddr_type; }; #define BNEPCONNADD _IOW('B', 200, int) struct bnep_connadd_req { int sock; uint32_t flags; uint16_t role; char device[16]; }; int vhci_fd; int hci_id; volatile int vhci_quit = 0; uint16_t kernel_scid = 0; void *vhci_thread(void *arg) { unsigned char buf[1024]; struct pollfd pfd = { .fd = vhci_fd, .events = POLLIN }; while (!vhci_quit) { if (poll(&pfd, 1, 10) <= 0) continue; int n = read(vhci_fd, buf, sizeof(buf)); if (n < 0) break; if (n >= 3 && buf[0] == 1) { // HCI_COMMAND_PKT uint16_t opcode = buf[1] | (buf[2] << 8); unsigned char resp[260] = {0}; resp[0] = 4; // HCI_EVENT_PKT resp[1] = 0x0e; // HCI_EV_CMD_COMPLETE resp[3] = 1; // num hci command packets resp[4] = buf[1]; // opcode low resp[5] = buf[2]; // opcode high resp[6] = 0; // status int len = 0; switch (opcode) { case 0x1009: // READ_BD_ADDR resp[7] = 0xaa; resp[8] = 0xbb; resp[9] = 0xcc; resp[10] = 0xdd; resp[11] = 0xee; resp[12] = 0xff; len = 7; break; case 0x1001: // READ_LOCAL_VERSION resp[7] = 6; // HCI_VERSION_4_0 len = 9; break; case 0x1003: // READ_LOCAL_FEATURES len = 9; break; case 0x1004: // READ_LOCAL_EXT_FEATURES resp[7] = 0; // page resp[8] = 0; // max_page memset(&resp[9], 0, 8); // features len = 11; break; case 0x1002: // READ_LOCAL_COMMANDS memset(&resp[7], 0, 64); len = 65; break; case 0x2002: // LE_READ_BUFFER_SIZE resp[7] = 255; resp[8] = 0; // le_mtu resp[9] = 10; // le_pkts len = 4; break; case 0x2003: // LE_READ_LOCAL_FEATURES len = 9; break; case 0x0c14: // READ_LOCAL_NAME len = 249; break; case 0x1005: // READ_BUFFER_SIZE resp[7] = 0xff; resp[8] = 0x00; // acl_mtu resp[9] = 0xff; // sco_mtu resp[10] = 0x0a; resp[11] = 0x00; // acl_max_pkt resp[12] = 0x0a; resp[13] = 0x00; // sco_max_pkt len = 8; break; case 0x0c23: // READ_CLASS_OF_DEV len = 4; break; case 0x0c25: // READ_VOICE_SETTING len = 3; break; case 0x0c1b: // READ_PAGE_SCAN_ACTIVITY len = 5; break; case 0x0c46: // READ_PAGE_SCAN_TYPE len = 2; break; case 0x0c38: // READ_NUM_SUPPORTED_IAC resp[7] = 1; // num_iac len = 2; break; case 0x0c39: // READ_CURRENT_IAC_LAP resp[7] = 1; // num_iac resp[8] = 0x9e; resp[9] = 0x8b; resp[10] = 0x33; // lap len = 5; break; case 0x0c0d: // READ_STORED_LINK_KEY resp[7] = 0; resp[8] = 0; // max_keys resp[9] = 0; resp[10] = 0; // num_keys len = 5; break; case 0x0c5a: // READ_DEF_ERR_DATA_REPORTING resp[7] = 0; // err_data_reporting len = 2; break; case 0x2007: // LE_READ_ADV_TX_POWER resp[7] = 0; // tx_power len = 2; break; case 0x204b: // LE_READ_TRANSMIT_POWER resp[7] = 0; resp[8] = 0; len = 3; break; case 0x200f: // LE_READ_ACCEPT_LIST_SIZE resp[7] = 0; // size len = 2; break; case 0x202a: // LE_READ_RESOLV_LIST_SIZE resp[7] = 0; // size len = 2; break; case 0x202f: // LE_READ_MAX_DATA_LEN memset(&resp[7], 0, 8); len = 9; break; case 0x2023: // LE_READ_DEF_DATA_LEN memset(&resp[7], 0, 4); len = 5; break; case 0x203b: // LE_READ_NUM_SUPPORTED_ADV_SETS resp[7] = 1; // num_adv_sets len = 2; break; case 0x0c12: // DELETE_STORED_LINK_KEY resp[7] = 0; resp[8] = 0; // num_keys_deleted len = 3; break; case 0x100c: // READ_LOCAL_PAIRING_OPTS resp[7] = 0; resp[8] = 0; len = 3; break; case 0x100b: // READ_LOCAL_CODECS case 0x100d: // READ_LOCAL_CODECS_V2 resp[7] = 0; // num_codecs len = 2; break; case 0x0405: { // CREATE_CONN unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char cc[14] = { 4, 0x03, 11, 0, 42, 0, // handle 42 buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], // bdaddr 1, 0 // ACL, no enc }; if (write(vhci_fd, cc, 14) < 0) break; continue; } case 0x0406: { // DISCONNECT unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char dc[7] = { 4, 0x05, 4, 0, // status buf[4], buf[5], // handle 0x16 // reason }; if (write(vhci_fd, dc, 7) < 0) break; continue; } case 0x0411: { // AUTH_REQUESTED unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char ac[6] = { 4, 0x06, 3, 0, // status buf[4], buf[5] // handle }; if (write(vhci_fd, ac, 6) < 0) break; continue; } case 0x0413: { // SET_CONN_ENCRYPT unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char ec[7] = { 4, 0x08, 4, 0, // status buf[4], buf[5], // handle buf[6] // encrypt enable }; if (write(vhci_fd, ec, 7) < 0) break; continue; } case 0x041b: { // READ_REMOTE_FEATURES unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char rf[14] = { 4, 0x0b, 11, 0, // status = 0 (success) buf[4], buf[5], // handle 0, 0, 0, 0, 0, 0, 0, 0 // features }; if (write(vhci_fd, rf, 14) < 0) break; continue; } case 0x041d: { // READ_REMOTE_VERSION unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char rv[11] = { 4, 0x0c, 8, 0, // status buf[4], buf[5], // handle 6, // LMP version 0, 0, // Manufacturer 0, 0 // LMP subversion }; if (write(vhci_fd, rv, 11) < 0) break; continue; } case 0x0428: { // READ_REMOTE_EXT_FEATURES unsigned char cs[7] = { 4, 0x0f, 4, 0, 1, buf[1], buf[2] }; if (write(vhci_fd, cs, 7) < 0) break; unsigned char rf[16] = { 4, 0x23, 13, 0, // status buf[4], buf[5], // handle 1, // page 1, // max page 0, 0, 0, 0, 0, 0, 0, 0 // features }; if (write(vhci_fd, rf, 16) < 0) break; continue; } default: len = 1; // generic fallback to satisfy min_len checks break; } resp[2] = len + 3; if (write(vhci_fd, resp, resp[2] + 3) < 0) break; } else if (n >= 9 && buf[0] == 2) { // HCI_ACLDATA_PKT uint16_t cid = buf[7] | (buf[8] << 8); if (cid == 1 && n >= 13) { // Signaling channel uint8_t code = buf[9]; uint8_t ident = buf[10]; uint8_t h_flags = (buf[2] & 0x0F) | 0x20; if (code == 0x0a) { // Info Req unsigned char rsp[17] = { 2, buf[1], h_flags, 12, 0, 8, 0, 1, 0, 0x0b, ident, 4, 0, buf[13], buf[14], 1, 0 // NOTSUPP }; if (write(vhci_fd, rsp, 17) < 0) break; } else if (code == 0x02) { // Conn Req kernel_scid = buf[15] | (buf[16] << 8); unsigned char rsp[21] = { 2, buf[1], h_flags, 16, 0, 12, 0, 1, 0, 0x03, ident, 8, 0, 0x40, 0x00, // dcid kernel_scid & 0xff, kernel_scid >> 8, // scid 0, 0, 0, 0 // success }; if (write(vhci_fd, rsp, 21) < 0) break; // Send our own Config Req unsigned char creq[17] = { 2, buf[1], h_flags, 12, 0, 8, 0, 1, 0, 0x04, 42, 4, 0, kernel_scid & 0xff, kernel_scid >> 8, // dcid 0, 0 }; if (write(vhci_fd, creq, 17) < 0) break; } else if (code == 0x04) { // Config Req unsigned char rsp[19] = { 2, buf[1], h_flags, 14, 0, 10, 0, 1, 0, 0x05, ident, 6, 0, kernel_scid & 0xff, kernel_scid >> 8, // scid 0, 0, 0, 0 // success }; if (write(vhci_fd, rsp, 19) < 0) break; } else if (code == 0x06) { // Disconn Req unsigned char rsp[17] = { 2, buf[1], h_flags, 12, 0, 8, 0, 1, 0, 0x07, ident, 4, 0, buf[13], buf[14], buf[15], buf[16] }; if (write(vhci_fd, rsp, 17) < 0) break; } } } } return NULL; } int setup_vhci(pthread_t *th) { vhci_fd = open("/dev/vhci", O_RDWR); if (vhci_fd < 0) { printf("[-] open /dev/vhci failed: %s\n", strerror(errno)); return -1; } printf("[+] open /dev/vhci successful.\n"); unsigned char req[2] = { 0xff, 0x00 }; if (write(vhci_fd, req, 2) != 2) { printf("[-] write to /dev/vhci failed: %s\n", strerror(errno)); close(vhci_fd); return -1; } printf("[+] write to /dev/vhci successful.\n"); unsigned char resp[4]; if (read(vhci_fd, resp, 4) != 4) { printf("[-] read from /dev/vhci failed: %s\n", strerror(errno)); close(vhci_fd); return -1; } printf("[+] read from /dev/vhci successful.\n"); hci_id = resp[2] | (resp[3] << 8); vhci_quit = 0; if (pthread_create(th, NULL, vhci_thread, NULL) != 0) { printf("[-] pthread_create failed: %s\n", strerror(errno)); close(vhci_fd); return -1; } printf("[+] pthread_create successful.\n"); int sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (sock < 0) { printf("[-] socket BTPROTO_HCI failed: %s\n", strerror(errno)); vhci_quit = 1; pthread_join(*th, NULL); close(vhci_fd); return -1; } printf("[+] socket BTPROTO_HCI successful.\n"); if (ioctl(sock, HCIDEVUP, hci_id) < 0 && errno != EALREADY) { printf("[-] ioctl HCIDEVUP failed: %s\n", strerror(errno)); close(sock); vhci_quit = 1; pthread_join(*th, NULL); close(vhci_fd); return -1; } printf("[+] ioctl HCIDEVUP successful.\n"); if (close(sock) < 0) { printf("[-] close sock failed: %s\n", strerror(errno)); vhci_quit = 1; pthread_join(*th, NULL); close(vhci_fd); return -1; } printf("[+] close sock successful.\n"); return 0; } struct race_args { event_t *ev; int hci_id; }; void *race_thread(void *arg) { struct race_args *args = arg; event_wait(args->ev); // Small random delay to hit the race window usleep(rand() % 1000); int sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (sock < 0) { printf("[-] socket BTPROTO_HCI in race_thread failed: %s\n", strerror(errno)); return NULL; } printf("[+] socket BTPROTO_HCI in race_thread successful.\n"); if (ioctl(sock, HCIDEVDOWN, args->hci_id) < 0) { printf("[-] ioctl HCIDEVDOWN failed (expected in race): %s\n", strerror(errno)); } else { printf("[+] ioctl HCIDEVDOWN successful.\n"); } if (close(sock) < 0) { printf("[-] close sock in race_thread failed: %s\n", strerror(errno)); } else { printf("[+] close sock in race_thread successful.\n"); } return NULL; } int main() { SETUP_UNBUFFERED_IO(); srand(time(NULL)); for (int i = 0; i < 1000; i++) { printf("[+] Iteration %d\n", i); pthread_t vhci_th; if (setup_vhci(&vhci_th) < 0) { printf("[-] setup_vhci failed\n"); continue; } int l2cap_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (l2cap_sock < 0) { printf("[-] socket BTPROTO_L2CAP failed: %s\n", strerror(errno)); goto cleanup_vhci; } printf("[+] socket BTPROTO_L2CAP successful.\n"); int flags = fcntl(l2cap_sock, F_GETFL, 0); if (flags < 0) { printf("[-] fcntl F_GETFL failed: %s\n", strerror(errno)); close(l2cap_sock); goto cleanup_vhci; } if (fcntl(l2cap_sock, F_SETFL, flags | O_NONBLOCK) < 0) { printf("[-] fcntl F_SETFL failed: %s\n", strerror(errno)); close(l2cap_sock); goto cleanup_vhci; } printf("[+] fcntl O_NONBLOCK successful.\n"); struct sockaddr_l2 addr = {0}; addr.l2_family = AF_BLUETOOTH; addr.l2_psm = htole16(0x000f); // BNEP PSM (0x0f) addr.l2_bdaddr.b[0] = 0x11; addr.l2_bdaddr.b[1] = 0x22; addr.l2_bdaddr.b[2] = 0x33; addr.l2_bdaddr.b[3] = 0x44; addr.l2_bdaddr.b[4] = 0x55; addr.l2_bdaddr.b[5] = 0x66; addr.l2_bdaddr_type = BDADDR_BREDR; if (connect(l2cap_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0 && errno != EINPROGRESS) { printf("[-] connect failed: %s\n", strerror(errno)); close(l2cap_sock); goto cleanup_vhci; } printf("[+] connect initiated.\n"); struct pollfd pfd = { .fd = l2cap_sock, .events = POLLOUT }; if (poll(&pfd, 1, 3000) <= 0) { printf("[-] poll timeout on connect\n"); close(l2cap_sock); goto cleanup_vhci; } printf("[+] poll successful.\n"); int err = 0; socklen_t len = sizeof(err); if (getsockopt(l2cap_sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { printf("[-] getsockopt failed: %s\n", strerror(errno)); close(l2cap_sock); goto cleanup_vhci; } if (err != 0) { printf("[-] connect failed asynchronously: %s\n", strerror(err)); close(l2cap_sock); goto cleanup_vhci; } printf("[+] connect successful.\n"); if (fcntl(l2cap_sock, F_SETFL, flags) < 0) { printf("[-] fcntl restore failed: %s\n", strerror(errno)); close(l2cap_sock); goto cleanup_vhci; } printf("[+] fcntl restore successful.\n"); int bnep_sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP); if (bnep_sock < 0) { printf("[-] socket BTPROTO_BNEP failed: %s\n", strerror(errno)); close(l2cap_sock); goto cleanup_vhci; } printf("[+] socket BTPROTO_BNEP successful.\n"); struct bnep_connadd_req req = {0}; req.sock = l2cap_sock; req.flags = 0; req.role = 0; strcpy(req.device, "bnep%d"); event_t ev; event_init(&ev); struct race_args args = { &ev, hci_id }; pthread_t th; if (pthread_create(&th, NULL, race_thread, &args) != 0) { printf("[-] pthread_create race_thread failed: %s\n", strerror(errno)); close(bnep_sock); close(l2cap_sock); goto cleanup_vhci; } printf("[+] pthread_create race_thread successful.\n"); if (ioctl(bnep_sock, BNEPCONNADD, &req) < 0) { printf("[-] ioctl BNEPCONNADD failed: %s\n", strerror(errno)); } else { printf("[+] ioctl BNEPCONNADD successful.\n"); } event_set(&ev); // Wait a bit to let the race happen usleep(1000); if (close(bnep_sock) < 0) { printf("[-] close bnep_sock failed: %s\n", strerror(errno)); } else { printf("[+] close bnep_sock successful.\n"); } if (close(l2cap_sock) < 0) { printf("[-] close l2cap_sock failed: %s\n", strerror(errno)); } else { printf("[+] close l2cap_sock successful.\n"); } pthread_join(th, NULL); printf("[+] pthread_join race_thread successful.\n"); cleanup_vhci: vhci_quit = 1; pthread_join(vhci_th, NULL); printf("[+] pthread_join vhci_th successful.\n"); if (close(vhci_fd) < 0) { printf("[-] close vhci_fd failed: %s\n", strerror(errno)); } else { printf("[+] close vhci_fd successful.\n"); } usleep(10000); } printf("[+] Done\n"); sleep(2); return 0; }