// https://syzkaller.appspot.com/bug?id=df39eb04731fba20cc5e39c10f57de35c7997268 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef AUDIT_SET #define AUDIT_SET 1001 #endif #ifndef AUDIT_STATUS_ENABLED #define AUDIT_STATUS_ENABLED 0x0001 #endif #ifndef O_TMPFILE #define O_TMPFILE (020000000 | 00200000) #endif int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for read_seqbegin null-ptr-deref in exit_task_work...\n"); /* 1. Mount securityfs at /sys/kernel/security */ int res = mkdir("/sys/kernel/security", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/security: %s\n", strerror(errno)); exit(1); } res = mount("securityfs", "/sys/kernel/security", "securityfs", 0, NULL); if (res < 0 && errno != EBUSY) { printf("[-] Failed to mount securityfs: %s\n", strerror(errno)); exit(1); } printf("[+] Mounted securityfs at /sys/kernel/security.\n"); /* 2. Mount smackfs at /sys/fs/smackfs or /smack */ const char *smackfs_dir = "/sys/fs/smackfs"; res = mkdir(smackfs_dir, 0755); if (res < 0 && errno != EEXIST) { smackfs_dir = "/smack"; mkdir(smackfs_dir, 0755); } res = mount("smackfs", smackfs_dir, "smackfs", 0, NULL); if (res < 0 && errno != EBUSY) { if (strcmp(smackfs_dir, "/smack") != 0) { smackfs_dir = "/smack"; mkdir(smackfs_dir, 0755); res = mount("smackfs", smackfs_dir, "smackfs", 0, NULL); } if (res < 0 && errno != EBUSY) { printf("[-] Failed to mount smackfs: %s\n", strerror(errno)); exit(1); } } printf("[+] Mounted smackfs at %s.\n", smackfs_dir); /* 3. Enable kernel audit subsystem via NETLINK_AUDIT */ int audit_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_AUDIT); if (audit_fd < 0) { printf("[-] Failed to create NETLINK_AUDIT socket: %s\n", strerror(errno)); exit(1); } printf("[+] NETLINK_AUDIT socket created successfully.\n"); struct { struct nlmsghdr nlh; struct audit_status s; } req; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)); req.nlh.nlmsg_type = AUDIT_SET; req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_seq = 1; req.s.mask = AUDIT_STATUS_ENABLED; req.s.enabled = 1; struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; res = sendto(audit_fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); if (res < 0) { printf("[-] Failed to send AUDIT_SET: %s\n", strerror(errno)); close(audit_fd); exit(1); } printf("[+] Sent AUDIT_SET command.\n"); char ack_buf[4096]; res = recv(audit_fd, ack_buf, sizeof(ack_buf), 0); if (res < 0) { printf("[-] Failed to recv AUDIT_SET ack: %s\n", strerror(errno)); close(audit_fd); exit(1); } printf("[+] Received AUDIT_SET ack (%d bytes).\n", res); res = close(audit_fd); if (res < 0) { printf("[-] Failed to close audit_fd: %s\n", strerror(errno)); exit(1); } printf("[+] Closed audit_fd successfully.\n"); /* 4. Configure Smack logging to log all accepted and denied access */ char logging_path[128]; snprintf(logging_path, sizeof(logging_path), "%s/logging", smackfs_dir); int smack_fd = open(logging_path, O_WRONLY); if (smack_fd < 0) { snprintf(logging_path, sizeof(logging_path), "/smack/logging"); smack_fd = open(logging_path, O_WRONLY); } if (smack_fd < 0) { printf("[-] Failed to open smack logging node: %s\n", strerror(errno)); exit(1); } printf("[+] Opened %s for writing.\n", logging_path); const char *log_policy_val = "3\n"; res = write(smack_fd, log_policy_val, strlen(log_policy_val)); if (res < 0) { printf("[-] Failed to write to %s: %s\n", logging_path, strerror(errno)); close(smack_fd); exit(1); } printf("[+] Configured Smack log policy to 3 (DENIED | ACCEPT).\n"); res = close(smack_fd); if (res < 0) { printf("[-] Failed to close smack_fd: %s\n", strerror(errno)); exit(1); } printf("[+] Closed smack_fd successfully.\n"); /* 5. Commit IMA appraisal policy */ int ima_fd = open("/sys/kernel/security/ima/policy", O_WRONLY); if (ima_fd < 0) { printf("[-] Failed to open /sys/kernel/security/ima/policy: %s\n", strerror(errno)); exit(1); } printf("[+] Opened /sys/kernel/security/ima/policy for writing.\n"); const char *ima_rule = "appraise func=FILE_CHECK\n"; res = write(ima_fd, ima_rule, strlen(ima_rule)); if (res < 0) { printf("[-] Failed to write IMA rule to /sys/kernel/security/ima/policy: %s\n", strerror(errno)); close(ima_fd); exit(1); } printf("[+] Written rule '%s' to IMA policy.\n", ima_rule); res = close(ima_fd); if (res < 0) { printf("[-] Failed to close IMA policy file: %s\n", strerror(errno)); exit(1); } printf("[+] Closed IMA policy file, policy successfully committed.\n"); /* 6. Spawn child processes to trigger fput in exit_task_work with current->fs == NULL */ for (int iter = 0; iter < 10; iter++) { printf("[*] Starting reproduction iteration %d...\n", iter); pid_t pid = fork(); if (pid < 0) { printf("[-] Failed to fork child: %s\n", strerror(errno)); exit(1); } if (pid == 0) { /* * Child process: * Must open files strictly O_WRONLY so that ima_calc_file_hash() * must invoke dentry_open() to calculate the hash during __fput(). */ unlink("./ima_test_file"); int fd1 = open("./ima_test_file", O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd1 >= 0) { printf("[+] Child: Opened ./ima_test_file (fd=%d)\n", fd1); ssize_t wres = write(fd1, "ima_data_payload\n", 17); if (wres > 0) printf("[+] Child: Wrote %zd bytes to fd %d\n", wres, fd1); } else { printf("[-] Child: Failed to open ./ima_test_file: %s\n", strerror(errno)); } unlink("/tmp/ima_test_file"); int fd2 = open("/tmp/ima_test_file", O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd2 >= 0) { printf("[+] Child: Opened /tmp/ima_test_file (fd=%d)\n", fd2); ssize_t wres = write(fd2, "ima_data_payload\n", 17); if (wres > 0) printf("[+] Child: Wrote %zd bytes to fd %d\n", wres, fd2); } else { printf("[-] Child: Failed to open /tmp/ima_test_file: %s\n", strerror(errno)); } int fd3 = open("/tmp", O_TMPFILE | O_WRONLY, 0666); if (fd3 >= 0) { printf("[+] Child: Opened /tmp O_TMPFILE (fd=%d)\n", fd3); ssize_t wres = write(fd3, "ima_data_payload\n", 17); if (wres > 0) printf("[+] Child: Wrote %zd bytes to fd %d\n", wres, fd3); } /* * Exit immediately without closing file descriptors. * exit_files() schedules deferred ____fput task_work, * exit_fs() sets current->fs = NULL, * exit_task_work() invokes ____fput() -> ima_file_free() -> * ima_update_xattr() -> dentry_open() -> smack_file_open() -> * smack_log() -> common_lsm_audit() -> d_path() -> read_seqbegin(&fs->seq). * Since current->fs is NULL, this crashes at NULL + 0x10. */ printf("[*] Child exiting without closing descriptors to trigger exit_task_work crash...\n"); _exit(0); } int status; pid_t wpid = waitpid(pid, &status, 0); if (wpid < 0) { printf("[-] Failed to waitpid for child %d: %s\n", pid, strerror(errno)); exit(1); } printf("[+] Child %d exited with status 0x%x\n", pid, status); unlink("./ima_test_file"); unlink("/tmp/ima_test_file"); usleep(50000); } /* Also exit main process with open write-only file */ unlink("./ima_test_file"); int fd_final = open("./ima_test_file", O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd_final >= 0) { printf("[+] Main: Opened ./ima_test_file with fd %d\n", fd_final); ssize_t wres = write(fd_final, "ima_data_payload\n", 17); if (wres > 0) printf("[+] Main: Wrote %zd bytes to fd %d\n", wres, fd_final); } printf("[*] Main exiting without closing fd %d to trigger deferred fput in exit_task_work...\n", fd_final); _exit(0); }