From 5235024ca9839967b5edadf445964429a8dab3ae Mon Sep 17 00:00:00 2001 From: Pratham Pratap Date: Tue, 2 Feb 2021 14:42:56 +0530 Subject: [PATCH] usb: f_fs: Prevent race & use after free scenario on epfile In case of fast composition switch along with disconnect/connect performed, there are chances that the process of ep_disable, function_disable, epfile_release & ep0_release race between each other causing use after free scenarios. This is seen with epfile struct itself and also with the read_buffer member variable as well. This change is a squash of below commits. (1c3b63b8d818) usb: f_fs: Avoid use-after-free of epfile. (971a85773340) usb: f_fs: Fix use-after-free for epfile. (a008a43c5cd8) usb: f_fs: Prevent race between ep0_release & reset_work. (f7fbba49e4a8) usb: f_fs: Fix Double free from ffs_data_clear. Change-Id: If3f69e1e25be810aa82931b65fa2506b2e9f33bd Signed-off-by: Pratham Pratap Signed-off-by: Udipto Goswami --- drivers/usb/gadget/function/f_fs.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index f7ba1e567a35..cef4e8fd0d6c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1328,10 +1328,13 @@ ffs_epfile_release(struct inode *inode, struct file *file) { struct ffs_epfile *epfile = inode->i_private; struct ffs_data *ffs = epfile->ffs; + unsigned long flags; ENTER(); + spin_lock_irqsave(&epfile->ffs->eps_lock, flags); __ffs_epfile_read_buffer_free(epfile); + spin_unlock_irqrestore(&epfile->ffs->eps_lock, flags); ffs_log("%s: state %d setup_state %d flag %lu opened %u", epfile->name, epfile->ffs->state, epfile->ffs->setup_state, epfile->ffs->flags, atomic_read(&epfile->opened)); @@ -1833,11 +1836,13 @@ static void ffs_data_closed(struct ffs_data *ffs) if (atomic_dec_and_test(&ffs->opened)) { if (ffs->no_disconnect) { ffs->state = FFS_DEACTIVATED; + mutex_lock(&ffs->mutex); if (ffs->epfiles) { ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); ffs->epfiles = NULL; } + mutex_unlock(&ffs->mutex); if (ffs->setup_state == FFS_SETUP_PENDING) __ffs_ep0_stall(ffs); } else { @@ -1909,15 +1914,22 @@ static void ffs_data_clear(struct ffs_data *ffs) BUG_ON(ffs->gadget); - if (ffs->epfiles) + mutex_lock(&ffs->mutex); + if (ffs->epfiles) { ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); + ffs->epfiles = NULL; + } if (ffs->ffs_eventfd) eventfd_ctx_put(ffs->ffs_eventfd); kfree(ffs->raw_descs_data); + ffs->raw_descs_data = NULL; kfree(ffs->raw_strings); + ffs->raw_strings = NULL; kfree(ffs->stringtabs); + ffs->stringtabs = NULL; + mutex_unlock(&ffs->mutex); } static void ffs_data_reset(struct ffs_data *ffs) @@ -1929,11 +1941,7 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs_data_clear(ffs); - ffs->epfiles = NULL; - ffs->raw_descs_data = NULL; ffs->raw_descs = NULL; - ffs->raw_strings = NULL; - ffs->stringtabs = NULL; ffs->raw_descs_length = 0; ffs->fs_descs_count = 0; @@ -2073,16 +2081,19 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) static void ffs_func_eps_disable(struct ffs_function *func) { - struct ffs_ep *ep = func->eps; struct ffs_data *ffs = func->ffs; - struct ffs_epfile *epfile = func->ffs->epfiles; - unsigned count = func->ffs->eps_count; + struct ffs_ep *ep; + struct ffs_epfile *epfile; + unsigned short count; unsigned long flags; ffs_log("enter: state %d setup_state %d flag %lu", func->ffs->state, func->ffs->setup_state, func->ffs->flags); spin_lock_irqsave(&func->ffs->eps_lock, flags); + count = func->ffs->eps_count; + epfile = func->ffs->epfiles; + ep = func->eps; while (count--) { /* pending requests get nuked */ if (likely(ep->ep))