lkml.org 
[lkml]   [2012]   [Feb]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[ 70/72] epoll: ep_unregister_pollwait() can use the freed pwq->whead
    3.2-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Oleg Nesterov <oleg@redhat.com>

    commit 971316f0503a5c50633d07b83b6db2f15a3a5b00 upstream.

    signalfd_cleanup() ensures that ->signalfd_wqh is not used, but
    this is not enough. eppoll_entry->whead still points to the memory
    we are going to free, ep_unregister_pollwait()->remove_wait_queue()
    is obviously unsafe.

    Change ep_poll_callback(POLLFREE) to set eppoll_entry->whead = NULL,
    change ep_unregister_pollwait() to check pwq->whead != NULL under
    rcu_read_lock() before remove_wait_queue(). We add the new helper,
    ep_remove_wait_queue(), for this.

    This works because sighand_cachep is SLAB_DESTROY_BY_RCU and because
    ->signalfd_wqh is initialized in sighand_ctor(), not in copy_sighand.
    ep_unregister_pollwait()->remove_wait_queue() can play with already
    freed and potentially reused ->sighand, but this is fine. This memory
    must have the valid ->signalfd_wqh until rcu_read_unlock().

    Reported-by: Maxime Bizon <mbizon@freebox.fr>
    Signed-off-by: Oleg Nesterov <oleg@redhat.com>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/eventpoll.c | 30 +++++++++++++++++++++++++++---
    fs/signalfd.c | 6 +++++-
    2 files changed, 32 insertions(+), 4 deletions(-)

    --- a/fs/eventpoll.c
    +++ b/fs/eventpoll.c
    @@ -299,6 +299,11 @@ static inline int ep_is_linked(struct li
    return !list_empty(p);
    }

    +static inline struct eppoll_entry *ep_pwq_from_wait(wait_queue_t *p)
    +{
    + return container_of(p, struct eppoll_entry, wait);
    +}
    +
    /* Get the "struct epitem" from a wait queue pointer */
    static inline struct epitem *ep_item_from_wait(wait_queue_t *p)
    {
    @@ -446,6 +451,18 @@ static void ep_poll_safewake(wait_queue_
    put_cpu();
    }

    +static void ep_remove_wait_queue(struct eppoll_entry *pwq)
    +{
    + wait_queue_head_t *whead;
    +
    + rcu_read_lock();
    + /* If it is cleared by POLLFREE, it should be rcu-safe */
    + whead = rcu_dereference(pwq->whead);
    + if (whead)
    + remove_wait_queue(whead, &pwq->wait);
    + rcu_read_unlock();
    +}
    +
    /*
    * This function unregisters poll callbacks from the associated file
    * descriptor. Must be called with "mtx" held (or "epmutex" if called from
    @@ -460,7 +477,7 @@ static void ep_unregister_pollwait(struc
    pwq = list_first_entry(lsthead, struct eppoll_entry, llink);

    list_del(&pwq->llink);
    - remove_wait_queue(pwq->whead, &pwq->wait);
    + ep_remove_wait_queue(pwq);
    kmem_cache_free(pwq_cache, pwq);
    }
    }
    @@ -827,9 +844,16 @@ static int ep_poll_callback(wait_queue_t
    struct epitem *epi = ep_item_from_wait(wait);
    struct eventpoll *ep = epi->ep;

    - /* the caller holds eppoll_entry->whead->lock */
    - if ((unsigned long)key & POLLFREE)
    + if ((unsigned long)key & POLLFREE) {
    + ep_pwq_from_wait(wait)->whead = NULL;
    + /*
    + * whead = NULL above can race with ep_remove_wait_queue()
    + * which can do another remove_wait_queue() after us, so we
    + * can't use __remove_wait_queue(). whead->lock is held by
    + * the caller.
    + */
    list_del_init(&wait->task_list);
    + }

    spin_lock_irqsave(&ep->lock, flags);

    --- a/fs/signalfd.c
    +++ b/fs/signalfd.c
    @@ -33,7 +33,11 @@
    void signalfd_cleanup(struct sighand_struct *sighand)
    {
    wait_queue_head_t *wqh = &sighand->signalfd_wqh;
    -
    + /*
    + * The lockless check can race with remove_wait_queue() in progress,
    + * but in this case its caller should run under rcu_read_lock() and
    + * sighand_cachep is SLAB_DESTROY_BY_RCU, we can safely return.
    + */
    if (likely(!waitqueue_active(wqh)))
    return;




    \
     
     \ /
      Last update: 2012-02-28 02:19    [W:0.027 / U:0.992 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site