// https://syzkaller.appspot.com/bug?id=4dbb2d6bcfb88068305eae941ffc80f3684d62eb #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include static void configure_debugfs_file(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); exit(1); } printf("[+] open %s successful.\n", path); size_t len = strlen(val); ssize_t wres = write(fd, val, len); if (wres < 0 || (size_t)wres != len) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); close(fd); exit(1); } printf("[+] write to %s successful.\n", path); int res = close(fd); if (res < 0) { printf("[-] Failed to close %s: %s\n", path, strerror(errno)); exit(1); } printf("[+] close %s successful.\n", path); } static void pci_rescan(void) { int fd = open("/sys/bus/pci/rescan", O_WRONLY); if (fd < 0) { printf("[-] Failed to open /sys/bus/pci/rescan: %s\n", strerror(errno)); exit(1); } ssize_t wres = write(fd, "1\n", 2); if (wres < 0) { printf("[-] Failed to write to /sys/bus/pci/rescan: %s\n", strerror(errno)); close(fd); exit(1); } int res = close(fd); if (res < 0) { printf("[-] Failed to close /sys/bus/pci/rescan: %s\n", strerror(errno)); exit(1); } } static int ensure_device_ready(const char *dev_name) { char remove_path[512]; char driver_path[512]; char irq_path[512]; snprintf(remove_path, sizeof(remove_path), "/sys/bus/pci/devices/%s/remove", dev_name); snprintf(driver_path, sizeof(driver_path), "/sys/bus/pci/devices/%s/driver", dev_name); snprintf(irq_path, sizeof(irq_path), "/sys/bus/pci/devices/%s/irq", dev_name); for (int attempts = 0; attempts < 50; attempts++) { if (access(remove_path, W_OK) == 0 && access(driver_path, F_OK) == 0) { int irq_fd = open(irq_path, O_RDONLY); if (irq_fd >= 0) { char buf[32] = {0}; ssize_t n = read(irq_fd, buf, sizeof(buf) - 1); int c_res = close(irq_fd); if (c_res < 0) { printf("[-] Failed to close %s: %s\n", irq_path, strerror(errno)); exit(1); } if (n > 0 && atoi(buf) > 0) return 0; } } /* Re-enumerate and re-bind PCI device */ pci_rescan(); usleep(20000); } printf("[-] Failed to ensure device %s is ready after 50 attempts\n", dev_name); return -1; } static void find_target_pci_device(char *out_dev, size_t max_len) { DIR *dir = opendir("/sys/bus/pci/devices"); if (!dir) { printf("[-] Failed to opendir /sys/bus/pci/devices: %s\n", strerror(errno)); exit(1); } printf("[+] opendir /sys/bus/pci/devices successful.\n"); struct dirent *de; char best_dev[256] = {0}; int best_score = -1; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] == '.') continue; char dev_path[512]; char class_path[512]; char driver_path[512]; char remove_path[512]; char irq_path[512]; char block_path[512]; snprintf(dev_path, sizeof(dev_path), "/sys/bus/pci/devices/%s", de->d_name); snprintf(class_path, sizeof(class_path), "%s/class", dev_path); snprintf(driver_path, sizeof(driver_path), "%s/driver", dev_path); snprintf(remove_path, sizeof(remove_path), "%s/remove", dev_path); snprintf(irq_path, sizeof(irq_path), "%s/irq", dev_path); snprintf(block_path, sizeof(block_path), "%s/block", dev_path); /* 1. Never select devices with associated block devices */ if (access(block_path, F_OK) == 0) { continue; } /* 2. Read class code */ int class_fd = open(class_path, O_RDONLY); if (class_fd < 0) { continue; } char class_buf[32] = {0}; ssize_t cr = read(class_fd, class_buf, sizeof(class_buf) - 1); int cres = close(class_fd); if (cres < 0) { printf("[-] Failed to close %s: %s\n", class_path, strerror(errno)); closedir(dir); exit(1); } if (cr <= 0) { continue; } /* Strict Blacklist: * NEVER select storage controllers (class 0x01xxxx: IDE, SATA, SCSI, NVMe) * NEVER select bridge devices (class 0x06xxxx: Host, PCI-PCI, ISA bridge) */ if (strncmp(class_buf, "0x01", 4) == 0) { continue; } if (strncmp(class_buf, "0x06", 4) == 0) { continue; } /* 3. Eligibility Checks: * Driver must be present (device bound) * Remove attribute must be writable */ if (access(driver_path, F_OK) != 0) { continue; } if (access(remove_path, W_OK) != 0) { continue; } /* Check IRQ: read irq attribute, parse integer, require irq > 0 */ int irq_fd = open(irq_path, O_RDONLY); if (irq_fd < 0) { continue; } char irq_buf[32] = {0}; ssize_t ir = read(irq_fd, irq_buf, sizeof(irq_buf) - 1); int ires = close(irq_fd); if (ires < 0) { printf("[-] Failed to close %s: %s\n", irq_path, strerror(errno)); closedir(dir); exit(1); } if (ir <= 0 || atoi(irq_buf) <= 0) { continue; } /* Clean class_buf for printing */ char *nl = strchr(class_buf, '\n'); if (nl) *nl = '\0'; /* 4. Preference Scoring: * Best choice: Network controllers (class 0x02xxxx, e.g., e1000, virtio-net) -> 100 * Fallback: Serial/bus controllers (0x0cxxxx) or multimedia (0x04xxxx) -> 50 * Other -> 10 */ int score = 10; if (strncmp(class_buf, "0x02", 4) == 0) { score = 100; } else if (strncmp(class_buf, "0x0c", 4) == 0 || strncmp(class_buf, "0x04", 4) == 0) { score = 50; } printf("[*] Evaluated candidate PCI device %s: class=%s, irq=%d, score=%d\n", de->d_name, class_buf, atoi(irq_buf), score); if (score > best_score) { best_score = score; strncpy(best_dev, de->d_name, sizeof(best_dev) - 1); best_dev[sizeof(best_dev) - 1] = '\0'; } } int res = closedir(dir); if (res < 0) { printf("[-] Failed to closedir /sys/bus/pci/devices: %s\n", strerror(errno)); exit(1); } printf("[+] closedir /sys/bus/pci/devices successful.\n"); if (best_score < 0 || best_dev[0] == '\0') { printf("[-] Failed to find suitable candidate PCI device\n"); exit(1); } snprintf(out_dev, max_len, "%s", best_dev); printf("[+] Selected target PCI device: %s (score=%d)\n", out_dev, best_score); } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for KASAN slab-use-after-free in acpi_ns_check_package...\n"); /* 1. Mount debugfs if not already mounted */ int res = mount("nodev", "/sys/kernel/debug", "debugfs", 0, NULL); if (res < 0 && errno != EBUSY) { printf("[-] Failed to mount debugfs: %s\n", strerror(errno)); exit(1); } printf("[+] Mount debugfs successful (or already mounted).\n"); /* 2. Configure failslab */ configure_debugfs_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N\n"); configure_debugfs_file("/sys/kernel/debug/failslab/times", "-1\n"); configure_debugfs_file("/sys/kernel/debug/failslab/verbose", "0\n"); /* 3. Identify target PCI device */ char target_dev[256] = {0}; find_target_pci_device(target_dev, sizeof(target_dev)); /* 4. Ensure device is initially ready */ printf("[*] Ensuring target device %s is initially ready...\n", target_dev); if (ensure_device_ready(target_dev) < 0) { printf("[-] Failed initial readiness check for %s\n", target_dev); exit(1); } printf("[+] Target device %s is ready.\n", target_dev); /* 5. Fault injection sweep loop */ printf("[*] Beginning fault-injection sweep on %s...\n", target_dev); for (int round = 1; round <= 3; round++) { printf("[*] Starting sweep round %d/3...\n", round); for (int nth = 1; nth <= 250; nth++) { if (ensure_device_ready(target_dev) < 0) { printf("[-] Failed to ready device %s before nth=%d\n", target_dev, nth); exit(1); } pid_t pid = fork(); if (pid < 0) { printf("[-] Failed to fork: %s\n", strerror(errno)); exit(1); } if (pid == 0) { char remove_path[512]; snprintf(remove_path, sizeof(remove_path), "/sys/bus/pci/devices/%s/remove", target_dev); int rem_fd = open(remove_path, O_WRONLY); if (rem_fd < 0) _exit(1); int fn_fd = open("/proc/self/fail-nth", O_WRONLY); if (fn_fd < 0) { close(rem_fd); _exit(1); } char buf[32]; int len = snprintf(buf, sizeof(buf), "%d\n", nth); if (write(fn_fd, buf, len) != len) { close(fn_fd); close(rem_fd); _exit(1); } /* Arms fail-nth and immediately triggers device removal */ write(rem_fd, "1", 1); _exit(0); } int status = 0; res = waitpid(pid, &status, 0); if (res < 0) { printf("[-] Failed to waitpid: %s\n", strerror(errno)); exit(1); } if (nth % 25 == 0) { printf("[+] Round %d: tested up to nth = %d\n", round, nth); } } printf("[+] Completed sweep round %d/3.\n", round); } printf("[+] Sweep completed without crash.\n"); return 0; }