Skip to content

Commit 63f818f

Browse files
committed
proc: Use a dedicated lock in struct pid
syzbot wrote: > ======================================================== > WARNING: possible irq lock inversion dependency detected > 5.6.0-syzkaller #0 Not tainted > -------------------------------------------------------- > swapper/1/0 just changed the state of lock: > ffffffff898090d8 (tasklist_lock){.+.?}-{2:2}, at: send_sigurg+0x9f/0x320 fs/fcntl.c:840 > but this lock took another, SOFTIRQ-unsafe lock in the past: > (&pid->wait_pidfd){+.+.}-{2:2} > > > and interrupts could create inverse lock ordering between them. > > > other info that might help us debug this: > Possible interrupt unsafe locking scenario: > > CPU0 CPU1 > ---- ---- > lock(&pid->wait_pidfd); > local_irq_disable(); > lock(tasklist_lock); > lock(&pid->wait_pidfd); > <Interrupt> > lock(tasklist_lock); > > *** DEADLOCK *** > > 4 locks held by swapper/1/0: The problem is that because wait_pidfd.lock is taken under the tasklist lock. It must always be taken with irqs disabled as tasklist_lock can be taken from interrupt context and if wait_pidfd.lock was already taken this would create a lock order inversion. Oleg suggested just disabling irqs where I have added extra calls to wait_pidfd.lock. That should be safe and I think the code will eventually do that. It was rightly pointed out by Christian that sharing the wait_pidfd.lock was a premature optimization. It is also true that my pre-merge window testing was insufficient. So remove the premature optimization and give struct pid a dedicated lock of it's own for struct pid things. I have verified that lockdep sees all 3 paths where we take the new pid->lock and lockdep does not complain. It is my current day dream that one day pid->lock can be used to guard the task lists as well and then the tasklist_lock won't need to be held to deliver signals. That will require taking pid->lock with irqs disabled. Acked-by: Christian Brauner <[email protected]> Link: https://lore.kernel.org/lkml/[email protected]/ Cc: Oleg Nesterov <[email protected]> Cc: Christian Brauner <[email protected]> Reported-by: [email protected] Reported-by: [email protected] Reported-by: [email protected] Reported-by: [email protected] Fixes: 7bc3e6e ("proc: Use a list of inodes to flush from proc") Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent d1e7fd6 commit 63f818f

File tree

3 files changed

+7
-5
lines changed

3 files changed

+7
-5
lines changed

fs/proc/base.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,9 +1839,9 @@ void proc_pid_evict_inode(struct proc_inode *ei)
18391839
struct pid *pid = ei->pid;
18401840

18411841
if (S_ISDIR(ei->vfs_inode.i_mode)) {
1842-
spin_lock(&pid->wait_pidfd.lock);
1842+
spin_lock(&pid->lock);
18431843
hlist_del_init_rcu(&ei->sibling_inodes);
1844-
spin_unlock(&pid->wait_pidfd.lock);
1844+
spin_unlock(&pid->lock);
18451845
}
18461846

18471847
put_pid(pid);
@@ -1877,9 +1877,9 @@ struct inode *proc_pid_make_inode(struct super_block * sb,
18771877
/* Let the pid remember us for quick removal */
18781878
ei->pid = pid;
18791879
if (S_ISDIR(mode)) {
1880-
spin_lock(&pid->wait_pidfd.lock);
1880+
spin_lock(&pid->lock);
18811881
hlist_add_head_rcu(&ei->sibling_inodes, &pid->inodes);
1882-
spin_unlock(&pid->wait_pidfd.lock);
1882+
spin_unlock(&pid->lock);
18831883
}
18841884

18851885
task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid);
@@ -3273,7 +3273,7 @@ static const struct inode_operations proc_tgid_base_inode_operations = {
32733273

32743274
void proc_flush_pid(struct pid *pid)
32753275
{
3276-
proc_invalidate_siblings_dcache(&pid->inodes, &pid->wait_pidfd.lock);
3276+
proc_invalidate_siblings_dcache(&pid->inodes, &pid->lock);
32773277
put_pid(pid);
32783278
}
32793279

include/linux/pid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ struct pid
6060
{
6161
refcount_t count;
6262
unsigned int level;
63+
spinlock_t lock;
6364
/* lists of tasks that use this pid */
6465
struct hlist_head tasks[PIDTYPE_MAX];
6566
struct hlist_head inodes;

kernel/pid.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid,
246246

247247
get_pid_ns(ns);
248248
refcount_set(&pid->count, 1);
249+
spin_lock_init(&pid->lock);
249250
for (type = 0; type < PIDTYPE_MAX; ++type)
250251
INIT_HLIST_HEAD(&pid->tasks[type]);
251252

0 commit comments

Comments
 (0)