[BUG] usb: gadget: f_tcm: possible circular locking dependency in usbg_make_tpg

From: ZW Tang

Date: Mon Aug 31 2026 - 06:09:50 EST


Hi,

I am reporting a possible circular locking dependency triggered by a
syzkaller reproducer on Linux 7.2.0-rc3. Similar issues appear to have
been reported upstream before, but I can still reproduce this warning on
Linux 7.2.0-rc3 with HEAD commit
1137d8b5df06137fb49513cc923b3b24d94cb809.

The reproducer mounts configfs, creates a USB gadget TCM function, creates
a target usb_gadget fabric TPG, and then links the TCM function into a USB
gadget configuration.

Lockdep reports a cycle involving the configfs inode mutex,
tpg_instances_lock, and the f_tcm opts->dep_lock. The current path is
creating a TPG through configfs_mkdir(). It reaches usbg_make_tpg(), holds
tpg_instances_lock and opts->dep_lock, and then calls
configfs_depend_item_unlocked(), which tries to acquire a configfs inode
mutex.

The existing reverse dependency is created by the USB gadget symlink path:
configfs_symlink() calls config_usb_cfg_link(), which calls
usb_get_function() and then tcm_alloc(), where tpg_instances_lock is
acquired.

This creates the following lock order cycle:

configfs inode mutex -> tpg_instances_lock -> opts->dep_lock
opts->dep_lock -> configfs inode mutex

This looks like a lock-ordering issue in the USB gadget TCM function /
target configfs integration. The kernel should avoid taking these locks in
opposite orders, because this can lead to an actual deadlock.

Reproducer:

syz reproducer: https://pastebin.com/raw/xHmrR2GY
console output: https://pastebin.com/raw/G4WtE3Xs
kernel config: https://pastebin.com/raw/GhP15ssA

Kernel:

HEAD commit: 1137d8b5df06137fb49513cc923b3b24d94cb809
git tree: torvalds/linux
kernel version: 7.2.0-rc3 #1 PREEMPT(full) (QEMU Ubuntu 24.10)

Relevant log:

syz.0.17/12335 is trying to acquire lock:
ffff88801e4a6280 (&sb->s_type->i_mutex_key#26){+.+.}-{4:4},
at: configfs_depend_item_unlocked+0xe8/0x460

but task is already holding lock:
ffff888058c6c918 (&opts->dep_lock){+.+.}-{4:4},
at: usbg_make_tpg+0x153/0x5e0

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #3 (&opts->dep_lock){+.+.}-{4:4}:
__mutex_lock+0x16e/0x2760
usbg_make_tpg+0x153/0x5e0
target_fabric_make_tpg+0xab/0xad0
configfs_mkdir+0x5ae/0xc50
vfs_mkdir+0x35f/0x7b0
filename_mkdirat+0x407/0x580
__x64_sys_mkdirat+0x8a/0xc0

-> #2 (tpg_instances_lock){+.+.}-{4:4}:
__mutex_lock+0x16e/0x2760
tcm_alloc+0x20/0x310
usb_get_function+0x61/0xd0
config_usb_cfg_link+0x1f6/0x3f0
configfs_symlink+0x53c/0x1180
vfs_symlink+0x180/0x4e0
filename_symlinkat+0x324/0x430
__x64_sys_symlinkat+0x9c/0xe0

-> #1 (&gi->lock){+.+.}-{4:4}:
__mutex_lock+0x16e/0x2760
config_usb_cfg_link+0x57/0x3f0
configfs_symlink+0x53c/0x1180
vfs_symlink+0x180/0x4e0
filename_symlinkat+0x324/0x430
__x64_sys_symlinkat+0x9c/0xe0

-> #0 (&sb->s_type->i_mutex_key#26){+.+.}-{4:4}:
down_write+0x91/0x200
configfs_depend_item_unlocked+0xe8/0x460
usbg_make_tpg+0x214/0x5e0
target_fabric_make_tpg+0xab/0xad0
configfs_mkdir+0x5ae/0xc50
vfs_mkdir+0x35f/0x7b0
filename_mkdirat+0x407/0x580
__x64_sys_mkdirat+0x8a/0xc0

Chain exists of:
&sb->s_type->i_mutex_key#26 --> tpg_instances_lock --> &opts->dep_lock

Possible unsafe locking scenario:

CPU0 CPU1
---- ----

lock(&opts->dep_lock);
lock(tpg_instances_lock);
lock(&opts->dep_lock);
lock(&sb->s_type->i_mutex_key#26);

*** DEADLOCK ***