diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_fs.c')
| -rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 60 | 
1 files changed, 44 insertions, 16 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a7e069b18544..1922fd02043c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -614,7 +614,7 @@ static int ffs_ep0_open(struct inode *inode, struct file *file)  	file->private_data = ffs;  	ffs_data_opened(ffs); -	return 0; +	return stream_open(inode, file);  }  static int ffs_ep0_release(struct inode *inode, struct file *file) @@ -1154,7 +1154,7 @@ ffs_epfile_open(struct inode *inode, struct file *file)  	file->private_data = epfile;  	ffs_data_opened(epfile->ffs); -	return 0; +	return stream_open(inode, file);  }  static int ffs_aio_cancel(struct kiocb *kiocb) @@ -1711,16 +1711,24 @@ static void ffs_data_put(struct ffs_data *ffs)  static void ffs_data_closed(struct ffs_data *ffs)  { +	struct ffs_epfile *epfiles; +	unsigned long flags; +  	ENTER();  	if (atomic_dec_and_test(&ffs->opened)) {  		if (ffs->no_disconnect) {  			ffs->state = FFS_DEACTIVATED; -			if (ffs->epfiles) { -				ffs_epfiles_destroy(ffs->epfiles, -						   ffs->eps_count); -				ffs->epfiles = NULL; -			} +			spin_lock_irqsave(&ffs->eps_lock, flags); +			epfiles = ffs->epfiles; +			ffs->epfiles = NULL; +			spin_unlock_irqrestore(&ffs->eps_lock, +							flags); + +			if (epfiles) +				ffs_epfiles_destroy(epfiles, +						 ffs->eps_count); +  			if (ffs->setup_state == FFS_SETUP_PENDING)  				__ffs_ep0_stall(ffs);  		} else { @@ -1767,14 +1775,27 @@ static struct ffs_data *ffs_data_new(const char *dev_name)  static void ffs_data_clear(struct ffs_data *ffs)  { +	struct ffs_epfile *epfiles; +	unsigned long flags; +  	ENTER();  	ffs_closed(ffs);  	BUG_ON(ffs->gadget); -	if (ffs->epfiles) { -		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); +	spin_lock_irqsave(&ffs->eps_lock, flags); +	epfiles = ffs->epfiles; +	ffs->epfiles = NULL; +	spin_unlock_irqrestore(&ffs->eps_lock, flags); + +	/* +	 * potential race possible between ffs_func_eps_disable +	 * & ffs_epfile_release therefore maintaining a local +	 * copy of epfile will save us from use-after-free. +	 */ +	if (epfiles) { +		ffs_epfiles_destroy(epfiles, ffs->eps_count);  		ffs->epfiles = NULL;  	} @@ -1922,12 +1943,15 @@ 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_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;  	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 (ep->ep) @@ -1945,14 +1969,18 @@ static void ffs_func_eps_disable(struct ffs_function *func)  static int ffs_func_eps_enable(struct ffs_function *func)  { -	struct ffs_data *ffs      = func->ffs; -	struct ffs_ep *ep         = func->eps; -	struct ffs_epfile *epfile = ffs->epfiles; -	unsigned count            = ffs->eps_count; +	struct ffs_data *ffs; +	struct ffs_ep *ep; +	struct ffs_epfile *epfile; +	unsigned short count;  	unsigned long flags;  	int ret = 0;  	spin_lock_irqsave(&func->ffs->eps_lock, flags); +	ffs = func->ffs; +	ep = func->eps; +	epfile = ffs->epfiles; +	count = ffs->eps_count;  	while(count--) {  		ep->ep->driver_data = ep;  |