// https://syzkaller.appspot.com/bug?id=5ddac33368ddb55edb1e152940542d2bd497ba88 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_WORKERS 4 #define NUM_KILLERS 2 static pthread_t workers[NUM_WORKERS]; static pthread_t killers[NUM_KILLERS]; static pid_t worker_tids[NUM_WORKERS]; static pthread_barrier_t init_barrier; static atomic_int stop_flag = 0; static atomic_int count_success = 0; static atomic_int count_eintr = 0; static atomic_int count_ebusy = 0; static atomic_int count_other = 0; static void sigalrm_handler(int sig) { (void)sig; } static void *killer_fn(void *arg) { (void)arg; pthread_barrier_wait(&init_barrier); pid_t pid = getpid(); while (!atomic_load(&stop_flag)) { for (int i = 0; i < NUM_WORKERS; i++) { pid_t tid = worker_tids[i]; if (tid > 0) { syscall(SYS_tgkill, pid, tid, SIGALRM); } } usleep(25); } return NULL; } static void *worker_fn(void *arg) { long id = (long)arg; worker_tids[id] = syscall(SYS_gettid); pthread_barrier_wait(&init_barrier); sigset_t set; sigemptyset(&set); sigaddset(&set, SIGALRM); int res = pthread_sigmask(SIG_UNBLOCK, &set, NULL); if (res != 0) { printf("[-] Worker %ld failed to unblock SIGALRM: %s\n", id, strerror(res)); } pid_t peer_tid = worker_tids[(id + 1) % NUM_WORKERS]; while (!atomic_load(&stop_flag)) { if (peer_tid > 0) { syscall(SYS_tgkill, getpid(), peer_tid, SIGALRM); } int fd = open("/dev/dvb/adapter0/frontend0", O_RDWR); if (fd >= 0) { atomic_fetch_add(&count_success, 1); close(fd); } else { if (errno == EINTR) { atomic_fetch_add(&count_eintr, 1); } else if (errno == EBUSY) { atomic_fetch_add(&count_ebusy, 1); } else { atomic_fetch_add(&count_other, 1); } } fd = open("/dev/dvb/adapter0/frontend0", O_RDWR | O_NONBLOCK); if (fd >= 0) { atomic_fetch_add(&count_success, 1); close(fd); } else { if (errno == EINTR) { atomic_fetch_add(&count_eintr, 1); } else if (errno == EBUSY) { atomic_fetch_add(&count_ebusy, 1); } else { atomic_fetch_add(&count_other, 1); } } fd = open("/dev/dvb0.frontend0", O_RDWR); if (fd >= 0) { atomic_fetch_add(&count_success, 1); close(fd); } else { if (errno == EINTR) { atomic_fetch_add(&count_eintr, 1); } else if (errno == EBUSY) { atomic_fetch_add(&count_ebusy, 1); } else { atomic_fetch_add(&count_other, 1); } } fd = open("/dev/dvb/adapter0/demux0", O_RDWR | O_NONBLOCK); if (fd >= 0) { close(fd); } fd = open("/dev/dvb/adapter0/dvr0", O_RDONLY | O_NONBLOCK); if (fd >= 0) { close(fd); } } return NULL; } static void setup_dev_node(const char *sys_name, const char *node1, const char *node2, unsigned int def_maj, unsigned int def_min) { unsigned int maj = def_maj; unsigned int min = def_min; char sys_path[256]; snprintf(sys_path, sizeof(sys_path), "/sys/class/dvb/%s/dev", sys_name); FILE *f = fopen(sys_path, "r"); if (f) { if (fscanf(f, "%u:%u", &maj, &min) == 2) { printf("[+] Read sysfs %s: major=%u, minor=%u\n", sys_path, maj, min); } else { printf("[-] Failed to parse %s, falling back to default %u:%u\n", sys_path, maj, min); } fclose(f); } else { printf("[*] Note: fopen %s: %s (using default major=%u, minor=%u)\n", sys_path, strerror(errno), maj, min); } struct stat st; if (stat(node1, &st) == 0) { printf("[+] Node %s already exists (major=%u, minor=%u)\n", node1, major(st.st_rdev), minor(st.st_rdev)); } else { int res = mknod(node1, S_IFCHR | 0666, makedev(maj, min)); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mknod %s: %s\n", node1, strerror(errno)); } else { printf("[+] mknod %s successful (major=%u, minor=%u)\n", node1, maj, min); } } if (node2) { if (stat(node2, &st) == 0) { printf("[+] Node %s already exists (major=%u, minor=%u)\n", node2, major(st.st_rdev), minor(st.st_rdev)); } else { int res = mknod(node2, S_IFCHR | 0666, makedev(maj, min)); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mknod %s: %s\n", node2, strerror(errno)); } else { printf("[+] mknod %s successful (major=%u, minor=%u)\n", node2, maj, min); } } } } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[+] Starting DVB refcount bug reproducer\n"); int res = mkdir("/dev/dvb", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /dev/dvb: %s\n", strerror(errno)); exit(1); } printf("[+] Directory /dev/dvb checked/created\n"); res = mkdir("/dev/dvb/adapter0", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /dev/dvb/adapter0: %s\n", strerror(errno)); exit(1); } printf("[+] Directory /dev/dvb/adapter0 checked/created\n"); DIR *dvb_dir = opendir("/sys/class/dvb"); if (dvb_dir) { printf("[+] opendir /sys/class/dvb successful\n"); struct dirent *de; while ((de = readdir(dvb_dir)) != NULL) { if (de->d_name[0] == '.') continue; printf("[+] Discovered /sys/class/dvb/%s\n", de->d_name); } res = closedir(dvb_dir); if (res < 0) { printf("[-] Failed to closedir /sys/class/dvb: %s\n", strerror(errno)); exit(1); } } else { printf("[*] opendir /sys/class/dvb: %s\n", strerror(errno)); } setup_dev_node("dvb0.frontend0", "/dev/dvb/adapter0/frontend0", "/dev/dvb0.frontend0", 212, 0); setup_dev_node("dvb0.demux0", "/dev/dvb/adapter0/demux0", "/dev/dvb0.demux0", 212, 1); setup_dev_node("dvb0.dvr0", "/dev/dvb/adapter0/dvr0", "/dev/dvb0.dvr0", 212, 2); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigalrm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; res = sigaction(SIGALRM, &sa, NULL); if (res < 0) { printf("[-] Failed to sigaction SIGALRM: %s\n", strerror(errno)); exit(1); } printf("[+] sigaction SIGALRM registered (sa_flags=0, SA_RESTART disabled)\n"); sigset_t set; sigemptyset(&set); sigaddset(&set, SIGALRM); res = pthread_sigmask(SIG_BLOCK, &set, NULL); if (res != 0) { printf("[-] Failed to block SIGALRM in main: %s\n", strerror(res)); exit(1); } printf("[+] SIGALRM blocked in main thread\n"); res = pthread_barrier_init(&init_barrier, NULL, NUM_WORKERS + NUM_KILLERS + 1); if (res != 0) { printf("[-] Failed to init barrier: %s\n", strerror(res)); exit(1); } printf("[+] pthread_barrier_init initialized for %d threads\n", NUM_WORKERS + NUM_KILLERS + 1); for (long i = 0; i < NUM_WORKERS; i++) { res = pthread_create(&workers[i], NULL, worker_fn, (void *)i); if (res != 0) { printf("[-] Failed to create worker %ld: %s\n", i, strerror(res)); exit(1); } } printf("[+] Successfully spawned %d worker threads\n", NUM_WORKERS); for (long i = 0; i < NUM_KILLERS; i++) { res = pthread_create(&killers[i], NULL, killer_fn, (void *)i); if (res != 0) { printf("[-] Failed to create killer %ld: %s\n", i, strerror(res)); exit(1); } } printf("[+] Successfully spawned %d killer threads\n", NUM_KILLERS); pthread_barrier_wait(&init_barrier); printf("[+] All worker and killer threads synchronized past barrier\n"); struct itimerval it = { .it_interval = { .tv_sec = 0, .tv_usec = 100 }, .it_value = { .tv_sec = 0, .tv_usec = 100 }, }; res = setitimer(ITIMER_REAL, &it, NULL); if (res < 0) { printf("[-] Failed to setitimer: %s\n", strerror(errno)); exit(1); } printf("[+] Periodic timer armed (100us interval)\n"); printf("[*] Running multi-threaded open/signal race for 5 seconds...\n"); struct timespec start_ts, cur_ts; clock_gettime(CLOCK_MONOTONIC, &start_ts); int last_report_sec = 0; while (1) { clock_gettime(CLOCK_MONOTONIC, &cur_ts); double elapsed = (cur_ts.tv_sec - start_ts.tv_sec) + (cur_ts.tv_nsec - start_ts.tv_nsec) / 1e9; if (elapsed >= 5.0) break; int cur_sec = (int)elapsed; if (cur_sec > last_report_sec) { last_report_sec = cur_sec; printf("[+] Elapsed: %ds | EINTR: %d | EBUSY: %d | Success: %d | Other: %d\n", cur_sec, atomic_load(&count_eintr), atomic_load(&count_ebusy), atomic_load(&count_success), atomic_load(&count_other)); } struct timespec req = { .tv_sec = 0, .tv_nsec = 50000000 }; nanosleep(&req, NULL); } printf("[+] Race window elapsed. Setting stop flag...\n"); atomic_store(&stop_flag, 1); struct itimerval it_dis = {0}; res = setitimer(ITIMER_REAL, &it_dis, NULL); if (res < 0) { printf("[-] Failed to disarm setitimer: %s\n", strerror(errno)); } else { printf("[+] Periodic timer disarmed\n"); } for (int i = 0; i < NUM_KILLERS; i++) { res = pthread_join(killers[i], NULL); if (res != 0) { printf("[-] Failed to join killer %d: %s\n", i, strerror(res)); } } printf("[+] All killer threads joined\n"); for (int i = 0; i < NUM_WORKERS; i++) { res = pthread_join(workers[i], NULL); if (res != 0) { printf("[-] Failed to join worker %d: %s\n", i, strerror(res)); } } printf("[+] All worker threads joined\n"); printf("[+] Final stats | EINTR: %d | EBUSY: %d | Success: %d | Other: %d\n", atomic_load(&count_eintr), atomic_load(&count_ebusy), atomic_load(&count_success), atomic_load(&count_other)); sleep(1); printf("[+] Reproducer execution complete.\n"); return 0; }