// https://syzkaller.appspot.com/bug?id=7931935cba7d2abad9aa84076d96537aa94e48a4 #include #include #include #include #include #include #include #include #include #include #include #include // 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 // } #define HCI_VENDOR_PKT 0xff #define HCI_COMMAND_PKT 0x01 #define HCI_EVENT_PKT 0x04 #define HCI_EV_CMD_COMPLETE 0x0e #define HCI_EV_CONN_COMPLETE 0x03 #define HCIDEVUP _IOW('H', 201, int) #define RFCOMMCREATEDEV _IOW('R', 200, int) #define RFCOMMRELEASEDEV _IOW('R', 201, int) #define AF_BLUETOOTH 31 #define BTPROTO_L2CAP 0 #define BTPROTO_HCI 1 #define BTPROTO_RFCOMM 3 typedef struct { uint8_t b[6]; } __attribute__((packed)) bdaddr_t; struct sockaddr_l2 { sa_family_t l2_family; uint16_t l2_psm; bdaddr_t l2_bdaddr; uint16_t l2_cid; uint8_t l2_bdaddr_type; }; struct rfcomm_dev_req { int16_t dev_id; uint32_t flags; bdaddr_t src; bdaddr_t dst; uint8_t channel; }; int vhci_fd = -1; int rfcomm_sk = -1; int dev_id = -1; int hci_id = -1; volatile int sync_flag = 0; volatile int reader_exit = 0; void send_cmd_complete(int fd, uint16_t opcode, const void *rp, size_t rp_len) { uint8_t resp[260]; memset(resp, 0, sizeof(resp)); resp[0] = HCI_EVENT_PKT; resp[1] = HCI_EV_CMD_COMPLETE; resp[2] = 3 + rp_len; // 1 byte ncmd + 2 bytes opcode + rp_len resp[3] = 1; // ncmd resp[4] = opcode & 0xff; resp[5] = opcode >> 8; if (rp_len > 0) { memcpy(&resp[6], rp, rp_len); } if (write(fd, resp, 6 + rp_len) < 0) { // ignore } } void *vhci_reader_thread(void *arg) { uint8_t buf[1024]; struct pollfd pfd; while (!reader_exit) { pfd.fd = vhci_fd; pfd.events = POLLIN; int ret = poll(&pfd, 1, 100); if (ret <= 0) continue; int n = read(vhci_fd, buf, sizeof(buf)); if (n < 0) { if (errno == EAGAIN || errno == EINTR) continue; break; } if (n >= 4 && buf[0] == HCI_VENDOR_PKT && buf[1] == 0x00) { hci_id = buf[2] | (buf[3] << 8); } else if (n >= 3 && buf[0] == HCI_COMMAND_PKT) { uint16_t opcode = buf[1] | (buf[2] << 8); if (opcode == 0x0405) { // HCI_OP_CREATE_CONN // Do not reply, we will manually inject HCI_EV_CONN_COMPLETE continue; } if (opcode == 0x1009) { // HCI_OP_READ_BD_ADDR uint8_t rp[7] = {0, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } else if (opcode == 0x1005) { // HCI_OP_READ_BUFFER_SIZE uint8_t rp[8] = {0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } else if (opcode == 0x1001) { // HCI_OP_READ_LOCAL_VERSION uint8_t rp[9] = {0, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } else if (opcode == 0x1003) { // HCI_OP_READ_LOCAL_FEATURES uint8_t rp[9] = {0}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } else if (opcode == 0x100a) { // HCI_OP_READ_LOCAL_EXT_FEATURES uint8_t rp[11] = {0}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } else if (opcode == 0x1002) { // HCI_OP_READ_LOCAL_COMMANDS uint8_t rp[65] = {0}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } else { // Generic fallback for all other commands to satisfy min_len checks uint8_t rp[252] = {0}; send_cmd_complete(vhci_fd, opcode, rp, sizeof(rp)); } } } return NULL; } void *thread_release(void *arg) { struct rfcomm_dev_req req = {0}; req.dev_id = dev_id; WAIT_ON(&sync_flag, 1); // Add a random delay to allow the asynchronous hci_rx_work to start // and acquire device_pm_lock() before we call device_del(). usleep(rand() % 5000); ioctl(rfcomm_sk, RFCOMMRELEASEDEV, &req); return NULL; } void *thread_disconnect(void *arg) { WAIT_ON(&sync_flag, 1); // Inject HCI_EV_DISCONN_COMPLETE to asynchronously trigger device_move() unsigned char disconn_pkt[] = { 0x04, 0x05, 0x04, 0x00, 0x42, 0x00, 0x13 }; if (write(vhci_fd, disconn_pkt, sizeof(disconn_pkt)) < 0) { // ignore } return NULL; } int main() { SETUP_UNBUFFERED_IO(); srand(time(NULL)); TIMER_START(start); int iter = 0; while (TIMER_NOT_EXPIRED(start, 10.0)) { iter++; printf("[+] Starting iteration %d\n", iter); vhci_fd = open("/dev/vhci", O_RDWR); if (vhci_fd < 0) { printf("[-] Failed to open /dev/vhci: %s\n", strerror(errno)); exit(1); } printf("[+] open /dev/vhci successful.\n"); reader_exit = 0; hci_id = -1; pthread_t reader_th; if (pthread_create(&reader_th, NULL, vhci_reader_thread, NULL) != 0) { printf("[-] Failed to create reader thread: %s\n", strerror(errno)); exit(1); } printf("[+] pthread_create reader_th successful.\n"); // Create primary device with opcode 0x00 (no quirks) uint8_t create_pkt[] = {HCI_VENDOR_PKT, 0x00}; if (write(vhci_fd, create_pkt, sizeof(create_pkt)) < 0) { printf("[-] Failed to write create_pkt: %s\n", strerror(errno)); exit(1); } printf("[+] write create_pkt successful.\n"); // Wait for hci_id to be populated by the reader thread for (int i = 0; i < 100; i++) { if (hci_id != -1) break; usleep(1000); } if (hci_id == -1) { printf("[-] Failed to get hci_id\n"); exit(1); } printf("[+] Got hci_id: %d\n", hci_id); int ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (ctl < 0) { printf("[-] Failed to socket BTPROTO_HCI: %s\n", strerror(errno)); exit(1); } if (ioctl(ctl, HCIDEVUP, hci_id) < 0) { if (errno != EALREADY) { printf("[-] Failed to ioctl HCIDEVUP: %s\n", strerror(errno)); exit(1); } } close(ctl); printf("[+] HCIDEVUP successful.\n"); usleep(50000); // Create an L2CAP socket and connect to initiate an ACL connection int l2cap_sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (l2cap_sk < 0) { printf("[-] Failed to socket BTPROTO_L2CAP: %s\n", strerror(errno)); exit(1); } struct sockaddr_l2 addr = {0}; addr.l2_family = AF_BLUETOOTH; addr.l2_psm = 0x1001; 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; int flags = fcntl(l2cap_sk, F_GETFL, 0); if (flags < 0) { printf("[-] Failed to fcntl F_GETFL: %s\n", strerror(errno)); exit(1); } if (fcntl(l2cap_sk, F_SETFL, flags | O_NONBLOCK) < 0) { printf("[-] Failed to fcntl F_SETFL: %s\n", strerror(errno)); exit(1); } printf("[+] fcntl O_NONBLOCK successful.\n"); int res = connect(l2cap_sk, (struct sockaddr *)&addr, sizeof(addr)); if (res < 0 && errno != EINPROGRESS && errno != EAGAIN) { printf("[-] Failed to connect L2CAP: %s\n", strerror(errno)); exit(1); } printf("[+] connect L2CAP initiated.\n"); usleep(50000); // Inject HCI_EV_CONN_COMPLETE to complete the connection and register the hci_conn sysfs device uint8_t conn_pkt[] = { HCI_EVENT_PKT, HCI_EV_CONN_COMPLETE, 0x0b, // length 0x00, // status 0x42, 0x00, // handle 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, // bdaddr 0x01, // link_type (ACL) 0x00 // encr_mode }; if (write(vhci_fd, conn_pkt, sizeof(conn_pkt)) < 0) { printf("[-] Failed to write conn_pkt: %s\n", strerror(errno)); exit(1); } printf("[+] write conn_pkt successful.\n"); // Give it a moment to process the connection and sysfs registration usleep(50000); rfcomm_sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (rfcomm_sk < 0) { printf("[-] Failed to socket BTPROTO_RFCOMM: %s\n", strerror(errno)); exit(1); } printf("[+] socket BTPROTO_RFCOMM successful.\n"); static uint8_t channel = 1; struct rfcomm_dev_req req = {0}; req.dev_id = -1; req.flags = 0; req.dst.b[0] = 0x11; req.dst.b[1] = 0x22; req.dst.b[2] = 0x33; req.dst.b[3] = 0x44; req.dst.b[4] = 0x55; req.dst.b[5] = 0x66; req.channel = channel++; if (channel > 30) channel = 1; // Create the RFCOMM TTY device and reparent it to the hci_conn dev_id = ioctl(rfcomm_sk, RFCOMMCREATEDEV, &req); if (dev_id < 0) { printf("[-] Failed to ioctl RFCOMMCREATEDEV: %s\n", strerror(errno)); exit(1); } printf("[+] RFCOMMCREATEDEV successful, dev_id=%d.\n", dev_id); sync_flag = 0; pthread_t t1, t2; if (pthread_create(&t1, NULL, thread_release, NULL) != 0) { printf("[-] Failed to create thread_release: %s\n", strerror(errno)); exit(1); } printf("[+] pthread_create thread_release successful.\n"); if (pthread_create(&t2, NULL, thread_disconnect, NULL) != 0) { printf("[-] Failed to create thread_disconnect: %s\n", strerror(errno)); exit(1); } printf("[+] pthread_create thread_disconnect successful.\n"); usleep(10000); // Wait for threads to be ready SIGNAL(&sync_flag, 1); pthread_join(t1, NULL); pthread_join(t2, NULL); close(rfcomm_sk); close(l2cap_sk); reader_exit = 1; pthread_join(reader_th, NULL); close(vhci_fd); printf("[+] Iteration %d complete.\n", iter); } printf("[+] Finished successfully. Waiting for asynchronous warnings...\n"); sleep(1); return 0; }