// https://syzkaller.appspot.com/bug?id=7927d89372995362b964b8fb6ab33dfb6299ed10 #define _GNU_SOURCE #include #include #include #include #include #include #include #include static void cleanup_path(const char *path, int is_dir) { int res; if (is_dir) { res = rmdir(path); } else { res = unlink(path); } if (res < 0 && errno != ENOENT) { printf("[-] Cleanup failed for %s: %s\n", path, strerror(errno)); } } static void cleanup_previous(void) { cleanup_path("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/iad_desc", 0); cleanup_path("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0", 1); cleanup_path("/sys/kernel/config/usb_gadget/g1/strings/0x409/iad_desc", 1); cleanup_path("/sys/kernel/config/usb_gadget/g1/strings/0x409", 1); cleanup_path("/sys/kernel/config/target/usb_gadget/naa.5001405000000000/tpgt_1", 1); cleanup_path("/sys/kernel/config/target/usb_gadget/naa.5001405000000000", 1); cleanup_path("/sys/kernel/config/target/usb_gadget", 1); cleanup_path("/sys/kernel/config/usb_gadget/g1/functions/tcm.usb0", 1); cleanup_path("/sys/kernel/config/usb_gadget/g1", 1); } int main(void) { int res; setvbuf(stdout, NULL, _IONBF, 0); printf("[+] Starting reproducer for circular locking dependency in uvc_func_allow_link...\n"); res = access("/sys/kernel/config", F_OK); if (res < 0) { res = mkdir("/sys/kernel/config", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /sys/kernel/config: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config successful.\n"); } res = mount("configfs", "/sys/kernel/config", "configfs", 0, NULL); if (res < 0) { if (errno == EBUSY) { printf("[+] configfs is already mounted at /sys/kernel/config.\n"); } else { printf("[-] Failed to mount configfs: %s\n", strerror(errno)); exit(1); } } else { printf("[+] mount configfs successful.\n"); } cleanup_previous(); /* * Step 1: Create USB gadget directory. */ res = mkdir("/sys/kernel/config/usb_gadget/g1", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/usb_gadget/g1: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/usb_gadget/g1 successful.\n"); /* * Step 2: Create TCM function instance. * This establishes lock dependency: gadget_subsys.su_mutex -> opts->dep_lock */ res = mkdir("/sys/kernel/config/usb_gadget/g1/functions/tcm.usb0", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/usb_gadget/g1/functions/tcm.usb0: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/usb_gadget/g1/functions/tcm.usb0 successful.\n"); /* * Step 3: Create Target Core usb_gadget fabric directory. */ res = mkdir("/sys/kernel/config/target/usb_gadget", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/target/usb_gadget: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/target/usb_gadget successful.\n"); /* * Step 4: Create WWN directory under usb_gadget target fabric. */ res = mkdir("/sys/kernel/config/target/usb_gadget/naa.5001405000000000", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/target/usb_gadget/naa.5001405000000000: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/target/usb_gadget/naa.5001405000000000 successful.\n"); /* * Step 5: Create TPG directory. * This calls usbg_make_tpg -> configfs_depend_item_unlocked, * establishing lock dependency: opts->dep_lock -> sb->s_type->i_mutex_key (configfs inode_lock) */ res = mkdir("/sys/kernel/config/target/usb_gadget/naa.5001405000000000/tpgt_1", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/target/usb_gadget/naa.5001405000000000/tpgt_1: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/target/usb_gadget/naa.5001405000000000/tpgt_1 successful.\n"); /* * Step 6: Create UVC function instance. */ res = mkdir("/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/usb_gadget/g1/functions/uvc.usb0: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/usb_gadget/g1/functions/uvc.usb0 successful.\n"); /* * Step 7: Create gadget language strings directory. */ res = mkdir("/sys/kernel/config/usb_gadget/g1/strings/0x409", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/usb_gadget/g1/strings/0x409: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/usb_gadget/g1/strings/0x409 successful.\n"); /* * Step 8: Create iad_desc string item. */ res = mkdir("/sys/kernel/config/usb_gadget/g1/strings/0x409/iad_desc", 0755); if (res < 0) { printf("[-] Failed to mkdir /sys/kernel/config/usb_gadget/g1/strings/0x409/iad_desc: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /sys/kernel/config/usb_gadget/g1/strings/0x409/iad_desc successful.\n"); /* * Step 9: Symlink iad_desc into uvc.usb0. * configfs_symlink acquires inode_lock on uvc.usb0 (sb->s_type->i_mutex_key) * and invokes uvc_func_allow_link, which tries to acquire gadget_subsys.su_mutex. * Lockdep detects the circular dependency: * gadget_subsys.su_mutex -> opts->dep_lock -> sb->s_type->i_mutex_key -> gadget_subsys.su_mutex */ res = symlink("/sys/kernel/config/usb_gadget/g1/strings/0x409/iad_desc", "/sys/kernel/config/usb_gadget/g1/functions/uvc.usb0/iad_desc"); if (res < 0) { printf("[-] Failed to symlink iad_desc: %s\n", strerror(errno)); exit(1); } printf("[+] symlink iad_desc successful (circular locking dependency detected by lockdep).\n"); sleep(1); printf("[+] Reproducer completed successfully.\n"); return 0; }