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 <prathampratap@codeaurora.org> Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
This commit is contained in:
committed by
Gerrit - the friendly Code Review server
parent
a4341e9d9e
commit
5235024ca9
@@ -1328,10 +1328,13 @@ ffs_epfile_release(struct inode *inode, struct file *file)
|
|||||||
{
|
{
|
||||||
struct ffs_epfile *epfile = inode->i_private;
|
struct ffs_epfile *epfile = inode->i_private;
|
||||||
struct ffs_data *ffs = epfile->ffs;
|
struct ffs_data *ffs = epfile->ffs;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
ENTER();
|
ENTER();
|
||||||
|
|
||||||
|
spin_lock_irqsave(&epfile->ffs->eps_lock, flags);
|
||||||
__ffs_epfile_read_buffer_free(epfile);
|
__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",
|
ffs_log("%s: state %d setup_state %d flag %lu opened %u",
|
||||||
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
|
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
|
||||||
epfile->ffs->flags, atomic_read(&epfile->opened));
|
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 (atomic_dec_and_test(&ffs->opened)) {
|
||||||
if (ffs->no_disconnect) {
|
if (ffs->no_disconnect) {
|
||||||
ffs->state = FFS_DEACTIVATED;
|
ffs->state = FFS_DEACTIVATED;
|
||||||
|
mutex_lock(&ffs->mutex);
|
||||||
if (ffs->epfiles) {
|
if (ffs->epfiles) {
|
||||||
ffs_epfiles_destroy(ffs->epfiles,
|
ffs_epfiles_destroy(ffs->epfiles,
|
||||||
ffs->eps_count);
|
ffs->eps_count);
|
||||||
ffs->epfiles = NULL;
|
ffs->epfiles = NULL;
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&ffs->mutex);
|
||||||
if (ffs->setup_state == FFS_SETUP_PENDING)
|
if (ffs->setup_state == FFS_SETUP_PENDING)
|
||||||
__ffs_ep0_stall(ffs);
|
__ffs_ep0_stall(ffs);
|
||||||
} else {
|
} else {
|
||||||
@@ -1909,15 +1914,22 @@ static void ffs_data_clear(struct ffs_data *ffs)
|
|||||||
|
|
||||||
BUG_ON(ffs->gadget);
|
BUG_ON(ffs->gadget);
|
||||||
|
|
||||||
if (ffs->epfiles)
|
mutex_lock(&ffs->mutex);
|
||||||
|
if (ffs->epfiles) {
|
||||||
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
|
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
|
||||||
|
ffs->epfiles = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (ffs->ffs_eventfd)
|
if (ffs->ffs_eventfd)
|
||||||
eventfd_ctx_put(ffs->ffs_eventfd);
|
eventfd_ctx_put(ffs->ffs_eventfd);
|
||||||
|
|
||||||
kfree(ffs->raw_descs_data);
|
kfree(ffs->raw_descs_data);
|
||||||
|
ffs->raw_descs_data = NULL;
|
||||||
kfree(ffs->raw_strings);
|
kfree(ffs->raw_strings);
|
||||||
|
ffs->raw_strings = NULL;
|
||||||
kfree(ffs->stringtabs);
|
kfree(ffs->stringtabs);
|
||||||
|
ffs->stringtabs = NULL;
|
||||||
|
mutex_unlock(&ffs->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ffs_data_reset(struct ffs_data *ffs)
|
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_data_clear(ffs);
|
||||||
|
|
||||||
ffs->epfiles = NULL;
|
|
||||||
ffs->raw_descs_data = NULL;
|
|
||||||
ffs->raw_descs = NULL;
|
ffs->raw_descs = NULL;
|
||||||
ffs->raw_strings = NULL;
|
|
||||||
ffs->stringtabs = NULL;
|
|
||||||
|
|
||||||
ffs->raw_descs_length = 0;
|
ffs->raw_descs_length = 0;
|
||||||
ffs->fs_descs_count = 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)
|
static void ffs_func_eps_disable(struct ffs_function *func)
|
||||||
{
|
{
|
||||||
struct ffs_ep *ep = func->eps;
|
|
||||||
struct ffs_data *ffs = func->ffs;
|
struct ffs_data *ffs = func->ffs;
|
||||||
struct ffs_epfile *epfile = func->ffs->epfiles;
|
struct ffs_ep *ep;
|
||||||
unsigned count = func->ffs->eps_count;
|
struct ffs_epfile *epfile;
|
||||||
|
unsigned short count;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
ffs_log("enter: state %d setup_state %d flag %lu", func->ffs->state,
|
ffs_log("enter: state %d setup_state %d flag %lu", func->ffs->state,
|
||||||
func->ffs->setup_state, func->ffs->flags);
|
func->ffs->setup_state, func->ffs->flags);
|
||||||
|
|
||||||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||||
|
count = func->ffs->eps_count;
|
||||||
|
epfile = func->ffs->epfiles;
|
||||||
|
ep = func->eps;
|
||||||
while (count--) {
|
while (count--) {
|
||||||
/* pending requests get nuked */
|
/* pending requests get nuked */
|
||||||
if (likely(ep->ep))
|
if (likely(ep->ep))
|
||||||
|
|||||||
Reference in New Issue
Block a user