lkml.org 
[lkml]   [2017]   [Nov]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.13 03/33] workqueue: Fix NULL pointer dereference
    Date
    4.13-stable review patch.  If anyone has any objections, please let me know.

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

    From: Li Bin <huawei.libin@huawei.com>

    commit cef572ad9bd7f85035ba8272e5352040e8be0152 upstream.

    When queue_work() is used in irq (not in task context), there is
    a potential case that trigger NULL pointer dereference.
    ----------------------------------------------------------------
    worker_thread()
    |-spin_lock_irq()
    |-process_one_work()
    |-worker->current_pwq = pwq
    |-spin_unlock_irq()
    |-worker->current_func(work)
    |-spin_lock_irq()
    |-worker->current_pwq = NULL
    |-spin_unlock_irq()

    //interrupt here
    |-irq_handler
    |-__queue_work()
    //assuming that the wq is draining
    |-is_chained_work(wq)
    |-current_wq_worker()
    //Here, 'current' is the interrupted worker!
    |-current->current_pwq is NULL here!
    |-schedule()
    ----------------------------------------------------------------

    Avoid it by checking for task context in current_wq_worker(), and
    if not in task context, we shouldn't use the 'current' to check the
    condition.

    Reported-by: Xiaofei Tan <tanxiaofei@huawei.com>
    Signed-off-by: Li Bin <huawei.libin@huawei.com>
    Reviewed-by: Lai Jiangshan <jiangshanlai@gmail.com>
    Signed-off-by: Tejun Heo <tj@kernel.org>
    Fixes: 8d03ecfe4718 ("workqueue: reimplement is_chained_work() using current_wq_worker()")
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    kernel/workqueue_internal.h | 3 ++-
    1 file changed, 2 insertions(+), 1 deletion(-)

    --- a/kernel/workqueue_internal.h
    +++ b/kernel/workqueue_internal.h
    @@ -9,6 +9,7 @@

    #include <linux/workqueue.h>
    #include <linux/kthread.h>
    +#include <linux/preempt.h>

    struct worker_pool;

    @@ -59,7 +60,7 @@ struct worker {
    */
    static inline struct worker *current_wq_worker(void)
    {
    - if (current->flags & PF_WQ_WORKER)
    + if (in_task() && (current->flags & PF_WQ_WORKER))
    return kthread_data(current);
    return NULL;
    }

    \
     
     \ /
      Last update: 2017-11-13 14:13    [W:4.087 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site