lkml.org 
[lkml]   [2022]   [Aug]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.10 07/65] watch_queue: Fix missing locking in add_watch_to_object()
    Date
    From: Linus Torvalds <torvalds@linux-foundation.org>

    commit e64ab2dbd882933b65cd82ff6235d705ad65dbb6 upstream.

    If a watch is being added to a queue, it needs to guard against
    interference from addition of a new watch, manual removal of a watch and
    removal of a watch due to some other queue being destroyed.

    KEYCTL_WATCH_KEY guards against this for the same {key,queue} pair by
    holding the key->sem writelocked and by holding refs on both the key and
    the queue - but that doesn't prevent interaction from other {key,queue}
    pairs.

    While add_watch_to_object() does take the spinlock on the event queue,
    it doesn't take the lock on the source's watch list. The assumption was
    that the caller would prevent that (say by taking key->sem) - but that
    doesn't prevent interference from the destruction of another queue.

    Fix this by locking the watcher list in add_watch_to_object().

    Fixes: c73be61cede5 ("pipe: Add general notification queue support")
    Reported-by: syzbot+03d7b43290037d1f87ca@syzkaller.appspotmail.com
    Signed-off-by: David Howells <dhowells@redhat.com>
    cc: keyrings@vger.kernel.org
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    kernel/watch_queue.c | 58 +++++++++++++++++++++++++++++++--------------------
    1 file changed, 36 insertions(+), 22 deletions(-)

    --- a/kernel/watch_queue.c
    +++ b/kernel/watch_queue.c
    @@ -457,6 +457,33 @@ void init_watch(struct watch *watch, str
    rcu_assign_pointer(watch->queue, wqueue);
    }

    +static int add_one_watch(struct watch *watch, struct watch_list *wlist, struct watch_queue *wqueue)
    +{
    + const struct cred *cred;
    + struct watch *w;
    +
    + hlist_for_each_entry(w, &wlist->watchers, list_node) {
    + struct watch_queue *wq = rcu_access_pointer(w->queue);
    + if (wqueue == wq && watch->id == w->id)
    + return -EBUSY;
    + }
    +
    + cred = current_cred();
    + if (atomic_inc_return(&cred->user->nr_watches) > task_rlimit(current, RLIMIT_NOFILE)) {
    + atomic_dec(&cred->user->nr_watches);
    + return -EAGAIN;
    + }
    +
    + watch->cred = get_cred(cred);
    + rcu_assign_pointer(watch->watch_list, wlist);
    +
    + kref_get(&wqueue->usage);
    + kref_get(&watch->usage);
    + hlist_add_head(&watch->queue_node, &wqueue->watches);
    + hlist_add_head_rcu(&watch->list_node, &wlist->watchers);
    + return 0;
    +}
    +
    /**
    * add_watch_to_object - Add a watch on an object to a watch list
    * @watch: The watch to add
    @@ -471,34 +498,21 @@ void init_watch(struct watch *watch, str
    */
    int add_watch_to_object(struct watch *watch, struct watch_list *wlist)
    {
    - struct watch_queue *wqueue = rcu_access_pointer(watch->queue);
    - struct watch *w;
    + struct watch_queue *wqueue;
    + int ret = -ENOENT;

    - hlist_for_each_entry(w, &wlist->watchers, list_node) {
    - struct watch_queue *wq = rcu_access_pointer(w->queue);
    - if (wqueue == wq && watch->id == w->id)
    - return -EBUSY;
    - }
    -
    - watch->cred = get_current_cred();
    - rcu_assign_pointer(watch->watch_list, wlist);
    -
    - if (atomic_inc_return(&watch->cred->user->nr_watches) >
    - task_rlimit(current, RLIMIT_NOFILE)) {
    - atomic_dec(&watch->cred->user->nr_watches);
    - put_cred(watch->cred);
    - return -EAGAIN;
    - }
    + rcu_read_lock();

    + wqueue = rcu_access_pointer(watch->queue);
    if (lock_wqueue(wqueue)) {
    - kref_get(&wqueue->usage);
    - kref_get(&watch->usage);
    - hlist_add_head(&watch->queue_node, &wqueue->watches);
    + spin_lock(&wlist->lock);
    + ret = add_one_watch(watch, wlist, wqueue);
    + spin_unlock(&wlist->lock);
    unlock_wqueue(wqueue);
    }

    - hlist_add_head_rcu(&watch->list_node, &wlist->watchers);
    - return 0;
    + rcu_read_unlock();
    + return ret;
    }
    EXPORT_SYMBOL(add_watch_to_object);


    \
     
     \ /
      Last update: 2022-08-01 13:55    [W:4.042 / U:0.416 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site