usb: gadget: f_fs: Fix use-after-free for unbind with remaining io 56/251756/1
authorSeung-Woo Kim <sw0312.kim@samsung.com>
Tue, 19 Jan 2021 05:47:25 +0000 (14:47 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Tue, 19 Jan 2021 05:50:39 +0000 (14:50 +0900)
If usb has stall, then there can be remaining submitted io and
unbinding f_fs with the remaining io, there is use-after-free.
Fix the use-after-free by checking endpoint after wait.

This fixes following kasan warning:
   BUG: KASAN: use-after-free in ffs_epfile_io+0x654/0xb58
   Read of size 4 at addr ffffffc0a44e65dc by task mtp-responder/5117
   ...
   [<ffffff900a037794>] ffs_epfile_io+0x654/0xb58
   [<ffffff900a03818c>] ffs_epfile_read_iter+0x1ac/0x3e0
   ...

   Allocated by task 3869:
   ...
    __kmalloc+0x234/0x760
    _ffs_func_bind+0x264/0x7c8
    ffs_func_bind+0xe8/0x650
    usb_add_function+0x13c/0x378
   ...
   Freed by task 3869:
   ...
    kfree+0xa4/0x750
    ffs_func_unbind+0x150/0x248
    purge_configs_funcs+0x1a0/0x310
   ...

Change-Id: I2bb9b07d93b1ac42432caaa2c2176d987b36b140
Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
drivers/usb/gadget/function/f_fs.c

index 620e509..df74bfa 100644 (file)
@@ -1181,7 +1181,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
 
                spin_unlock_irq(&epfile->ffs->eps_lock);
 
-               if (unlikely(wait_for_completion_interruptible(&done))) {
+               if (unlikely(wait_for_completion_interruptible(&done)) &&
+                   epfile->ep) {
                        /*
                         * To avoid race condition with ffs_epfile_io_complete,
                         * dequeue the request first then check
@@ -1193,6 +1194,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                        interrupted = ep->status < 0;
                }
 
+               if (epfile->ep != ep) {
+                       /* In the meantime, endpoint got disabled or changed. */
+                       ret = -ESHUTDOWN;
+                       goto error_mutex;
+               }
+
                if (interrupted)
                        ret = -EINTR;