Commit b6c0d357 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-fixes-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs fixes from Miklos Szeredi:
 "Fix a regression in docker introduced by overlayfs changes in 4.19.
  Also fix a couple of miscellaneous bugs"

* tag 'ovl-fixes-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: filter of trusted xattr results in audit
  ovl: Fix dereferencing possible ERR_PTR()
  ovl: fix regression caused by overlapping layers detection
parents 7d14df2d 5c2e9f34
...@@ -302,7 +302,7 @@ beneath or above the path of another overlay lower layer path. ...@@ -302,7 +302,7 @@ beneath or above the path of another overlay lower layer path.
Using an upper layer path and/or a workdir path that are already used by Using an upper layer path and/or a workdir path that are already used by
another overlay mount is not allowed and may fail with EBUSY. Using another overlay mount is not allowed and may fail with EBUSY. Using
partially overlapping paths is not allowed but will not fail with EBUSY. partially overlapping paths is not allowed and may fail with EBUSY.
If files are accessed from two overlayfs mounts which share or overlap the If files are accessed from two overlayfs mounts which share or overlap the
upper layer and/or workdir path the behavior of the overlay is undefined, upper layer and/or workdir path the behavior of the overlay is undefined,
though it will not result in a crash or deadlock. though it will not result in a crash or deadlock.
......
...@@ -227,9 +227,8 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) ...@@ -227,9 +227,8 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
/* Encode an upper or lower file handle */ /* Encode an upper or lower file handle */
fh = ovl_encode_real_fh(enc_lower ? ovl_dentry_lower(dentry) : fh = ovl_encode_real_fh(enc_lower ? ovl_dentry_lower(dentry) :
ovl_dentry_upper(dentry), !enc_lower); ovl_dentry_upper(dentry), !enc_lower);
err = PTR_ERR(fh);
if (IS_ERR(fh)) if (IS_ERR(fh))
goto fail; return PTR_ERR(fh);
err = -EOVERFLOW; err = -EOVERFLOW;
if (fh->len > buflen) if (fh->len > buflen)
......
...@@ -383,7 +383,8 @@ static bool ovl_can_list(const char *s) ...@@ -383,7 +383,8 @@ static bool ovl_can_list(const char *s)
return true; return true;
/* Never list trusted.overlay, list other trusted for superuser only */ /* Never list trusted.overlay, list other trusted for superuser only */
return !ovl_is_private_xattr(s) && capable(CAP_SYS_ADMIN); return !ovl_is_private_xattr(s) &&
ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
} }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
......
...@@ -66,6 +66,7 @@ struct ovl_fs { ...@@ -66,6 +66,7 @@ struct ovl_fs {
bool workdir_locked; bool workdir_locked;
/* Traps in ovl inode cache */ /* Traps in ovl inode cache */
struct inode *upperdir_trap; struct inode *upperdir_trap;
struct inode *workbasedir_trap;
struct inode *workdir_trap; struct inode *workdir_trap;
struct inode *indexdir_trap; struct inode *indexdir_trap;
/* Inode numbers in all layers do not use the high xino_bits */ /* Inode numbers in all layers do not use the high xino_bits */
......
...@@ -212,6 +212,7 @@ static void ovl_free_fs(struct ovl_fs *ofs) ...@@ -212,6 +212,7 @@ static void ovl_free_fs(struct ovl_fs *ofs)
{ {
unsigned i; unsigned i;
iput(ofs->workbasedir_trap);
iput(ofs->indexdir_trap); iput(ofs->indexdir_trap);
iput(ofs->workdir_trap); iput(ofs->workdir_trap);
iput(ofs->upperdir_trap); iput(ofs->upperdir_trap);
...@@ -1003,6 +1004,25 @@ static int ovl_setup_trap(struct super_block *sb, struct dentry *dir, ...@@ -1003,6 +1004,25 @@ static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
return 0; return 0;
} }
/*
* Determine how we treat concurrent use of upperdir/workdir based on the
* index feature. This is papering over mount leaks of container runtimes,
* for example, an old overlay mount is leaked and now its upperdir is
* attempted to be used as a lower layer in a new overlay mount.
*/
static int ovl_report_in_use(struct ovl_fs *ofs, const char *name)
{
if (ofs->config.index) {
pr_err("overlayfs: %s is in-use as upperdir/workdir of another mount, mount with '-o index=off' to override exclusive upperdir protection.\n",
name);
return -EBUSY;
} else {
pr_warn("overlayfs: %s is in-use as upperdir/workdir of another mount, accessing files from both mounts will result in undefined behavior.\n",
name);
return 0;
}
}
static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
struct path *upperpath) struct path *upperpath)
{ {
...@@ -1040,14 +1060,12 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1040,14 +1060,12 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
ofs->upper_mnt = upper_mnt; ofs->upper_mnt = upper_mnt;
err = -EBUSY;
if (ovl_inuse_trylock(ofs->upper_mnt->mnt_root)) { if (ovl_inuse_trylock(ofs->upper_mnt->mnt_root)) {
ofs->upperdir_locked = true; ofs->upperdir_locked = true;
} else if (ofs->config.index) {
pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n");
goto out;
} else { } else {
pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); err = ovl_report_in_use(ofs, "upperdir");
if (err)
goto out;
} }
err = 0; err = 0;
...@@ -1157,16 +1175,19 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1157,16 +1175,19 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs,
ofs->workbasedir = dget(workpath.dentry); ofs->workbasedir = dget(workpath.dentry);
err = -EBUSY;
if (ovl_inuse_trylock(ofs->workbasedir)) { if (ovl_inuse_trylock(ofs->workbasedir)) {
ofs->workdir_locked = true; ofs->workdir_locked = true;
} else if (ofs->config.index) {
pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
goto out;
} else { } else {
pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); err = ovl_report_in_use(ofs, "workdir");
if (err)
goto out;
} }
err = ovl_setup_trap(sb, ofs->workbasedir, &ofs->workbasedir_trap,
"workdir");
if (err)
goto out;
err = ovl_make_workdir(sb, ofs, &workpath); err = ovl_make_workdir(sb, ofs, &workpath);
out: out:
...@@ -1313,15 +1334,15 @@ static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1313,15 +1334,15 @@ static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs,
if (err < 0) if (err < 0)
goto out; goto out;
err = -EBUSY; err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir");
if (ovl_is_inuse(stack[i].dentry)) { if (err)
pr_err("overlayfs: lowerdir is in-use as upperdir/workdir\n");
goto out; goto out;
}
err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir"); if (ovl_is_inuse(stack[i].dentry)) {
err = ovl_report_in_use(ofs, "lowerdir");
if (err) if (err)
goto out; goto out;
}
mnt = clone_private_mount(&stack[i]); mnt = clone_private_mount(&stack[i]);
err = PTR_ERR(mnt); err = PTR_ERR(mnt);
...@@ -1469,8 +1490,8 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ...@@ -1469,8 +1490,8 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
* - another layer of this overlayfs instance * - another layer of this overlayfs instance
* - upper/work dir of any overlayfs instance * - upper/work dir of any overlayfs instance
*/ */
static int ovl_check_layer(struct super_block *sb, struct dentry *dentry, static int ovl_check_layer(struct super_block *sb, struct ovl_fs *ofs,
const char *name) struct dentry *dentry, const char *name)
{ {
struct dentry *next = dentry, *parent; struct dentry *next = dentry, *parent;
int err = 0; int err = 0;
...@@ -1482,13 +1503,11 @@ static int ovl_check_layer(struct super_block *sb, struct dentry *dentry, ...@@ -1482,13 +1503,11 @@ static int ovl_check_layer(struct super_block *sb, struct dentry *dentry,
/* Walk back ancestors to root (inclusive) looking for traps */ /* Walk back ancestors to root (inclusive) looking for traps */
while (!err && parent != next) { while (!err && parent != next) {
if (ovl_is_inuse(parent)) { if (ovl_lookup_trap_inode(sb, parent)) {
err = -EBUSY;
pr_err("overlayfs: %s path overlapping in-use upperdir/workdir\n",
name);
} else if (ovl_lookup_trap_inode(sb, parent)) {
err = -ELOOP; err = -ELOOP;
pr_err("overlayfs: overlapping %s path\n", name); pr_err("overlayfs: overlapping %s path\n", name);
} else if (ovl_is_inuse(parent)) {
err = ovl_report_in_use(ofs, name);
} }
next = parent; next = parent;
parent = dget_parent(next); parent = dget_parent(next);
...@@ -1509,7 +1528,8 @@ static int ovl_check_overlapping_layers(struct super_block *sb, ...@@ -1509,7 +1528,8 @@ static int ovl_check_overlapping_layers(struct super_block *sb,
int i, err; int i, err;
if (ofs->upper_mnt) { if (ofs->upper_mnt) {
err = ovl_check_layer(sb, ofs->upper_mnt->mnt_root, "upperdir"); err = ovl_check_layer(sb, ofs, ofs->upper_mnt->mnt_root,
"upperdir");
if (err) if (err)
return err; return err;
...@@ -1520,13 +1540,14 @@ static int ovl_check_overlapping_layers(struct super_block *sb, ...@@ -1520,13 +1540,14 @@ static int ovl_check_overlapping_layers(struct super_block *sb,
* workbasedir. In that case, we already have their traps in * workbasedir. In that case, we already have their traps in
* inode cache and we will catch that case on lookup. * inode cache and we will catch that case on lookup.
*/ */
err = ovl_check_layer(sb, ofs->workbasedir, "workdir"); err = ovl_check_layer(sb, ofs, ofs->workbasedir, "workdir");
if (err) if (err)
return err; return err;
} }
for (i = 0; i < ofs->numlower; i++) { for (i = 0; i < ofs->numlower; i++) {
err = ovl_check_layer(sb, ofs->lower_layers[i].mnt->mnt_root, err = ovl_check_layer(sb, ofs,
ofs->lower_layers[i].mnt->mnt_root,
"lowerdir"); "lowerdir");
if (err) if (err)
return err; return err;
......
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