Skip to content

Commit 7484e15

Browse files
author
Al Viro
committed
replace collect_mounts()/drop_collected_mounts() with a safer variant
collect_mounts() has several problems - one can't iterate over the results directly, so it has to be done with callback passed to iterate_mounts(); it has an oopsable race with d_invalidate(); it creates temporary clones of mounts invisibly for sync umount (IOW, you can have non-lazy umount succeed leaving filesystem not mounted anywhere and yet still busy). A saner approach is to give caller an array of struct path that would pin every mount in a subtree, without cloning any mounts. * collect_mounts()/drop_collected_mounts()/iterate_mounts() is gone * collect_paths(where, preallocated, size) gives either ERR_PTR(-E...) or a pointer to array of struct path, one for each chunk of tree visible under 'where' (i.e. the first element is a copy of where, followed by (mount,root) for everything mounted under it - the same set collect_mounts() would give). Unlike collect_mounts(), the mounts are *not* cloned - we just get pinning references to the roots of subtrees in the caller's namespace. Array is terminated by {NULL, NULL} struct path. If it fits into preallocated array (on-stack, normally), that's where it goes; otherwise it's allocated by kmalloc_array(). Passing 0 as size means that 'preallocated' is ignored (and expected to be NULL). * drop_collected_paths(paths, preallocated) is given the array returned by an earlier call of collect_paths() and the preallocated array passed to that call. All mount/dentry references are dropped and array is kfree'd if it's not equal to 'preallocated'. * instead of iterate_mounts(), users should just iterate over array of struct path - nothing exotic is needed for that. Existing users (all in audit_tree.c) are converted. [folded a fix for braino reported by Venkat Rao Bagalkote <[email protected]>] Fixes: 80b5dce ("vfs: Add a function to lazily unmount all mounts from any dentry") Tested-by: Venkat Rao Bagalkote <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 19272b3 commit 7484e15

File tree

5 files changed

+104
-73
lines changed

5 files changed

+104
-73
lines changed

Documentation/filesystems/porting.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,3 +1249,12 @@ Using try_lookup_noperm() will require linux/namei.h to be included.
12491249

12501250
Calling conventions for ->d_automount() have changed; we should *not* grab
12511251
an extra reference to new mount - it should be returned with refcount 1.
1252+
1253+
---
1254+
1255+
collect_mounts()/drop_collected_mounts()/iterate_mounts() are gone now.
1256+
Replacement is collect_paths()/drop_collected_path(), with no special
1257+
iterator needed. Instead of a cloned mount tree, the new interface returns
1258+
an array of struct path, one for each mount collect_mounts() would've
1259+
created. These struct path point to locations in the caller's namespace
1260+
that would be roots of the cloned mounts.

fs/namespace.c

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,21 +2310,62 @@ struct mount *copy_tree(struct mount *src_root, struct dentry *dentry,
23102310
return dst_mnt;
23112311
}
23122312

2313-
/* Caller should check returned pointer for errors */
2313+
static inline bool extend_array(struct path **res, struct path **to_free,
2314+
unsigned n, unsigned *count, unsigned new_count)
2315+
{
2316+
struct path *p;
2317+
2318+
if (likely(n < *count))
2319+
return true;
2320+
p = kmalloc_array(new_count, sizeof(struct path), GFP_KERNEL);
2321+
if (p && *count)
2322+
memcpy(p, *res, *count * sizeof(struct path));
2323+
*count = new_count;
2324+
kfree(*to_free);
2325+
*to_free = *res = p;
2326+
return p;
2327+
}
23142328

2315-
struct vfsmount *collect_mounts(const struct path *path)
2329+
struct path *collect_paths(const struct path *path,
2330+
struct path *prealloc, unsigned count)
23162331
{
2317-
struct mount *tree;
2318-
namespace_lock();
2319-
if (!check_mnt(real_mount(path->mnt)))
2320-
tree = ERR_PTR(-EINVAL);
2321-
else
2322-
tree = copy_tree(real_mount(path->mnt), path->dentry,
2323-
CL_COPY_ALL | CL_PRIVATE);
2324-
namespace_unlock();
2325-
if (IS_ERR(tree))
2326-
return ERR_CAST(tree);
2327-
return &tree->mnt;
2332+
struct mount *root = real_mount(path->mnt);
2333+
struct mount *child;
2334+
struct path *res = prealloc, *to_free = NULL;
2335+
unsigned n = 0;
2336+
2337+
guard(rwsem_read)(&namespace_sem);
2338+
2339+
if (!check_mnt(root))
2340+
return ERR_PTR(-EINVAL);
2341+
if (!extend_array(&res, &to_free, 0, &count, 32))
2342+
return ERR_PTR(-ENOMEM);
2343+
res[n++] = *path;
2344+
list_for_each_entry(child, &root->mnt_mounts, mnt_child) {
2345+
if (!is_subdir(child->mnt_mountpoint, path->dentry))
2346+
continue;
2347+
for (struct mount *m = child; m; m = next_mnt(m, child)) {
2348+
if (!extend_array(&res, &to_free, n, &count, 2 * count))
2349+
return ERR_PTR(-ENOMEM);
2350+
res[n].mnt = &m->mnt;
2351+
res[n].dentry = m->mnt.mnt_root;
2352+
n++;
2353+
}
2354+
}
2355+
if (!extend_array(&res, &to_free, n, &count, count + 1))
2356+
return ERR_PTR(-ENOMEM);
2357+
memset(res + n, 0, (count - n) * sizeof(struct path));
2358+
for (struct path *p = res; p->mnt; p++)
2359+
path_get(p);
2360+
return res;
2361+
}
2362+
2363+
void drop_collected_paths(struct path *paths, struct path *prealloc)
2364+
{
2365+
for (struct path *p = paths; p->mnt; p++)
2366+
path_put(p);
2367+
if (paths != prealloc)
2368+
kfree(paths);
23282369
}
23292370

23302371
static void free_mnt_ns(struct mnt_namespace *);
@@ -2401,15 +2442,6 @@ void dissolve_on_fput(struct vfsmount *mnt)
24012442
free_mnt_ns(ns);
24022443
}
24032444

2404-
void drop_collected_mounts(struct vfsmount *mnt)
2405-
{
2406-
namespace_lock();
2407-
lock_mount_hash();
2408-
umount_tree(real_mount(mnt), 0);
2409-
unlock_mount_hash();
2410-
namespace_unlock();
2411-
}
2412-
24132445
static bool __has_locked_children(struct mount *mnt, struct dentry *dentry)
24142446
{
24152447
struct mount *child;
@@ -2511,21 +2543,6 @@ struct vfsmount *clone_private_mount(const struct path *path)
25112543
}
25122544
EXPORT_SYMBOL_GPL(clone_private_mount);
25132545

2514-
int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
2515-
struct vfsmount *root)
2516-
{
2517-
struct mount *mnt;
2518-
int res = f(root, arg);
2519-
if (res)
2520-
return res;
2521-
list_for_each_entry(mnt, &real_mount(root)->mnt_list, mnt_list) {
2522-
res = f(&mnt->mnt, arg);
2523-
if (res)
2524-
return res;
2525-
}
2526-
return 0;
2527-
}
2528-
25292546
static void lock_mnt_tree(struct mount *mnt)
25302547
{
25312548
struct mount *p;
@@ -6262,7 +6279,11 @@ void put_mnt_ns(struct mnt_namespace *ns)
62626279
{
62636280
if (!refcount_dec_and_test(&ns->ns.count))
62646281
return;
6265-
drop_collected_mounts(&ns->root->mnt);
6282+
namespace_lock();
6283+
lock_mount_hash();
6284+
umount_tree(ns->root, 0);
6285+
unlock_mount_hash();
6286+
namespace_unlock();
62666287
free_mnt_ns(ns);
62676288
}
62686289

fs/pnode.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
#define CL_SHARED_TO_SLAVE 0x20
2929
#define CL_COPY_MNT_NS_FILE 0x40
3030

31-
#define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE)
32-
3331
static inline void set_mnt_shared(struct mount *mnt)
3432
{
3533
mnt->mnt.mnt_flags &= ~MNT_SHARED_MASK;

include/linux/mount.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,8 @@ extern int may_umount_tree(struct vfsmount *);
116116
extern int may_umount(struct vfsmount *);
117117
int do_mount(const char *, const char __user *,
118118
const char *, unsigned long, void *);
119-
extern struct vfsmount *collect_mounts(const struct path *);
120-
extern void drop_collected_mounts(struct vfsmount *);
121-
extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
122-
struct vfsmount *);
119+
extern struct path *collect_paths(const struct path *, struct path *, unsigned);
120+
extern void drop_collected_paths(struct path *, struct path *);
123121
extern void kern_unmount_array(struct vfsmount *mnt[], unsigned int num);
124122

125123
extern int cifs_root_data(char **dev, char **opts);

kernel/audit_tree.c

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -668,12 +668,6 @@ int audit_remove_tree_rule(struct audit_krule *rule)
668668
return 0;
669669
}
670670

671-
static int compare_root(struct vfsmount *mnt, void *arg)
672-
{
673-
return inode_to_key(d_backing_inode(mnt->mnt_root)) ==
674-
(unsigned long)arg;
675-
}
676-
677671
void audit_trim_trees(void)
678672
{
679673
struct list_head cursor;
@@ -683,8 +677,9 @@ void audit_trim_trees(void)
683677
while (cursor.next != &tree_list) {
684678
struct audit_tree *tree;
685679
struct path path;
686-
struct vfsmount *root_mnt;
687680
struct audit_node *node;
681+
struct path *paths;
682+
struct path array[16];
688683
int err;
689684

690685
tree = container_of(cursor.next, struct audit_tree, list);
@@ -696,24 +691,27 @@ void audit_trim_trees(void)
696691
if (err)
697692
goto skip_it;
698693

699-
root_mnt = collect_mounts(&path);
694+
paths = collect_paths(&path, array, 16);
700695
path_put(&path);
701-
if (IS_ERR(root_mnt))
696+
if (IS_ERR(paths))
702697
goto skip_it;
703698

704699
spin_lock(&hash_lock);
705700
list_for_each_entry(node, &tree->chunks, list) {
706701
struct audit_chunk *chunk = find_chunk(node);
707702
/* this could be NULL if the watch is dying else where... */
708703
node->index |= 1U<<31;
709-
if (iterate_mounts(compare_root,
710-
(void *)(chunk->key),
711-
root_mnt))
712-
node->index &= ~(1U<<31);
704+
for (struct path *p = paths; p->dentry; p++) {
705+
struct inode *inode = p->dentry->d_inode;
706+
if (inode_to_key(inode) == chunk->key) {
707+
node->index &= ~(1U<<31);
708+
break;
709+
}
710+
}
713711
}
714712
spin_unlock(&hash_lock);
715713
trim_marked(tree);
716-
drop_collected_mounts(root_mnt);
714+
drop_collected_paths(paths, array);
717715
skip_it:
718716
put_tree(tree);
719717
mutex_lock(&audit_filter_mutex);
@@ -742,9 +740,14 @@ void audit_put_tree(struct audit_tree *tree)
742740
put_tree(tree);
743741
}
744742

745-
static int tag_mount(struct vfsmount *mnt, void *arg)
743+
static int tag_mounts(struct path *paths, struct audit_tree *tree)
746744
{
747-
return tag_chunk(d_backing_inode(mnt->mnt_root), arg);
745+
for (struct path *p = paths; p->dentry; p++) {
746+
int err = tag_chunk(p->dentry->d_inode, tree);
747+
if (err)
748+
return err;
749+
}
750+
return 0;
748751
}
749752

750753
/*
@@ -801,7 +804,8 @@ int audit_add_tree_rule(struct audit_krule *rule)
801804
{
802805
struct audit_tree *seed = rule->tree, *tree;
803806
struct path path;
804-
struct vfsmount *mnt;
807+
struct path array[16];
808+
struct path *paths;
805809
int err;
806810

807811
rule->tree = NULL;
@@ -828,16 +832,16 @@ int audit_add_tree_rule(struct audit_krule *rule)
828832
err = kern_path(tree->pathname, 0, &path);
829833
if (err)
830834
goto Err;
831-
mnt = collect_mounts(&path);
835+
paths = collect_paths(&path, array, 16);
832836
path_put(&path);
833-
if (IS_ERR(mnt)) {
834-
err = PTR_ERR(mnt);
837+
if (IS_ERR(paths)) {
838+
err = PTR_ERR(paths);
835839
goto Err;
836840
}
837841

838842
get_tree(tree);
839-
err = iterate_mounts(tag_mount, tree, mnt);
840-
drop_collected_mounts(mnt);
843+
err = tag_mounts(paths, tree);
844+
drop_collected_paths(paths, array);
841845

842846
if (!err) {
843847
struct audit_node *node;
@@ -872,20 +876,21 @@ int audit_tag_tree(char *old, char *new)
872876
struct list_head cursor, barrier;
873877
int failed = 0;
874878
struct path path1, path2;
875-
struct vfsmount *tagged;
879+
struct path array[16];
880+
struct path *paths;
876881
int err;
877882

878883
err = kern_path(new, 0, &path2);
879884
if (err)
880885
return err;
881-
tagged = collect_mounts(&path2);
886+
paths = collect_paths(&path2, array, 16);
882887
path_put(&path2);
883-
if (IS_ERR(tagged))
884-
return PTR_ERR(tagged);
888+
if (IS_ERR(paths))
889+
return PTR_ERR(paths);
885890

886891
err = kern_path(old, 0, &path1);
887892
if (err) {
888-
drop_collected_mounts(tagged);
893+
drop_collected_paths(paths, array);
889894
return err;
890895
}
891896

@@ -914,7 +919,7 @@ int audit_tag_tree(char *old, char *new)
914919
continue;
915920
}
916921

917-
failed = iterate_mounts(tag_mount, tree, tagged);
922+
failed = tag_mounts(paths, tree);
918923
if (failed) {
919924
put_tree(tree);
920925
mutex_lock(&audit_filter_mutex);
@@ -955,7 +960,7 @@ int audit_tag_tree(char *old, char *new)
955960
list_del(&cursor);
956961
mutex_unlock(&audit_filter_mutex);
957962
path_put(&path1);
958-
drop_collected_mounts(tagged);
963+
drop_collected_paths(paths, array);
959964
return failed;
960965
}
961966

0 commit comments

Comments
 (0)