[PATCH] sanitize locking in mark_mounts_for_expiry() and shrink_submounts()

... and fix a race on access of ->mnt_share et.al. without namespace_sem
in the latter.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro bcc5c7d2 7c4b93d8

+24 -81
+24 -81
fs/namespace.c
··· 1210 1210 1211 1211 EXPORT_SYMBOL_GPL(do_add_mount); 1212 1212 1213 - static void expire_mount(struct vfsmount *mnt, struct list_head *mounts, 1214 - struct list_head *umounts) 1215 - { 1216 - spin_lock(&vfsmount_lock); 1217 - 1218 - /* 1219 - * Check if mount is still attached, if not, let whoever holds it deal 1220 - * with the sucker 1221 - */ 1222 - if (mnt->mnt_parent == mnt) { 1223 - spin_unlock(&vfsmount_lock); 1224 - return; 1225 - } 1226 - 1227 - /* 1228 - * Check that it is still dead: the count should now be 2 - as 1229 - * contributed by the vfsmount parent and the mntget above 1230 - */ 1231 - if (!propagate_mount_busy(mnt, 2)) { 1232 - /* delete from the namespace */ 1233 - touch_mnt_namespace(mnt->mnt_ns); 1234 - list_del_init(&mnt->mnt_list); 1235 - mnt->mnt_ns = NULL; 1236 - umount_tree(mnt, 1, umounts); 1237 - spin_unlock(&vfsmount_lock); 1238 - } else { 1239 - /* 1240 - * Someone brought it back to life whilst we didn't have any 1241 - * locks held so return it to the expiration list 1242 - */ 1243 - list_add_tail(&mnt->mnt_expire, mounts); 1244 - spin_unlock(&vfsmount_lock); 1245 - } 1246 - } 1247 - 1248 - /* 1249 - * go through the vfsmounts we've just consigned to the graveyard to 1250 - * - check that they're still dead 1251 - * - delete the vfsmount from the appropriate namespace under lock 1252 - * - dispose of the corpse 1253 - */ 1254 - static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts) 1255 - { 1256 - struct mnt_namespace *ns; 1257 - struct vfsmount *mnt; 1258 - 1259 - while (!list_empty(graveyard)) { 1260 - LIST_HEAD(umounts); 1261 - mnt = list_first_entry(graveyard, struct vfsmount, mnt_expire); 1262 - list_del_init(&mnt->mnt_expire); 1263 - 1264 - /* don't do anything if the namespace is dead - all the 1265 - * vfsmounts from it are going away anyway */ 1266 - ns = mnt->mnt_ns; 1267 - if (!ns || !ns->root) 1268 - continue; 1269 - get_mnt_ns(ns); 1270 - 1271 - spin_unlock(&vfsmount_lock); 1272 - down_write(&namespace_sem); 1273 - expire_mount(mnt, mounts, &umounts); 1274 - up_write(&namespace_sem); 1275 - release_mounts(&umounts); 1276 - mntput(mnt); 1277 - put_mnt_ns(ns); 1278 - spin_lock(&vfsmount_lock); 1279 - } 1280 - } 1281 - 1282 1213 /* 1283 1214 * process a list of expirable mountpoints with the intent of discarding any 1284 1215 * mountpoints that aren't in use and haven't been touched since last we came ··· 1219 1288 { 1220 1289 struct vfsmount *mnt, *next; 1221 1290 LIST_HEAD(graveyard); 1291 + LIST_HEAD(umounts); 1222 1292 1223 1293 if (list_empty(mounts)) 1224 1294 return; 1225 1295 1296 + down_write(&namespace_sem); 1226 1297 spin_lock(&vfsmount_lock); 1227 1298 1228 1299 /* extract from the expiration list every vfsmount that matches the ··· 1235 1302 */ 1236 1303 list_for_each_entry_safe(mnt, next, mounts, mnt_expire) { 1237 1304 if (!xchg(&mnt->mnt_expiry_mark, 1) || 1238 - atomic_read(&mnt->mnt_count) != 1) 1305 + propagate_mount_busy(mnt, 1)) 1239 1306 continue; 1240 - 1241 - mntget(mnt); 1242 1307 list_move(&mnt->mnt_expire, &graveyard); 1243 1308 } 1244 - 1245 - expire_mount_list(&graveyard, mounts); 1246 - 1309 + while (!list_empty(&graveyard)) { 1310 + mnt = list_first_entry(&graveyard, struct vfsmount, mnt_expire); 1311 + touch_mnt_namespace(mnt->mnt_ns); 1312 + umount_tree(mnt, 1, &umounts); 1313 + } 1247 1314 spin_unlock(&vfsmount_lock); 1315 + up_write(&namespace_sem); 1316 + 1317 + release_mounts(&umounts); 1248 1318 } 1249 1319 1250 1320 EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); ··· 1283 1347 } 1284 1348 1285 1349 if (!propagate_mount_busy(mnt, 1)) { 1286 - mntget(mnt); 1287 1350 list_move_tail(&mnt->mnt_expire, graveyard); 1288 1351 found++; 1289 1352 } ··· 1305 1370 void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts) 1306 1371 { 1307 1372 LIST_HEAD(graveyard); 1308 - int found; 1373 + LIST_HEAD(umounts); 1374 + struct vfsmount *mnt; 1309 1375 1376 + down_write(&namespace_sem); 1310 1377 spin_lock(&vfsmount_lock); 1311 - 1312 1378 /* extract submounts of 'mountpoint' from the expiration list */ 1313 - while ((found = select_submounts(mountpoint, &graveyard)) != 0) 1314 - expire_mount_list(&graveyard, mounts); 1315 - 1379 + while (select_submounts(mountpoint, &graveyard)) { 1380 + while (!list_empty(&graveyard)) { 1381 + mnt = list_first_entry(&graveyard, struct vfsmount, 1382 + mnt_expire); 1383 + touch_mnt_namespace(mnt->mnt_ns); 1384 + umount_tree(mnt, 1, &umounts); 1385 + } 1386 + } 1316 1387 spin_unlock(&vfsmount_lock); 1388 + up_write(&namespace_sem); 1389 + release_mounts(&umounts); 1317 1390 } 1318 1391 1319 1392 EXPORT_SYMBOL_GPL(shrink_submounts);