From 691915fd8d4bb2c0f944b7d8e504a323b8e53b69 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 14 Jun 2020 13:51:30 +0300 Subject: [PATCH 2/2] ovl: warn about unsupported file operations on upper fs syzbot has reported a crash in a test case where overlayfs uses hugetlbfs as upper fs. Since hugetlbfs has no write() nor write_iter() file ops, there seems to be little value in supporting this configuration, however, we do not want to regress strange use cases that me be using such configuration. As a minimum, check for this case and warn about it on mount time as well as adding this check for the strict requirements set of remote upper fs. Signed-off-by: Amir Goldstein --- fs/overlayfs/super.c | 60 ++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 91476bc422f9..5bee70eb8f64 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -1131,42 +1132,61 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, } /* - * Returns 1 if RENAME_WHITEOUT is supported, 0 if not supported and + * Returns 1 if required ops are supported, 0 if not supported and * negative values if error is encountered. */ -static int ovl_check_rename_whiteout(struct dentry *workdir) +static int ovl_check_supported_ops(struct ovl_fs *ofs) { - struct inode *dir = d_inode(workdir); - struct dentry *temp; + struct inode *dir = d_inode(ofs->workdir); + struct dentry *temp = NULL; struct dentry *dest; struct dentry *whiteout; struct name_snapshot name; + struct path path; + struct file *file; + unsigned int unsupported; int err; inode_lock_nested(dir, I_MUTEX_PARENT); - temp = ovl_create_temp(workdir, OVL_CATTR(S_IFREG | 0)); - err = PTR_ERR(temp); - if (IS_ERR(temp)) + path.mnt = ovl_upper_mnt(ofs); + path.dentry = ovl_create_temp(ofs->workdir, OVL_CATTR(S_IFREG | 0)); + err = PTR_ERR(path.dentry); + if (IS_ERR(path.dentry)) goto out_unlock; - dest = ovl_lookup_temp(workdir); - err = PTR_ERR(dest); - if (IS_ERR(dest)) { - dput(temp); + temp = path.dentry; + file = dentry_open(&path, O_RDWR, current_cred()); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out_unlock; + + unsupported = OVL_UPPER_FMODE_MASK & ~file->f_mode; + fput(file); + if (unsupported) { + err = 0; + pr_warn("upper fs does not support required file operations (%x).\n", + unsupported); goto out_unlock; } + dest = ovl_lookup_temp(ofs->workdir); + err = PTR_ERR(dest); + if (IS_ERR(dest)) + goto out_unlock; + /* Name is inline and stable - using snapshot as a copy helper */ take_dentry_name_snapshot(&name, temp); err = ovl_do_rename(dir, temp, dir, dest, RENAME_WHITEOUT); if (err) { - if (err == -EINVAL) + if (err == -EINVAL) { err = 0; + pr_warn("upper fs does not support RENAME_WHITEOUT.\n"); + } goto cleanup_temp; } - whiteout = lookup_one_len(name.name.name, workdir, name.name.len); + whiteout = lookup_one_len(name.name.name, ofs->workdir, name.name.len); err = PTR_ERR(whiteout); if (IS_ERR(whiteout)) goto cleanup_temp; @@ -1181,10 +1201,10 @@ static int ovl_check_rename_whiteout(struct dentry *workdir) cleanup_temp: ovl_cleanup(dir, temp); release_dentry_name_snapshot(&name); - dput(temp); dput(dest); out_unlock: + dput(temp); inode_unlock(dir); return err; @@ -1195,7 +1215,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, { struct vfsmount *mnt = ovl_upper_mnt(ofs); struct dentry *temp; - bool rename_whiteout; + bool supported_ops; bool d_type; int fh_type; int err; @@ -1235,14 +1255,12 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, pr_warn("upper fs does not support tmpfile.\n"); - /* Check if upper/work fs supports RENAME_WHITEOUT */ - err = ovl_check_rename_whiteout(ofs->workdir); + /* Check if upper/work fs supports required ops */ + err = ovl_check_supported_ops(ofs); if (err < 0) goto out; - rename_whiteout = err; - if (!rename_whiteout) - pr_warn("upper fs does not support RENAME_WHITEOUT.\n"); + supported_ops = err; /* * Check if upper/work fs supports trusted.overlay.* xattr @@ -1264,7 +1282,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, * we can enforce strict requirements for remote upper fs. */ if (ovl_dentry_remote(ofs->workdir) && - (!d_type || !rename_whiteout || ofs->noxattr)) { + (!d_type || !supported_ops || ofs->noxattr)) { pr_err("upper fs missing required features.\n"); err = -EINVAL; goto out; -- 2.17.1