Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

functionfs: use spinlock for FFS_DEACTIVATED/FFS_CLOSING transitions

When all files are closed, functionfs needs ffs_data_reset() to be
done before any further opens are allowed.

During that time we have ffs->state set to FFS_CLOSING; that makes
->open() fail with -EBUSY. Once ffs_data_reset() is done, it
switches state (to FFS_READ_DESCRIPTORS) indicating that opening
that thing is allowed again. There's a couple of additional twists:
* mounting with -o no_disconnect delays ffs_data_reset()
from doing that at the final ->release() to the first subsequent
open(). That's indicated by ffs->state set to FFS_DEACTIVATED;
if open() sees that, it immediately switches to FFS_CLOSING and
proceeds with doing ffs_data_reset() before returning to userland.
* a couple of usb callbacks need to force the delayed
transition; unfortunately, they are done in locking environment
that does not allow blocking and ffs_data_reset() can block.
As the result, if these callbacks see FFS_DEACTIVATED, they change
state to FFS_CLOSING and use schedule_work() to get ffs_data_reset()
executed asynchronously.

Unfortunately, the locking is rather insufficient. A fix attempted
in e5bf5ee26663 ("functionfs: fix the open/removal races") had closed
a bunch of UAF, but it didn't do anything to the callbacks, lacked
barriers in transition from FFS_CLOSING to FFS_READ_DESCRIPTORS
_and_ it had been too heavy-handed in open()/open() serialization -
I've used ffs->mutex for that, and it's being held over actual IO on
ep0, complete with copy_from_user(), etc.

Even more unfortunately, the userland side is apparently racy enough
to have the resulting timing changes (no failures, just a delayed
return of open(2)) disrupt the things quite badly. Userland bugs
or not, it's a clear regression that needs to be dealt with.

Solution is to use a spinlock for serializing these state checks and
transitions - unlike ffs->mutex it can be taken in these callbacks
and it doesn't disrupt the timings in open().

We could introduce a new spinlock, but it's easier to use the one
that is already there (ffs->eps_lock) instead - the locking
environment is safe for it in all affected places.

Since now it is held over all places that alter or check the
open count (ffs->opened), there's no need to keep that atomic_t -
int would serve just fine and it's simpler that way.

Fixes: e5bf5ee26663 ("functionfs: fix the open/removal races")
Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") # v4.0
Tested-by: Samuel Wu <wusamuel@google.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 2005aabe 351ea48a

+50 -54
+49 -53
drivers/usb/gadget/function/f_fs.c
··· 59 59 __attribute__((malloc)); 60 60 61 61 /* Opened counter handling. */ 62 - static void ffs_data_opened(struct ffs_data *ffs); 63 62 static void ffs_data_closed(struct ffs_data *ffs); 64 63 65 64 /* Called with ffs->mutex held; take over ownership of data. */ ··· 635 636 return ret; 636 637 } 637 638 639 + 640 + static void ffs_data_reset(struct ffs_data *ffs); 641 + 638 642 static int ffs_ep0_open(struct inode *inode, struct file *file) 639 643 { 640 644 struct ffs_data *ffs = inode->i_sb->s_fs_info; 641 - int ret; 642 645 643 - /* Acquire mutex */ 644 - ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); 645 - if (ret < 0) 646 - return ret; 647 - 648 - ffs_data_opened(ffs); 646 + spin_lock_irq(&ffs->eps_lock); 649 647 if (ffs->state == FFS_CLOSING) { 650 - ffs_data_closed(ffs); 651 - mutex_unlock(&ffs->mutex); 648 + spin_unlock_irq(&ffs->eps_lock); 652 649 return -EBUSY; 653 650 } 654 - mutex_unlock(&ffs->mutex); 651 + if (!ffs->opened++ && ffs->state == FFS_DEACTIVATED) { 652 + ffs->state = FFS_CLOSING; 653 + spin_unlock_irq(&ffs->eps_lock); 654 + ffs_data_reset(ffs); 655 + } else { 656 + spin_unlock_irq(&ffs->eps_lock); 657 + } 655 658 file->private_data = ffs; 656 659 657 660 return stream_open(inode, file); ··· 1203 1202 { 1204 1203 struct ffs_data *ffs = inode->i_sb->s_fs_info; 1205 1204 struct ffs_epfile *epfile; 1206 - int ret; 1207 1205 1208 - /* Acquire mutex */ 1209 - ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); 1210 - if (ret < 0) 1211 - return ret; 1212 - 1213 - if (!atomic_inc_not_zero(&ffs->opened)) { 1214 - mutex_unlock(&ffs->mutex); 1206 + spin_lock_irq(&ffs->eps_lock); 1207 + if (!ffs->opened) { 1208 + spin_unlock_irq(&ffs->eps_lock); 1215 1209 return -ENODEV; 1216 1210 } 1217 1211 /* ··· 1216 1220 */ 1217 1221 epfile = smp_load_acquire(&inode->i_private); 1218 1222 if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) { 1219 - mutex_unlock(&ffs->mutex); 1220 - ffs_data_closed(ffs); 1223 + spin_unlock_irq(&ffs->eps_lock); 1221 1224 return -ENODEV; 1222 1225 } 1223 - mutex_unlock(&ffs->mutex); 1226 + ffs->opened++; 1227 + spin_unlock_irq(&ffs->eps_lock); 1224 1228 1225 1229 file->private_data = epfile; 1226 1230 return stream_open(inode, file); ··· 2088 2092 return 0; 2089 2093 } 2090 2094 2091 - static void ffs_data_reset(struct ffs_data *ffs); 2092 - 2093 2095 static void 2094 2096 ffs_fs_kill_sb(struct super_block *sb) 2095 2097 { ··· 2144 2150 refcount_inc(&ffs->ref); 2145 2151 } 2146 2152 2147 - static void ffs_data_opened(struct ffs_data *ffs) 2148 - { 2149 - if (atomic_add_return(1, &ffs->opened) == 1 && 2150 - ffs->state == FFS_DEACTIVATED) { 2151 - ffs->state = FFS_CLOSING; 2152 - ffs_data_reset(ffs); 2153 - } 2154 - } 2155 - 2156 2153 static void ffs_data_put(struct ffs_data *ffs) 2157 2154 { 2158 2155 if (refcount_dec_and_test(&ffs->ref)) { ··· 2161 2176 2162 2177 static void ffs_data_closed(struct ffs_data *ffs) 2163 2178 { 2164 - if (atomic_dec_and_test(&ffs->opened)) { 2165 - if (ffs->no_disconnect) { 2166 - struct ffs_epfile *epfiles; 2167 - unsigned long flags; 2179 + spin_lock_irq(&ffs->eps_lock); 2180 + if (--ffs->opened) { // not the last opener? 2181 + spin_unlock_irq(&ffs->eps_lock); 2182 + return; 2183 + } 2184 + if (ffs->no_disconnect) { 2185 + struct ffs_epfile *epfiles; 2168 2186 2169 - ffs->state = FFS_DEACTIVATED; 2170 - spin_lock_irqsave(&ffs->eps_lock, flags); 2171 - epfiles = ffs->epfiles; 2172 - ffs->epfiles = NULL; 2173 - spin_unlock_irqrestore(&ffs->eps_lock, 2174 - flags); 2187 + ffs->state = FFS_DEACTIVATED; 2188 + epfiles = ffs->epfiles; 2189 + ffs->epfiles = NULL; 2190 + spin_unlock_irq(&ffs->eps_lock); 2175 2191 2176 - if (epfiles) 2177 - ffs_epfiles_destroy(ffs->sb, epfiles, 2178 - ffs->eps_count); 2192 + if (epfiles) 2193 + ffs_epfiles_destroy(ffs->sb, epfiles, 2194 + ffs->eps_count); 2179 2195 2180 - if (ffs->setup_state == FFS_SETUP_PENDING) 2181 - __ffs_ep0_stall(ffs); 2182 - } else { 2183 - ffs->state = FFS_CLOSING; 2184 - ffs_data_reset(ffs); 2185 - } 2196 + if (ffs->setup_state == FFS_SETUP_PENDING) 2197 + __ffs_ep0_stall(ffs); 2198 + } else { 2199 + ffs->state = FFS_CLOSING; 2200 + spin_unlock_irq(&ffs->eps_lock); 2201 + ffs_data_reset(ffs); 2186 2202 } 2187 2203 } 2188 2204 ··· 2200 2214 } 2201 2215 2202 2216 refcount_set(&ffs->ref, 1); 2203 - atomic_set(&ffs->opened, 0); 2217 + ffs->opened = 0; 2204 2218 ffs->state = FFS_READ_DESCRIPTORS; 2205 2219 mutex_init(&ffs->mutex); 2206 2220 spin_lock_init(&ffs->eps_lock); ··· 2252 2266 { 2253 2267 ffs_data_clear(ffs); 2254 2268 2269 + spin_lock_irq(&ffs->eps_lock); 2255 2270 ffs->raw_descs_data = NULL; 2256 2271 ffs->raw_descs = NULL; 2257 2272 ffs->raw_strings = NULL; ··· 2276 2289 ffs->ms_os_descs_ext_prop_count = 0; 2277 2290 ffs->ms_os_descs_ext_prop_name_len = 0; 2278 2291 ffs->ms_os_descs_ext_prop_data_len = 0; 2292 + spin_unlock_irq(&ffs->eps_lock); 2279 2293 } 2280 2294 2281 2295 ··· 3744 3756 { 3745 3757 struct ffs_function *func = ffs_func_from_usb(f); 3746 3758 struct ffs_data *ffs = func->ffs; 3759 + unsigned long flags; 3747 3760 int ret = 0, intf; 3748 3761 3749 3762 if (alt > MAX_ALT_SETTINGS) ··· 3757 3768 if (ffs->func) 3758 3769 ffs_func_eps_disable(ffs->func); 3759 3770 3771 + spin_lock_irqsave(&ffs->eps_lock, flags); 3760 3772 if (ffs->state == FFS_DEACTIVATED) { 3761 3773 ffs->state = FFS_CLOSING; 3774 + spin_unlock_irqrestore(&ffs->eps_lock, flags); 3762 3775 INIT_WORK(&ffs->reset_work, ffs_reset_work); 3763 3776 schedule_work(&ffs->reset_work); 3764 3777 return -ENODEV; 3765 3778 } 3779 + spin_unlock_irqrestore(&ffs->eps_lock, flags); 3766 3780 3767 3781 if (ffs->state != FFS_ACTIVE) 3768 3782 return -ENODEV; ··· 3783 3791 { 3784 3792 struct ffs_function *func = ffs_func_from_usb(f); 3785 3793 struct ffs_data *ffs = func->ffs; 3794 + unsigned long flags; 3786 3795 3787 3796 if (ffs->func) 3788 3797 ffs_func_eps_disable(ffs->func); 3789 3798 3799 + spin_lock_irqsave(&ffs->eps_lock, flags); 3790 3800 if (ffs->state == FFS_DEACTIVATED) { 3791 3801 ffs->state = FFS_CLOSING; 3802 + spin_unlock_irqrestore(&ffs->eps_lock, flags); 3792 3803 INIT_WORK(&ffs->reset_work, ffs_reset_work); 3793 3804 schedule_work(&ffs->reset_work); 3794 3805 return; 3795 3806 } 3807 + spin_unlock_irqrestore(&ffs->eps_lock, flags); 3796 3808 3797 3809 if (ffs->state == FFS_ACTIVE) { 3798 3810 ffs->func = NULL;
+1 -1
drivers/usb/gadget/function/u_fs.h
··· 176 176 /* reference counter */ 177 177 refcount_t ref; 178 178 /* how many files are opened (EP0 and others) */ 179 - atomic_t opened; 179 + int opened; 180 180 181 181 /* EP0 state */ 182 182 enum ffs_state state;