• Sahitya Tummala's avatar
    fuse: fix use after free issue in fuse_dev_do_read() · 6ba4d272
    Sahitya Tummala authored
    There is a potential race between fuse_dev_do_write()
    and request_wait_answer() contexts as shown below:
    
    TASK 1:
    __fuse_request_send():
      |--spin_lock(&fiq->waitq.lock);
      |--queue_request();
      |--spin_unlock(&fiq->waitq.lock);
      |--request_wait_answer():
           |--if (test_bit(FR_SENT, &req->flags))
           <gets pre-empted after it is validated true>
                                       TASK 2:
                                       fuse_dev_do_write():
                                         |--clears bit FR_SENT,
                                         |--request_end():
                                            |--sets bit FR_FINISHED
                                            |--spin_lock(&fiq->waitq.lock);
                                            |--list_del_init(&req->intr_entry);
                                            |--spin_unlock(&fiq->waitq.lock);
                                            |--fuse_put_request();
           |--queue_interrupt();
           <request gets queued to interrupts list>
                |--wake_up_locked(&fiq->waitq);
           |--wait_event_freezable();
           <as FR_FINISHED is set, it returns and then
           the caller frees this request>
    
    Now, the next fuse_dev_do_read(), see interrupts list is not empty
    and then calls fuse_read_interrupt() which tries to access the request
    which is already free'd and gets the below crash:
    
    [11432.401266] Unable to handle kernel paging request at virtual address
    6b6b6b6b6b6b6b6b
    ...
    [11432.418518] Kernel BUG at ffffff80083720e0
    [11432.456168] PC is at __list_del_entry+0x6c/0xc4
    [11432.463573] LR is at fuse_dev_do_read+0x1ac/0x474
    ...
    [11432.679999] [<ffffff80083720e0>] __list_del_entry+0x6c/0xc4
    [11432.687794] [<ffffff80082c65e0>] fuse_dev_do_read+0x1ac/0x474
    [11432.693180] [<ffffff80082c6b14>] fuse_dev_read+0x6c/0x78
    [11432.699082] [<ffffff80081d5638>] __vfs_read+0xc0/0xe8
    [11432.704459] [<ffffff80081d5efc>] vfs_read+0x90/0x108
    [11432.709406] [<ffffff80081d67f0>] SyS_read+0x58/0x94
    
    As FR_FINISHED bit is set before deleting the intr_entry with input
    queue lock in request completion path, do the testing of this flag and
    queueing atomically with the same lock in queue_interrupt().
    Signed-off-by: default avatarSahitya Tummala <stummala@codeaurora.org>
    Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
    Fixes: fd22d62e ("fuse: no fc->lock for iqueue parts")
    Cc: <stable@vger.kernel.org> # 4.2+
    6ba4d272
dev.c 51.1 KB