// https://syzkaller.appspot.com/bug?id=05431ab282eb8c0a336f2107744c0d05db18c1a4 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include static void setup_namespace(void) { uid_t uid = getuid(); gid_t gid = getgid(); if (unshare(CLONE_NEWUSER | CLONE_NEWNS) < 0) { printf("[-] unshare failed: %s\n", strerror(errno)); exit(1); } printf("[+] unshare successful.\n"); char buf[256]; int fd = open("/proc/self/setgroups", O_WRONLY); if (fd >= 0) { if (write(fd, "deny", 4) < 0) { printf("[-] write setgroups failed: %s\n", strerror(errno)); } close(fd); } fd = open("/proc/self/uid_map", O_WRONLY); if (fd >= 0) { snprintf(buf, sizeof(buf), "0 %d 1\n", uid); if (write(fd, buf, strlen(buf)) < 0) { printf("[-] write uid_map failed: %s\n", strerror(errno)); } close(fd); } fd = open("/proc/self/gid_map", O_WRONLY); if (fd >= 0) { snprintf(buf, sizeof(buf), "0 %d 1\n", gid); if (write(fd, buf, strlen(buf)) < 0) { printf("[-] write gid_map failed: %s\n", strerror(errno)); } close(fd); } } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); setup_namespace(); if (mkdir("/tmp/my_tmp", 0777) != 0 && errno != EEXIST) { printf("[-] mkdir /tmp/my_tmp failed: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /tmp/my_tmp successful.\n"); if (mount("tmpfs", "/tmp/my_tmp", "tmpfs", 0, "") != 0) { printf("[-] mount tmpfs failed: %s\n", strerror(errno)); exit(1); } printf("[+] mount tmpfs successful.\n"); if (mkdir("/tmp/my_tmp/lower1", 0777) != 0) { printf("[-] mkdir lower1 failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/tmp/my_tmp/upper1", 0777) != 0) { printf("[-] mkdir upper1 failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/tmp/my_tmp/work1", 0777) != 0) { printf("[-] mkdir work1 failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/tmp/my_tmp/ovl1", 0777) != 0) { printf("[-] mkdir ovl1 failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/tmp/my_tmp/lower2", 0777) != 0) { printf("[-] mkdir lower2 failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/tmp/my_tmp/ovl2", 0777) != 0) { printf("[-] mkdir ovl2 failed: %s\n", strerror(errno)); exit(1); } if (mount("overlay", "/tmp/my_tmp/ovl1", "overlay", 0, "lowerdir=/tmp/my_tmp/lower1,upperdir=/tmp/my_tmp/upper1,workdir=/tmp/my_tmp/work1,userxattr") != 0) { printf("[-] mount ovl1 failed: %s\n", strerror(errno)); exit(1); } printf("[+] mount ovl1 successful.\n"); if (mount("overlay", "/tmp/my_tmp/ovl2", "overlay", 0, "lowerdir=/tmp/my_tmp/ovl1:/tmp/my_tmp/lower2,userxattr") != 0) { printf("[-] mount ovl2 failed: %s\n", strerror(errno)); exit(1); } printf("[+] mount ovl2 successful.\n"); char buf[1024]; // 2. Establish: &type->i_mutex_dir_key -> DCACHE_PAR_LOOKUP // Lookup a non-existent file in tmpfs. readlink("/tmp/my_tmp/lower1/nonexistent1", buf, sizeof(buf)); printf("[+] readlink on tmpfs done.\n"); // 3. Establish: DCACHE_PAR_LOOKUP -> &ovl_i_mutex_dir_key[depth]#2 // open(O_CREAT) on ovl2 triggers a nested lookup into ovl1. int fd2 = open("/tmp/my_tmp/ovl2/nonexistent2", O_CREAT | O_RDWR, 0666); if (fd2 >= 0) close(fd2); printf("[+] open on ovl2 done.\n"); // 4. Establish: &ovl_i_mutex_dir_key[depth]#2 -> &type->i_mutex_dir_key // getdents64 on ovl1 iterates the underlying tmpfs directory. int fd3 = open("/tmp/my_tmp/ovl1", O_RDONLY | O_DIRECTORY); if (fd3 >= 0) { syscall(SYS_getdents64, fd3, buf, sizeof(buf)); close(fd3); } else { printf("[-] open ovl1 failed: %s\n", strerror(errno)); exit(1); } printf("[+] getdents64 on ovl1 done.\n"); return 0; }