summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUdipto Goswami <quic_ugoswami@quicinc.com>2022-12-15 10:59:05 +0530
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-02-06 07:49:39 +0100
commite9036e951f93fb8d7b5e9d6e2c7f94a4da312ae4 (patch)
tree022ee3cd07549562868e8b299b622660d3b1863c
parentf958da03d9a71808548b2e5418d95482b106eb9a (diff)
downloadlinux-stable-e9036e951f93fb8d7b5e9d6e2c7f94a4da312ae4.tar.gz
linux-stable-e9036e951f93fb8d7b5e9d6e2c7f94a4da312ae4.tar.bz2
linux-stable-e9036e951f93fb8d7b5e9d6e2c7f94a4da312ae4.zip
usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait
[ Upstream commit 6a19da111057f69214b97c62fb0ac59023970850 ] While performing fast composition switch, there is a possibility that the process of ffs_ep0_write/ffs_ep0_read get into a race condition due to ep0req being freed up from functionfs_unbind. Consider the scenario that the ffs_ep0_write calls the ffs_ep0_queue_wait by taking a lock &ffs->ev.waitq.lock. However, the functionfs_unbind isn't bounded so it can go ahead and mark the ep0req to NULL, and since there is no NULL check in ffs_ep0_queue_wait we will end up in use-after-free. Fix this by making a serialized execution between the two functions using a mutex_lock(ffs->mutex). Fixes: ddf8abd25994 ("USB: f_fs: the FunctionFS driver") Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com> Tested-by: Krishna Kurapati <quic_kriskura@quicinc.com> Link: https://lore.kernel.org/r/20221215052906.8993-2-quic_ugoswami@quicinc.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--drivers/usb/gadget/function/f_fs.c5
1 files changed, 5 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 49eb4e3c760f..b3489a3449f6 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -271,6 +271,9 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
struct usb_request *req = ffs->ep0req;
int ret;
+ if (!req)
+ return -EINVAL;
+
req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -1807,10 +1810,12 @@ static void functionfs_unbind(struct ffs_data *ffs)
ENTER();
if (!WARN_ON(!ffs->gadget)) {
+ mutex_lock(&ffs->mutex);
usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
ffs->ep0req = NULL;
ffs->gadget = NULL;
clear_bit(FFS_FL_BOUND, &ffs->flags);
+ mutex_unlock(&ffs->mutex);
ffs_data_put(ffs);
}
}