ovl: honor flag MS_SILENT at mount
Subsystems (7)
PATCH 1/4 ovl: honor flag MS_SILENT at mount
This patch hides error about missing lowerdir if MS_SILENT is set.
We use mount(NULL, "/", "overlay", MS_SILENT, NULL) for testing support of
overlayfs: syscall returns -ENODEV if it's not supported. Otherwise kernel automatically loads module and returns -EINVAL because lowerdir is missing.
--- fs/overlayfs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/overlayfs/super.c +2 -1
@@ -840,7 +840,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) err = -EINVAL ;if (!ufs->config.lowerdir) { -pr_err ("overlayfs: missing 'lowerdir'\n" ); +if (!silent) +pr_err ("overlayfs: missing 'lowerdir'\n" ); goto out_free_config; }
PATCH 2/4 ovl: honor MS_NOEXEC flag at overlayfs mount
Overlayfs opens files directly at underlying filesystem. File->f_path.mnt points to private clone of lower/upper mount. Thus MS_NOEXEC at overlayfs mount has no effect because all checks are done against underlying mount.
This patch clears "noexec" at private lower/upper mounts (if noexec isn't locked) and checks MNT_NOEXEC in overlayfs mount flags in ->dentry_open().
MS_NODEV already works correctly because kernel checks it before opening.
--- Documentation/filesystems/Locking | 2 +- Documentation/filesystems/vfs.txt | 2 +- fs/open.c | 2 +- fs/overlayfs/inode.c | 14 ++++++++++++-- fs/overlayfs/super.c | 14 ++++++++++++++ include/linux/fs.h | 3 ++- 6 files changed, 31 insertions(+), 6 deletions(-)
Documentation/filesystems/Locking +1 -1
@@ -67,7 +67,7 @@ prototypes: struct file *, unsigned open_flag, umode_t create_mode, int *opened); int (*tmpfile) (struct inode *, struct dentry *, umode_t); - int (*dentry_open)(struct dentry *, struct file *, const struct cred *); + int (*dentry_open)(struct path *, struct file *, const struct cred *); locking rules: all may block
Documentation/filesystems/vfs.txt +1 -1
@@ -364,7 +364,7 @@ struct inode_operations { int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode, int *opened); int (*tmpfile) (struct inode *, struct dentry *, umode_t); - int (*dentry_open)(struct dentry *, struct file *, const struct cred *); + int (*dentry_open)(struct path *, struct file *, const struct cred *); }; Again, all methods are called without any locks being held, unless
fs/open.c +1 -1
@@ -865,7 +865,7 @@ int vfs_open(const struct path *path, struct file *filp,struct inode *inode = path->dentry->d_inode;if (inode->i_op->dentry_open) -return inode->i_op->dentry_open (path->dentry, filp, cred); +return inode->i_op->dentry_open (path, filp, cred);else { filp->f_path = *path;return do_dentry_open (filp,NULL , cred);
fs/overlayfs/inode.c +12 -2
@@ -10,6 +10,7 @@#include <linux/fs.h> #include <linux/slab.h> #include <linux/xattr.h> +#include <linux/mount.h> #include "overlayfs.h" static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr,
@@ -337,14 +338,23 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,return true; } -static int ovl_dentry_open (struct dentry *dentry,struct file *file, -const struct cred *cred) +static int ovl_dentry_open (const struct path *path,struct file *file, +const struct cred *cred) { +struct dentry *dentry = path->dentry;int err;struct path realpath;enum ovl_path_type type;bool want_write = false; +/* +* After opening file->f_path.mnt points to private lower/upper mount. +* Here is the last chance to check flags at overlayfs mount. +*/ +if ((file->f_flags & __FMODE_EXEC) && + (path->mnt->mnt_flags &MNT_NOEXEC )) +return -EACCES ; + type =ovl_path_real (dentry, &realpath);if (ovl_open_need_copy_up (file->f_flags, type, realpath.dentry)) { want_write = true;
fs/overlayfs/super.c +14 -0
@@ -923,6 +923,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_put_lowerpath; } +/* +* Drop noexec at upper mount if allowed. We'll check +* MNT_NOEXEC at overlayfs mount in ovl_dentry_open(). +*/ +if (!(ufs->upper_mnt->mnt_flags &MNT_LOCK_NOEXEC )) + ufs->upper_mnt->mnt_flags &= ~MNT_NOEXEC ; + ufs->workdir =ovl_workdir_create (ufs->upper_mnt, workpath.dentry); err =PTR_ERR (ufs->workdir);if (IS_ERR (ufs->workdir)) {
@@ -951,6 +958,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)*/ mnt->mnt_flags |=MNT_READONLY ; +/* +* Drop noexec at lower mount if allowed. We'll check +* MNT_NOEXEC at overlayfs mount in ovl_dentry_open(). +*/ +if (!(mnt->mnt_flags &MNT_LOCK_NOEXEC )) + mnt->mnt_flags &= ~MNT_NOEXEC ; + ufs->lower_mnt[ufs->numlower] = mnt; ufs->numlower++; }
include/linux/fs.h +2 -1
@@ -1653,7 +1653,8 @@ struct inode_operations {int (*set_acl)(struct inode *,struct posix_acl *,int );/* WARNING: probably going away soon, do not use! */ -int (*dentry_open)(struct dentry *,struct file *,const struct cred *); +int (*dentry_open)(const struct path *,struct file *, +const struct cred *); }____cacheline_aligned ; ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
PATCH 3/4 ovl: honor MS_NOSUID flag at overlayfs mount
In reply to Konstantin KhlebnikovMount flag MS_NOSUID in overlayfs has the same problem as MS_NOEXEC:
overlayfs opens files at underlying filesystem thus kernel check flags at private clone of lower/upper mountpoint instead of overlayfs itself.
This patch saves MNT_NOSUID from overlayfs mount in file->f_mode when opens file and checks it together with MNT_NOSUID. This behavior stricter than usual: opened file stays nosuid even after remounting overlayfs without MS_NOSUID but that should be fine.
--- fs/exec.c | 3 ++- fs/overlayfs/inode.c | 4 ++++ fs/overlayfs/super.c | 6 ++++++ include/linux/fs.h | 3 +++ security/commoncap.c | 3 ++- security/selinux/hooks.c | 3 ++- 6 files changed, 19 insertions(+), 3 deletions(-)
fs/exec.c +2 -1
@@ -1289,7 +1289,8 @@ static void bprm_fill_uid(struct linux_binprm *bprm) bprm->cred->euid =current_euid (); bprm->cred->egid =current_egid (); -if (bprm->file->f_path.mnt->mnt_flags &MNT_NOSUID ) +if ((bprm->file->f_mode &FMODE_NOSUID ) || + (bprm->file->f_path.mnt->mnt_flags &MNT_NOSUID ))return ;if (task_no_new_privs (current))
fs/overlayfs/inode.c +4 -0
@@ -373,6 +373,10 @@ static int ovl_dentry_open(const struct path *path, struct file *file, } err =vfs_open (&realpath, file, cred); + +if (path->mnt->mnt_flags &MNT_NOSUID ) + file->f_mode |=FMODE_NOSUID ; + out_drop_write:if (want_write)ovl_drop_write (dentry);
fs/overlayfs/super.c +6 -0
@@ -930,6 +930,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)if (!(ufs->upper_mnt->mnt_flags &MNT_LOCK_NOEXEC )) ufs->upper_mnt->mnt_flags &= ~MNT_NOEXEC ; +if (!(ufs->upper_mnt->mnt_flags &MNT_LOCK_NOSUID )) + ufs->upper_mnt->mnt_flags &= ~MNT_NOSUID ; + ufs->workdir =ovl_workdir_create (ufs->upper_mnt, workpath.dentry); err =PTR_ERR (ufs->workdir);if (IS_ERR (ufs->workdir)) {
@@ -965,6 +968,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)if (!(mnt->mnt_flags &MNT_LOCK_NOEXEC )) mnt->mnt_flags &= ~MNT_NOEXEC ; +if (!(mnt->mnt_flags &MNT_LOCK_NOSUID )) + mnt->mnt_flags &= ~MNT_NOSUID ; + ufs->lower_mnt[ufs->numlower] = mnt; ufs->numlower++; }
include/linux/fs.h +3 -0
@@ -136,6 +136,9 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,/* Has write method(s) */ #define FMODE_CAN_WRITE ((__force fmode_t)0x40000) +/* Ignode S_ISUID and S_ISGID bits when executing this file */ +#define FMODE_NOSUID ((__force fmode_t)0x80000) +/* File was opened by fanotify and shouldn't generate fanotify events */ #define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
security/commoncap.c +2 -1
@@ -437,7 +437,8 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cif (!file_caps_enabled)return 0 ; -if (bprm->file->f_path.mnt->mnt_flags &MNT_NOSUID ) +if ((bprm->file->f_mode &FMODE_NOSUID ) || + (bprm->file->f_path.mnt->mnt_flags &MNT_NOSUID ))return 0 ; rc =get_vfs_caps_from_disk (bprm->file->f_path.dentry, &vcaps);
security/selinux/hooks.c +2 -1
@@ -2137,7 +2137,8 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, const struct task_security_struct *new_tsec) {int nnp = (bprm->unsafe &LSM_UNSAFE_NO_NEW_PRIVS ); -int nosuid = (bprm->file->f_path.mnt->mnt_flags &MNT_NOSUID ); +int nosuid = (bprm->file->f_mode &FMODE_NOSUID ) || + (bprm->file->f_path.mnt->mnt_flags &MNT_NOSUID );int rc;if (!nnp && !nosuid)
PATCH 4/4 ovl: forbid overlayfs on top of overlayfs
In reply to Konstantin KhlebnikovMounting overlayfs using other overlayfs instance as lower or upper layer triggers false-positive lockdep warning about possible locking recursion of ->i_mutex in iterate_dir().
Nesting already limited with FILESYSTEM_MAX_STACK_DEPTH = 2 to prevent kernel stack overflow. Overlayfs supports multiple lower layers thus the same configuration could be constructed without nesting.
--- fs/overlayfs/super.c | 3 +++ 1 file changed, 3 insertions(+)
fs/overlayfs/super.c +3 -0
@@ -700,10 +700,13 @@ static bool ovl_is_allowed_fs_type(struct dentry *root)/* * We don't support: +* - overlayfs * - automount filesystems * - filesystems with revalidate (FIXME for lower layer) * - filesystems with case insensitive names */ +if (root->d_sb->s_op == &ovl_super_operations) +return false; if (dop && (dop->d_manage || dop->d_automount || dop->d_revalidate || dop->d_weak_revalidate ||
Miklos Szeredi
maintainerQuoted 5 lines · Konstantin Khlebnikov
This patch hides error about missing lowerdir if
MS_SILENTis set.We use
mount(NULL, "/", "overlay", MS_SILENT, NULL)for testing support of
overlayfs: syscall returns-ENODEVif it's not supported. Otherwise kernel automatically loads module and returns-EINVALbecause lowerdir is missing.
Queued.
Thanks,
Miklos