lkml.org 
[lkml]   [2020]   [Dec]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL 5.10 035/217] fcntl: Fix potential deadlock in send_sig{io, urg}()
    Date
    From: Boqun Feng <boqun.feng@gmail.com>

    [ Upstream commit 8d1ddb5e79374fb277985a6b3faa2ed8631c5b4c ]

    Syzbot reports a potential deadlock found by the newly added recursive
    read deadlock detection in lockdep:

    [...] ========================================================
    [...] WARNING: possible irq lock inversion dependency detected
    [...] 5.9.0-rc2-syzkaller #0 Not tainted
    [...] --------------------------------------------------------
    [...] syz-executor.1/10214 just changed the state of lock:
    [...] ffff88811f506338 (&f->f_owner.lock){.+..}-{2:2}, at: send_sigurg+0x1d/0x200
    [...] but this lock was taken by another, HARDIRQ-safe lock in the past:
    [...] (&dev->event_lock){-...}-{2:2}
    [...]
    [...]
    [...] and interrupts could create inverse lock ordering between them.
    [...]
    [...]
    [...] other info that might help us debug this:
    [...] Chain exists of:
    [...] &dev->event_lock --> &new->fa_lock --> &f->f_owner.lock
    [...]
    [...] Possible interrupt unsafe locking scenario:
    [...]
    [...] CPU0 CPU1
    [...] ---- ----
    [...] lock(&f->f_owner.lock);
    [...] local_irq_disable();
    [...] lock(&dev->event_lock);
    [...] lock(&new->fa_lock);
    [...] <Interrupt>
    [...] lock(&dev->event_lock);
    [...]
    [...] *** DEADLOCK ***

    The corresponding deadlock case is as followed:

    CPU 0 CPU 1 CPU 2
    read_lock(&fown->lock);
    spin_lock_irqsave(&dev->event_lock, ...)
    write_lock_irq(&filp->f_owner.lock); // wait for the lock
    read_lock(&fown-lock); // have to wait until the writer release
    // due to the fairness
    <interrupted>
    spin_lock_irqsave(&dev->event_lock); // wait for the lock

    The lock dependency on CPU 1 happens if there exists a call sequence:

    input_inject_event():
    spin_lock_irqsave(&dev->event_lock,...);
    input_handle_event():
    input_pass_values():
    input_to_handler():
    handler->event(): // evdev_event()
    evdev_pass_values():
    spin_lock(&client->buffer_lock);
    __pass_event():
    kill_fasync():
    kill_fasync_rcu():
    read_lock(&fa->fa_lock);
    send_sigio():
    read_lock(&fown->lock);

    To fix this, make the reader in send_sigurg() and send_sigio() use
    read_lock_irqsave() and read_lock_irqrestore().

    Reported-by: syzbot+22e87cdf94021b984aa6@syzkaller.appspotmail.com
    Reported-by: syzbot+c5e32344981ad9f33750@syzkaller.appspotmail.com
    Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
    Signed-off-by: Jeff Layton <jlayton@kernel.org>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    fs/fcntl.c | 10 ++++++----
    1 file changed, 6 insertions(+), 4 deletions(-)

    diff --git a/fs/fcntl.c b/fs/fcntl.c
    index 19ac5baad50fd..05b36b28f2e87 100644
    --- a/fs/fcntl.c
    +++ b/fs/fcntl.c
    @@ -781,9 +781,10 @@ void send_sigio(struct fown_struct *fown, int fd, int band)
    {
    struct task_struct *p;
    enum pid_type type;
    + unsigned long flags;
    struct pid *pid;

    - read_lock(&fown->lock);
    + read_lock_irqsave(&fown->lock, flags);

    type = fown->pid_type;
    pid = fown->pid;
    @@ -804,7 +805,7 @@ void send_sigio(struct fown_struct *fown, int fd, int band)
    read_unlock(&tasklist_lock);
    }
    out_unlock_fown:
    - read_unlock(&fown->lock);
    + read_unlock_irqrestore(&fown->lock, flags);
    }

    static void send_sigurg_to_task(struct task_struct *p,
    @@ -819,9 +820,10 @@ int send_sigurg(struct fown_struct *fown)
    struct task_struct *p;
    enum pid_type type;
    struct pid *pid;
    + unsigned long flags;
    int ret = 0;

    - read_lock(&fown->lock);
    + read_lock_irqsave(&fown->lock, flags);

    type = fown->pid_type;
    pid = fown->pid;
    @@ -844,7 +846,7 @@ int send_sigurg(struct fown_struct *fown)
    read_unlock(&tasklist_lock);
    }
    out_unlock_fown:
    - read_unlock(&fown->lock);
    + read_unlock_irqrestore(&fown->lock, flags);
    return ret;
    }

    --
    2.27.0
    \
     
     \ /
      Last update: 2020-12-23 04:10    [W:4.084 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site