Commit a535116d authored by Amir Goldstein's avatar Amir Goldstein

ovl: make use of ->layers safe in rcu pathwalk

ovl_permission() accesses ->layers[...].mnt; we can't have ->layers
freed without an RCU delay on fs shutdown.

Fortunately, kern_unmount_array() that is used to drop those mounts
does include an RCU delay, so freeing is delayed; unfortunately, the
array passed to kern_unmount_array() is formed by mangling ->layers
contents and that happens without any delays.

The ->layers[...].name string entries are used to store the strings to
display in "lowerdir=..." by ovl_show_options().  Those entries are not
accessed in RCU walk.

Move the name strings into a separate array ofs->config.lowerdirs and
reuse the ofs->config.lowerdirs array as the temporary mount array to
pass to kern_unmount_array().
Reported-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Link: https://lore.kernel.org/r/20231002023711.GP3389589@ZenIV/Acked-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
parent c54719c9
......@@ -8,6 +8,7 @@
struct ovl_config {
char *upperdir;
char *workdir;
char **lowerdirs;
bool default_permissions;
int redirect_mode;
int verity_mode;
......@@ -39,17 +40,8 @@ struct ovl_layer {
int idx;
/* One fsid per unique underlying sb (upper fsid == 0) */
int fsid;
char *name;
};
/*
* ovl_free_fs() relies on @mnt being the first member when unmounting
* the private mounts created for each layer. Let's check both the
* offset and type.
*/
static_assert(offsetof(struct ovl_layer, mnt) == 0);
static_assert(__same_type(typeof_member(struct ovl_layer, mnt), struct vfsmount *));
struct ovl_path {
const struct ovl_layer *layer;
struct dentry *dentry;
......
......@@ -752,12 +752,12 @@ void ovl_free_fs(struct ovl_fs *ofs)
if (ofs->upperdir_locked)
ovl_inuse_unlock(ovl_upper_mnt(ofs)->mnt_root);
/* Hack! Reuse ofs->layers as a vfsmount array before freeing it */
mounts = (struct vfsmount **) ofs->layers;
/* Reuse ofs->config.lowerdirs as a vfsmount array before freeing it */
mounts = (struct vfsmount **) ofs->config.lowerdirs;
for (i = 0; i < ofs->numlayer; i++) {
iput(ofs->layers[i].trap);
kfree(ofs->config.lowerdirs[i]);
mounts[i] = ofs->layers[i].mnt;
kfree(ofs->layers[i].name);
}
kern_unmount_array(mounts, ofs->numlayer);
kfree(ofs->layers);
......@@ -765,6 +765,7 @@ void ovl_free_fs(struct ovl_fs *ofs)
free_anon_bdev(ofs->fs[i].pseudo_dev);
kfree(ofs->fs);
kfree(ofs->config.lowerdirs);
kfree(ofs->config.upperdir);
kfree(ofs->config.workdir);
if (ofs->creator_cred)
......@@ -949,16 +950,16 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
struct super_block *sb = dentry->d_sb;
struct ovl_fs *ofs = OVL_FS(sb);
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower];
char **lowerdatadirs = &ofs->config.lowerdirs[nr_merged_lower];
/* ofs->layers[0] is the upper layer */
seq_printf(m, ",lowerdir=%s", ofs->layers[1].name);
/* lowerdirs[] starts from offset 1 */
seq_printf(m, ",lowerdir=%s", ofs->config.lowerdirs[1]);
/* dump regular lower layers */
for (nr = 2; nr < nr_merged_lower; nr++)
seq_printf(m, ":%s", ofs->layers[nr].name);
seq_printf(m, ":%s", ofs->config.lowerdirs[nr]);
/* dump data lower layers */
for (nr = 0; nr < ofs->numdatalayer; nr++)
seq_printf(m, "::%s", data_layers[nr].name);
seq_printf(m, "::%s", lowerdatadirs[nr]);
if (ofs->config.upperdir) {
seq_show_option(m, "upperdir", ofs->config.upperdir);
seq_show_option(m, "workdir", ofs->config.workdir);
......
......@@ -572,11 +572,6 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
upper_layer->idx = 0;
upper_layer->fsid = 0;
err = -ENOMEM;
upper_layer->name = kstrdup(ofs->config.upperdir, GFP_KERNEL);
if (!upper_layer->name)
goto out;
/*
* Inherit SB_NOSEC flag from upperdir.
*
......@@ -1125,7 +1120,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
layers[ofs->numlayer].idx = ofs->numlayer;
layers[ofs->numlayer].fsid = fsid;
layers[ofs->numlayer].fs = &ofs->fs[fsid];
layers[ofs->numlayer].name = l->name;
/* Store for printing lowerdir=... in ovl_show_options() */
ofs->config.lowerdirs[ofs->numlayer] = l->name;
l->name = NULL;
ofs->numlayer++;
ofs->fs[fsid].is_lower = true;
......@@ -1370,8 +1366,16 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
if (!layers)
goto out_err;
ofs->config.lowerdirs = kcalloc(ctx->nr + 1, sizeof(char *), GFP_KERNEL);
if (!ofs->config.lowerdirs) {
kfree(layers);
goto out_err;
}
ofs->layers = layers;
/* Layer 0 is reserved for upper even if there's no upper */
/*
* Layer 0 is reserved for upper even if there's no upper.
* For consistency, config.lowerdirs[0] is NULL.
*/
ofs->numlayer = 1;
sb->s_stack_depth = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment