From: Sasha Levin Date: Sun, 23 Jun 2019 02:02:50 +0000 (-0400) Subject: fixes for 4.19 X-Git-Tag: v5.1.15~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=65418671bbf7747d3a3f040edb68a44b4f9b5e27;p=thirdparty%2Fkernel%2Fstable-queue.git fixes for 4.19 Signed-off-by: Sasha Levin --- diff --git a/queue-4.19/ovl-detect-overlapping-layers.patch b/queue-4.19/ovl-detect-overlapping-layers.patch new file mode 100644 index 00000000000..bbefa14a38f --- /dev/null +++ b/queue-4.19/ovl-detect-overlapping-layers.patch @@ -0,0 +1,573 @@ +From 0102ec399b0008a2bad0238c0bafd0ca06e55e0a Mon Sep 17 00:00:00 2001 +From: Amir Goldstein +Date: Thu, 18 Apr 2019 17:42:08 +0300 +Subject: ovl: detect overlapping layers + +[ Upstream commit 146d62e5a5867fbf84490d82455718bfb10fe824 ] + +Overlapping overlay layers are not supported and can cause unexpected +behavior, but overlayfs does not currently check or warn about these +configurations. + +User is not supposed to specify the same directory for upper and +lower dirs or for different lower layers and user is not supposed to +specify directories that are descendants of each other for overlay +layers, but that is exactly what this zysbot repro did: + + https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000 + +Moving layer root directories into other layers while overlayfs +is mounted could also result in unexpected behavior. + +This commit places "traps" in the overlay inode hash table. +Those traps are dummy overlay inodes that are hashed by the layers +root inodes. + +On mount, the hash table trap entries are used to verify that overlay +layers are not overlapping. While at it, we also verify that overlay +layers are not overlapping with directories "in-use" by other overlay +instances as upperdir/workdir. + +On lookup, the trap entries are used to verify that overlay layers +root inodes have not been moved into other layers after mount. + +Some examples: + +$ ./run --ov --samefs -s +... +( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt + mount -o bind base/lower lower + mount -o bind base/upper upper + mount -t overlay none mnt ... + -o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w) + +$ umount mnt +$ mount -t overlay none mnt ... + -o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w + + [ 94.434900] overlayfs: overlapping upperdir path + mount: mount overlay on mnt failed: Too many levels of symbolic links + +$ mount -t overlay none mnt ... + -o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w + + [ 151.350132] overlayfs: conflicting lowerdir path + mount: none is already mounted or mnt busy + +$ mount -t overlay none mnt ... + -o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w + + [ 201.205045] overlayfs: overlapping lowerdir path + mount: mount overlay on mnt failed: Too many levels of symbolic links + +$ mount -t overlay none mnt ... + -o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w +$ mv base/upper/0/ base/lower/ +$ find mnt/0 + mnt/0 + mnt/0/w + find: 'mnt/0/w/work': Too many levels of symbolic links + find: 'mnt/0/u': Too many levels of symbolic links + +Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com +Signed-off-by: Amir Goldstein +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + fs/overlayfs/inode.c | 48 +++++++++++ + fs/overlayfs/namei.c | 8 ++ + fs/overlayfs/overlayfs.h | 3 + + fs/overlayfs/ovl_entry.h | 6 ++ + fs/overlayfs/super.c | 169 +++++++++++++++++++++++++++++++++++---- + fs/overlayfs/util.c | 12 +++ + 6 files changed, 229 insertions(+), 17 deletions(-) + +diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c +index 373ccff9880c..f0389849fd80 100644 +--- a/fs/overlayfs/inode.c ++++ b/fs/overlayfs/inode.c +@@ -777,6 +777,54 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, + return inode; + } + ++bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir) ++{ ++ struct inode *key = d_inode(dir); ++ struct inode *trap; ++ bool res; ++ ++ trap = ilookup5(sb, (unsigned long) key, ovl_inode_test, key); ++ if (!trap) ++ return false; ++ ++ res = IS_DEADDIR(trap) && !ovl_inode_upper(trap) && ++ !ovl_inode_lower(trap); ++ ++ iput(trap); ++ return res; ++} ++ ++/* ++ * Create an inode cache entry for layer root dir, that will intentionally ++ * fail ovl_verify_inode(), so any lookup that will find some layer root ++ * will fail. ++ */ ++struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir) ++{ ++ struct inode *key = d_inode(dir); ++ struct inode *trap; ++ ++ if (!d_is_dir(dir)) ++ return ERR_PTR(-ENOTDIR); ++ ++ trap = iget5_locked(sb, (unsigned long) key, ovl_inode_test, ++ ovl_inode_set, key); ++ if (!trap) ++ return ERR_PTR(-ENOMEM); ++ ++ if (!(trap->i_state & I_NEW)) { ++ /* Conflicting layer roots? */ ++ iput(trap); ++ return ERR_PTR(-ELOOP); ++ } ++ ++ trap->i_mode = S_IFDIR; ++ trap->i_flags = S_DEAD; ++ unlock_new_inode(trap); ++ ++ return trap; ++} ++ + /* + * Does overlay inode need to be hashed by lower inode? + */ +diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c +index efd372312ef1..badf039267a2 100644 +--- a/fs/overlayfs/namei.c ++++ b/fs/overlayfs/namei.c +@@ -18,6 +18,7 @@ + #include "overlayfs.h" + + struct ovl_lookup_data { ++ struct super_block *sb; + struct qstr name; + bool is_dir; + bool opaque; +@@ -244,6 +245,12 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, + if (!d->metacopy || d->last) + goto out; + } else { ++ if (ovl_lookup_trap_inode(d->sb, this)) { ++ /* Caught in a trap of overlapping layers */ ++ err = -ELOOP; ++ goto out_err; ++ } ++ + if (last_element) + d->is_dir = true; + if (d->last) +@@ -819,6 +826,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, + int err; + bool metacopy = false; + struct ovl_lookup_data d = { ++ .sb = dentry->d_sb, + .name = dentry->d_name, + .is_dir = false, + .opaque = false, +diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h +index 80fb66426760..265bf9cfde08 100644 +--- a/fs/overlayfs/overlayfs.h ++++ b/fs/overlayfs/overlayfs.h +@@ -270,6 +270,7 @@ void ovl_clear_flag(unsigned long flag, struct inode *inode); + bool ovl_test_flag(unsigned long flag, struct inode *inode); + bool ovl_inuse_trylock(struct dentry *dentry); + void ovl_inuse_unlock(struct dentry *dentry); ++bool ovl_is_inuse(struct dentry *dentry); + bool ovl_need_index(struct dentry *dentry); + int ovl_nlink_start(struct dentry *dentry, bool *locked); + void ovl_nlink_end(struct dentry *dentry, bool locked); +@@ -366,6 +367,8 @@ struct ovl_inode_params { + struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); + struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, + bool is_upper); ++bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir); ++struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir); + struct inode *ovl_get_inode(struct super_block *sb, + struct ovl_inode_params *oip); + static inline void ovl_copyattr(struct inode *from, struct inode *to) +diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h +index ec237035333a..6ed1ace8f8b3 100644 +--- a/fs/overlayfs/ovl_entry.h ++++ b/fs/overlayfs/ovl_entry.h +@@ -29,6 +29,8 @@ struct ovl_sb { + + struct ovl_layer { + struct vfsmount *mnt; ++ /* Trap in ovl inode cache */ ++ struct inode *trap; + struct ovl_sb *fs; + /* Index of this layer in fs root (upper idx == 0) */ + int idx; +@@ -65,6 +67,10 @@ struct ovl_fs { + /* Did we take the inuse lock? */ + bool upperdir_locked; + bool workdir_locked; ++ /* Traps in ovl inode cache */ ++ struct inode *upperdir_trap; ++ struct inode *workdir_trap; ++ struct inode *indexdir_trap; + /* Inode numbers in all layers do not use the high xino_bits */ + unsigned int xino_bits; + }; +diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c +index 0fb0a59a5e5c..4e268f981b4d 100644 +--- a/fs/overlayfs/super.c ++++ b/fs/overlayfs/super.c +@@ -217,6 +217,9 @@ static void ovl_free_fs(struct ovl_fs *ofs) + { + unsigned i; + ++ iput(ofs->indexdir_trap); ++ iput(ofs->workdir_trap); ++ iput(ofs->upperdir_trap); + dput(ofs->indexdir); + dput(ofs->workdir); + if (ofs->workdir_locked) +@@ -225,8 +228,10 @@ static void ovl_free_fs(struct ovl_fs *ofs) + if (ofs->upperdir_locked) + ovl_inuse_unlock(ofs->upper_mnt->mnt_root); + mntput(ofs->upper_mnt); +- for (i = 0; i < ofs->numlower; i++) ++ for (i = 0; i < ofs->numlower; i++) { ++ iput(ofs->lower_layers[i].trap); + mntput(ofs->lower_layers[i].mnt); ++ } + for (i = 0; i < ofs->numlowerfs; i++) + free_anon_bdev(ofs->lower_fs[i].pseudo_dev); + kfree(ofs->lower_layers); +@@ -984,7 +989,26 @@ static const struct xattr_handler *ovl_xattr_handlers[] = { + NULL + }; + +-static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) ++static int ovl_setup_trap(struct super_block *sb, struct dentry *dir, ++ struct inode **ptrap, const char *name) ++{ ++ struct inode *trap; ++ int err; ++ ++ trap = ovl_get_trap_inode(sb, dir); ++ err = PTR_ERR(trap); ++ if (IS_ERR(trap)) { ++ if (err == -ELOOP) ++ pr_err("overlayfs: conflicting %s path\n", name); ++ return err; ++ } ++ ++ *ptrap = trap; ++ return 0; ++} ++ ++static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, ++ struct path *upperpath) + { + struct vfsmount *upper_mnt; + int err; +@@ -1004,6 +1028,11 @@ static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) + if (err) + goto out; + ++ err = ovl_setup_trap(sb, upperpath->dentry, &ofs->upperdir_trap, ++ "upperdir"); ++ if (err) ++ goto out; ++ + upper_mnt = clone_private_mount(upperpath); + err = PTR_ERR(upper_mnt); + if (IS_ERR(upper_mnt)) { +@@ -1030,7 +1059,8 @@ static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) + return err; + } + +-static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ++static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ++ struct path *workpath) + { + struct vfsmount *mnt = ofs->upper_mnt; + struct dentry *temp; +@@ -1045,6 +1075,10 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) + if (!ofs->workdir) + goto out; + ++ err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir"); ++ if (err) ++ goto out; ++ + /* + * Upper should support d_type, else whiteouts are visible. Given + * workdir and upper are on same fs, we can do iterate_dir() on +@@ -1105,7 +1139,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) + return err; + } + +-static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath) ++static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs, ++ struct path *upperpath) + { + int err; + struct path workpath = { }; +@@ -1136,19 +1171,16 @@ static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath) + pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); + } + +- err = ovl_make_workdir(ofs, &workpath); +- if (err) +- goto out; ++ err = ovl_make_workdir(sb, ofs, &workpath); + +- err = 0; + out: + path_put(&workpath); + + return err; + } + +-static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, +- struct path *upperpath) ++static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, ++ struct ovl_entry *oe, struct path *upperpath) + { + struct vfsmount *mnt = ofs->upper_mnt; + int err; +@@ -1167,6 +1199,11 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, + + ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true); + if (ofs->indexdir) { ++ err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap, ++ "indexdir"); ++ if (err) ++ goto out; ++ + /* + * Verify upper root is exclusively associated with index dir. + * Older kernels stored upper fh in "trusted.overlay.origin" +@@ -1226,8 +1263,8 @@ static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb) + return ofs->numlowerfs; + } + +-static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, +- unsigned int numlower) ++static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs, ++ struct path *stack, unsigned int numlower) + { + int err; + unsigned int i; +@@ -1245,16 +1282,28 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, + + for (i = 0; i < numlower; i++) { + struct vfsmount *mnt; ++ struct inode *trap; + int fsid; + + err = fsid = ovl_get_fsid(ofs, stack[i].mnt->mnt_sb); + if (err < 0) + goto out; + ++ err = -EBUSY; ++ if (ovl_is_inuse(stack[i].dentry)) { ++ pr_err("overlayfs: lowerdir is in-use as upperdir/workdir\n"); ++ goto out; ++ } ++ ++ err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir"); ++ if (err) ++ goto out; ++ + mnt = clone_private_mount(&stack[i]); + err = PTR_ERR(mnt); + if (IS_ERR(mnt)) { + pr_err("overlayfs: failed to clone lowerpath\n"); ++ iput(trap); + goto out; + } + +@@ -1264,6 +1313,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, + */ + mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; + ++ ofs->lower_layers[ofs->numlower].trap = trap; + ofs->lower_layers[ofs->numlower].mnt = mnt; + ofs->lower_layers[ofs->numlower].idx = i + 1; + ofs->lower_layers[ofs->numlower].fsid = fsid; +@@ -1358,7 +1408,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, + goto out_err; + } + +- err = ovl_get_lower_layers(ofs, stack, numlower); ++ err = ovl_get_lower_layers(sb, ofs, stack, numlower); + if (err) + goto out_err; + +@@ -1390,6 +1440,85 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, + goto out; + } + ++/* ++ * Check if this layer root is a descendant of: ++ * - another layer of this overlayfs instance ++ * - upper/work dir of any overlayfs instance ++ * - a disconnected dentry (detached root) ++ */ ++static int ovl_check_layer(struct super_block *sb, struct dentry *dentry, ++ const char *name) ++{ ++ struct dentry *next, *parent; ++ bool is_root = false; ++ int err = 0; ++ ++ if (!dentry || dentry == dentry->d_sb->s_root) ++ return 0; ++ ++ next = dget(dentry); ++ /* Walk back ancestors to fs root (inclusive) looking for traps */ ++ do { ++ parent = dget_parent(next); ++ is_root = (parent == next); ++ if (ovl_is_inuse(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; ++ pr_err("overlayfs: overlapping %s path\n", name); ++ } ++ dput(next); ++ next = parent; ++ } while (!err && !is_root); ++ ++ /* Did we really walk to fs root or found a detached root? */ ++ if (!err && next != dentry->d_sb->s_root) { ++ err = -ESTALE; ++ pr_err("overlayfs: disconnected %s path\n", name); ++ } ++ ++ dput(next); ++ ++ return err; ++} ++ ++/* ++ * Check if any of the layers or work dirs overlap. ++ */ ++static int ovl_check_overlapping_layers(struct super_block *sb, ++ struct ovl_fs *ofs) ++{ ++ int i, err; ++ ++ if (ofs->upper_mnt) { ++ err = ovl_check_layer(sb, ofs->upper_mnt->mnt_root, "upperdir"); ++ if (err) ++ return err; ++ ++ /* ++ * Checking workbasedir avoids hitting ovl_is_inuse(parent) of ++ * this instance and covers overlapping work and index dirs, ++ * unless work or index dir have been moved since created inside ++ * workbasedir. In that case, we already have their traps in ++ * inode cache and we will catch that case on lookup. ++ */ ++ err = ovl_check_layer(sb, ofs->workbasedir, "workdir"); ++ if (err) ++ return err; ++ } ++ ++ for (i = 0; i < ofs->numlower; i++) { ++ err = ovl_check_layer(sb, ofs->lower_layers[i].mnt->mnt_root, ++ "lowerdir"); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ + static int ovl_fill_super(struct super_block *sb, void *data, int silent) + { + struct path upperpath = { }; +@@ -1429,17 +1558,20 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) + if (ofs->config.xino != OVL_XINO_OFF) + ofs->xino_bits = BITS_PER_LONG - 32; + ++ /* alloc/destroy_inode needed for setting up traps in inode cache */ ++ sb->s_op = &ovl_super_operations; ++ + if (ofs->config.upperdir) { + if (!ofs->config.workdir) { + pr_err("overlayfs: missing 'workdir'\n"); + goto out_err; + } + +- err = ovl_get_upper(ofs, &upperpath); ++ err = ovl_get_upper(sb, ofs, &upperpath); + if (err) + goto out_err; + +- err = ovl_get_workdir(ofs, &upperpath); ++ err = ovl_get_workdir(sb, ofs, &upperpath); + if (err) + goto out_err; + +@@ -1460,7 +1592,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) + sb->s_flags |= SB_RDONLY; + + if (!(ovl_force_readonly(ofs)) && ofs->config.index) { +- err = ovl_get_indexdir(ofs, oe, &upperpath); ++ err = ovl_get_indexdir(sb, ofs, oe, &upperpath); + if (err) + goto out_free_oe; + +@@ -1473,6 +1605,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) + + } + ++ err = ovl_check_overlapping_layers(sb, ofs); ++ if (err) ++ goto out_free_oe; ++ + /* Show index=off in /proc/mounts for forced r/o mount */ + if (!ofs->indexdir) { + ofs->config.index = false; +@@ -1494,7 +1630,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) + cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); + + sb->s_magic = OVERLAYFS_SUPER_MAGIC; +- sb->s_op = &ovl_super_operations; + sb->s_xattr = ovl_xattr_handlers; + sb->s_fs_info = ofs; + sb->s_flags |= SB_POSIXACL; +diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c +index c9a2e3c6d537..db8bdb29b320 100644 +--- a/fs/overlayfs/util.c ++++ b/fs/overlayfs/util.c +@@ -653,6 +653,18 @@ void ovl_inuse_unlock(struct dentry *dentry) + } + } + ++bool ovl_is_inuse(struct dentry *dentry) ++{ ++ struct inode *inode = d_inode(dentry); ++ bool inuse; ++ ++ spin_lock(&inode->i_lock); ++ inuse = (inode->i_state & I_OVL_INUSE); ++ spin_unlock(&inode->i_lock); ++ ++ return inuse; ++} ++ + /* + * Does this overlay dentry need to be indexed on copy up? + */ +-- +2.20.1 + diff --git a/queue-4.19/ovl-don-t-fail-with-disconnected-lower-nfs.patch b/queue-4.19/ovl-don-t-fail-with-disconnected-lower-nfs.patch new file mode 100644 index 00000000000..1d34deaaef5 --- /dev/null +++ b/queue-4.19/ovl-don-t-fail-with-disconnected-lower-nfs.patch @@ -0,0 +1,79 @@ +From c3860d1b1b755a9b4e1888bbc4b1d643a9f69fa6 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Tue, 18 Jun 2019 15:06:16 +0200 +Subject: ovl: don't fail with disconnected lower NFS + +[ Upstream commit 9179c21dc6ed1c993caa5fe4da876a6765c26af7 ] + +NFS mounts can be disconnected from fs root. Don't fail the overlapping +layer check because of this. + +The check is not authoritative anyway, since topology can change during or +after the check. + +Reported-by: Antti Antinoja +Signed-off-by: Miklos Szeredi +Fixes: 146d62e5a586 ("ovl: detect overlapping layers") +Signed-off-by: Sasha Levin +--- + fs/overlayfs/super.c | 26 +++++++++----------------- + 1 file changed, 9 insertions(+), 17 deletions(-) + +diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c +index 4e268f981b4d..d6e60a7156a1 100644 +--- a/fs/overlayfs/super.c ++++ b/fs/overlayfs/super.c +@@ -1444,23 +1444,20 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, + * Check if this layer root is a descendant of: + * - another layer of this overlayfs instance + * - upper/work dir of any overlayfs instance +- * - a disconnected dentry (detached root) + */ + static int ovl_check_layer(struct super_block *sb, struct dentry *dentry, + const char *name) + { +- struct dentry *next, *parent; +- bool is_root = false; ++ struct dentry *next = dentry, *parent; + int err = 0; + +- if (!dentry || dentry == dentry->d_sb->s_root) ++ if (!dentry) + return 0; + +- next = dget(dentry); +- /* Walk back ancestors to fs root (inclusive) looking for traps */ +- do { +- parent = dget_parent(next); +- is_root = (parent == next); ++ parent = dget_parent(next); ++ ++ /* Walk back ancestors to root (inclusive) looking for traps */ ++ while (!err && parent != next) { + if (ovl_is_inuse(parent)) { + err = -EBUSY; + pr_err("overlayfs: %s path overlapping in-use upperdir/workdir\n", +@@ -1469,17 +1466,12 @@ static int ovl_check_layer(struct super_block *sb, struct dentry *dentry, + err = -ELOOP; + pr_err("overlayfs: overlapping %s path\n", name); + } +- dput(next); + next = parent; +- } while (!err && !is_root); +- +- /* Did we really walk to fs root or found a detached root? */ +- if (!err && next != dentry->d_sb->s_root) { +- err = -ESTALE; +- pr_err("overlayfs: disconnected %s path\n", name); ++ parent = dget_parent(next); ++ dput(next); + } + +- dput(next); ++ dput(parent); + + return err; + } +-- +2.20.1 + diff --git a/queue-4.19/ovl-fix-bogus-wmaybe-unitialized-warning.patch b/queue-4.19/ovl-fix-bogus-wmaybe-unitialized-warning.patch new file mode 100644 index 00000000000..fffc9e0ca63 --- /dev/null +++ b/queue-4.19/ovl-fix-bogus-wmaybe-unitialized-warning.patch @@ -0,0 +1,45 @@ +From 68836aa4843eeb2306649eedd4749410fdd14159 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Mon, 17 Jun 2019 14:39:29 +0200 +Subject: ovl: fix bogus -Wmaybe-unitialized warning + +[ Upstream commit 1dac6f5b0ed2601be21bb4e27a44b0c3e667b7f4 ] + +gcc gets a bit confused by the logic in ovl_setup_trap() and +can't figure out whether the local 'trap' variable in the caller +was initialized or not: + +fs/overlayfs/super.c: In function 'ovl_fill_super': +fs/overlayfs/super.c:1333:4: error: 'trap' may be used uninitialized in this function [-Werror=maybe-uninitialized] + iput(trap); + ^~~~~~~~~~ +fs/overlayfs/super.c:1312:17: note: 'trap' was declared here + +Reword slightly to make it easier for the compiler to understand. + +Fixes: 146d62e5a586 ("ovl: detect overlapping layers") +Signed-off-by: Arnd Bergmann +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + fs/overlayfs/super.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c +index d6e60a7156a1..2d028c02621f 100644 +--- a/fs/overlayfs/super.c ++++ b/fs/overlayfs/super.c +@@ -996,8 +996,8 @@ static int ovl_setup_trap(struct super_block *sb, struct dentry *dir, + int err; + + trap = ovl_get_trap_inode(sb, dir); +- err = PTR_ERR(trap); +- if (IS_ERR(trap)) { ++ err = PTR_ERR_OR_ZERO(trap); ++ if (err) { + if (err == -ELOOP) + pr_err("overlayfs: conflicting %s path\n", name); + return err; +-- +2.20.1 + diff --git a/queue-4.19/ovl-fix-wrong-flags-check-in-fs_ioc_fs-sg-etxattr-io.patch b/queue-4.19/ovl-fix-wrong-flags-check-in-fs_ioc_fs-sg-etxattr-io.patch new file mode 100644 index 00000000000..dde82ff85b7 --- /dev/null +++ b/queue-4.19/ovl-fix-wrong-flags-check-in-fs_ioc_fs-sg-etxattr-io.patch @@ -0,0 +1,152 @@ +From 16fa3104ff72cb88af22bc67d4fe47f6313262fc Mon Sep 17 00:00:00 2001 +From: Amir Goldstein +Date: Tue, 11 Jun 2019 18:09:28 +0300 +Subject: ovl: fix wrong flags check in FS_IOC_FS[SG]ETXATTR ioctls + +[ Upstream commit 941d935ac7636911a3fd8fa80e758e52b0b11e20 ] + +The ioctl argument was parsed as the wrong type. + +Fixes: b21d9c435f93 ("ovl: support the FS_IOC_FS[SG]ETXATTR ioctls") +Signed-off-by: Amir Goldstein +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + fs/overlayfs/file.c | 91 ++++++++++++++++++++++++++++++++------------- + 1 file changed, 65 insertions(+), 26 deletions(-) + +diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c +index 749532fd51d7..0bd276e4ccbe 100644 +--- a/fs/overlayfs/file.c ++++ b/fs/overlayfs/file.c +@@ -409,37 +409,16 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd, + return ret; + } + +-static unsigned int ovl_get_inode_flags(struct inode *inode) +-{ +- unsigned int flags = READ_ONCE(inode->i_flags); +- unsigned int ovl_iflags = 0; +- +- if (flags & S_SYNC) +- ovl_iflags |= FS_SYNC_FL; +- if (flags & S_APPEND) +- ovl_iflags |= FS_APPEND_FL; +- if (flags & S_IMMUTABLE) +- ovl_iflags |= FS_IMMUTABLE_FL; +- if (flags & S_NOATIME) +- ovl_iflags |= FS_NOATIME_FL; +- +- return ovl_iflags; +-} +- + static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, +- unsigned long arg) ++ unsigned long arg, unsigned int iflags) + { + long ret; + struct inode *inode = file_inode(file); +- unsigned int flags; +- unsigned int old_flags; ++ unsigned int old_iflags; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + +- if (get_user(flags, (int __user *) arg)) +- return -EFAULT; +- + ret = mnt_want_write_file(file); + if (ret) + return ret; +@@ -448,8 +427,8 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, + + /* Check the capability before cred override */ + ret = -EPERM; +- old_flags = ovl_get_inode_flags(inode); +- if (((flags ^ old_flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && ++ old_iflags = READ_ONCE(inode->i_flags); ++ if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) && + !capable(CAP_LINUX_IMMUTABLE)) + goto unlock; + +@@ -469,6 +448,63 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, + + } + ++static unsigned int ovl_fsflags_to_iflags(unsigned int flags) ++{ ++ unsigned int iflags = 0; ++ ++ if (flags & FS_SYNC_FL) ++ iflags |= S_SYNC; ++ if (flags & FS_APPEND_FL) ++ iflags |= S_APPEND; ++ if (flags & FS_IMMUTABLE_FL) ++ iflags |= S_IMMUTABLE; ++ if (flags & FS_NOATIME_FL) ++ iflags |= S_NOATIME; ++ ++ return iflags; ++} ++ ++static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ unsigned int flags; ++ ++ if (get_user(flags, (int __user *) arg)) ++ return -EFAULT; ++ ++ return ovl_ioctl_set_flags(file, cmd, arg, ++ ovl_fsflags_to_iflags(flags)); ++} ++ ++static unsigned int ovl_fsxflags_to_iflags(unsigned int xflags) ++{ ++ unsigned int iflags = 0; ++ ++ if (xflags & FS_XFLAG_SYNC) ++ iflags |= S_SYNC; ++ if (xflags & FS_XFLAG_APPEND) ++ iflags |= S_APPEND; ++ if (xflags & FS_XFLAG_IMMUTABLE) ++ iflags |= S_IMMUTABLE; ++ if (xflags & FS_XFLAG_NOATIME) ++ iflags |= S_NOATIME; ++ ++ return iflags; ++} ++ ++static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct fsxattr fa; ++ ++ memset(&fa, 0, sizeof(fa)); ++ if (copy_from_user(&fa, (void __user *) arg, sizeof(fa))) ++ return -EFAULT; ++ ++ return ovl_ioctl_set_flags(file, cmd, arg, ++ ovl_fsxflags_to_iflags(fa.fsx_xflags)); ++} ++ + static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + long ret; +@@ -480,8 +516,11 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + break; + + case FS_IOC_SETFLAGS: ++ ret = ovl_ioctl_set_fsflags(file, cmd, arg); ++ break; ++ + case FS_IOC_FSSETXATTR: +- ret = ovl_ioctl_set_flags(file, cmd, arg); ++ ret = ovl_ioctl_set_fsxflags(file, cmd, arg); + break; + + default: +-- +2.20.1 + diff --git a/queue-4.19/ovl-make-i_ino-consistent-with-st_ino-in-more-cases.patch b/queue-4.19/ovl-make-i_ino-consistent-with-st_ino-in-more-cases.patch new file mode 100644 index 00000000000..8ad22a89d27 --- /dev/null +++ b/queue-4.19/ovl-make-i_ino-consistent-with-st_ino-in-more-cases.patch @@ -0,0 +1,53 @@ +From aded172679503e13da91a5a12dda390b2e39de85 Mon Sep 17 00:00:00 2001 +From: Amir Goldstein +Date: Sun, 9 Jun 2019 19:03:44 +0300 +Subject: ovl: make i_ino consistent with st_ino in more cases + +[ Upstream commit 6dde1e42f497b2d4e22466f23019016775607947 ] + +Relax the condition that overlayfs supports nfs export, to require +that i_ino is consistent with st_ino/d_ino. + +It is enough to require that st_ino and d_ino are consistent. + +This fixes the failure of xfstest generic/504, due to mismatch of +st_ino to inode number in the output of /proc/locks. + +Fixes: 12574a9f4c9c ("ovl: consistent i_ino for non-samefs with xino") +Cc: # v4.19 +Signed-off-by: Amir Goldstein +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + fs/overlayfs/inode.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c +index b48273e846ad..373ccff9880c 100644 +--- a/fs/overlayfs/inode.c ++++ b/fs/overlayfs/inode.c +@@ -553,15 +553,15 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev, + int xinobits = ovl_xino_bits(inode->i_sb); + + /* +- * When NFS export is enabled and d_ino is consistent with st_ino +- * (samefs or i_ino has enough bits to encode layer), set the same +- * value used for d_ino to i_ino, because nfsd readdirplus compares +- * d_ino values to i_ino values of child entries. When called from ++ * When d_ino is consistent with st_ino (samefs or i_ino has enough ++ * bits to encode layer), set the same value used for st_ino to i_ino, ++ * so inode number exposed via /proc/locks and a like will be ++ * consistent with d_ino and st_ino values. An i_ino value inconsistent ++ * with d_ino also causes nfsd readdirplus to fail. When called from + * ovl_new_inode(), ino arg is 0, so i_ino will be updated to real + * upper inode i_ino on ovl_inode_init() or ovl_inode_update(). + */ +- if (inode->i_sb->s_export_op && +- (ovl_same_sb(inode->i_sb) || xinobits)) { ++ if (ovl_same_sb(inode->i_sb) || xinobits) { + inode->i_ino = ino; + if (xinobits && fsid && !(ino >> (64 - xinobits))) + inode->i_ino |= (unsigned long)fsid << (64 - xinobits); +-- +2.20.1 + diff --git a/queue-4.19/ovl-support-the-fs_ioc_fs-sg-etxattr-ioctls.patch b/queue-4.19/ovl-support-the-fs_ioc_fs-sg-etxattr-ioctls.patch new file mode 100644 index 00000000000..2df4c6a956d --- /dev/null +++ b/queue-4.19/ovl-support-the-fs_ioc_fs-sg-etxattr-ioctls.patch @@ -0,0 +1,64 @@ +From 1ca682dc2a92aaa68309e03485a5d4cddb5bc523 Mon Sep 17 00:00:00 2001 +From: Amir Goldstein +Date: Sun, 26 May 2019 09:28:25 +0300 +Subject: ovl: support the FS_IOC_FS[SG]ETXATTR ioctls + +[ Upstream commit b21d9c435f935014d3e3fa6914f2e4fbabb0e94d ] + +They are the extended version of FS_IOC_FS[SG]ETFLAGS ioctls. +xfs_io -c "chattr " uses the new ioctls for setting flags. + +This used to work in kernel pre v4.19, before stacked file ops +introduced the ovl_ioctl whitelist. + +Reported-by: Dave Chinner +Fixes: d1d04ef8572b ("ovl: stack file ops") +Cc: # v4.19 +Signed-off-by: Amir Goldstein +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + fs/overlayfs/file.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c +index 00338b828f76..749532fd51d7 100644 +--- a/fs/overlayfs/file.c ++++ b/fs/overlayfs/file.c +@@ -426,7 +426,8 @@ static unsigned int ovl_get_inode_flags(struct inode *inode) + return ovl_iflags; + } + +-static long ovl_ioctl_set_flags(struct file *file, unsigned long arg) ++static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, ++ unsigned long arg) + { + long ret; + struct inode *inode = file_inode(file); +@@ -456,7 +457,7 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned long arg) + if (ret) + goto unlock; + +- ret = ovl_real_ioctl(file, FS_IOC_SETFLAGS, arg); ++ ret = ovl_real_ioctl(file, cmd, arg); + + ovl_copyflags(ovl_inode_real(inode), inode); + unlock: +@@ -474,11 +475,13 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + + switch (cmd) { + case FS_IOC_GETFLAGS: ++ case FS_IOC_FSGETXATTR: + ret = ovl_real_ioctl(file, cmd, arg); + break; + + case FS_IOC_SETFLAGS: +- ret = ovl_ioctl_set_flags(file, arg); ++ case FS_IOC_FSSETXATTR: ++ ret = ovl_ioctl_set_flags(file, cmd, arg); + break; + + default: +-- +2.20.1 + diff --git a/queue-4.19/s390-ap-rework-assembler-functions-to-use-unions-for.patch b/queue-4.19/s390-ap-rework-assembler-functions-to-use-unions-for.patch new file mode 100644 index 00000000000..5b4e397801d --- /dev/null +++ b/queue-4.19/s390-ap-rework-assembler-functions-to-use-unions-for.patch @@ -0,0 +1,86 @@ +From e2a889526f14636917cfce020a4f2ab912df0950 Mon Sep 17 00:00:00 2001 +From: Harald Freudenberger +Date: Fri, 16 Nov 2018 15:48:10 +0100 +Subject: s390/ap: rework assembler functions to use unions for in/out register + variables + +[ Upstream commit 159491f3b509bd8101199944dc7b0673b881c734 ] + +The inline assembler functions ap_aqic() and ap_qact() used two +variables declared on the very same register. One variable was for +input only, the other for output. Looks like newer versions of the gcc +don't like this. Anyway it is a better coding to use one variable +(which may have a union data type) on one register for input and +output. So this patch introduces unions and uses only one variable now +for input and output for GR1 for the PQAP(QACT) and PQAP(QIC) +invocation. + +Signed-off-by: Harald Freudenberger +Acked-by: Ilya Leoshkevich +Signed-off-by: Martin Schwidefsky +Signed-off-by: Sasha Levin +--- + arch/s390/include/asm/ap.h | 28 +++++++++++++++++++--------- + 1 file changed, 19 insertions(+), 9 deletions(-) + +diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h +index 8c00fd509c45..1a6a7092d942 100644 +--- a/arch/s390/include/asm/ap.h ++++ b/arch/s390/include/asm/ap.h +@@ -221,16 +221,22 @@ static inline struct ap_queue_status ap_aqic(ap_qid_t qid, + void *ind) + { + register unsigned long reg0 asm ("0") = qid | (3UL << 24); +- register struct ap_qirq_ctrl reg1_in asm ("1") = qirqctrl; +- register struct ap_queue_status reg1_out asm ("1"); ++ register union { ++ unsigned long value; ++ struct ap_qirq_ctrl qirqctrl; ++ struct ap_queue_status status; ++ } reg1 asm ("1"); + register void *reg2 asm ("2") = ind; + ++ reg1.qirqctrl = qirqctrl; ++ + asm volatile( + ".long 0xb2af0000" /* PQAP(AQIC) */ +- : "=d" (reg1_out) +- : "d" (reg0), "d" (reg1_in), "d" (reg2) ++ : "+d" (reg1) ++ : "d" (reg0), "d" (reg2) + : "cc"); +- return reg1_out; ++ ++ return reg1.status; + } + + /* +@@ -264,17 +270,21 @@ static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit, + { + register unsigned long reg0 asm ("0") = qid | (5UL << 24) + | ((ifbit & 0x01) << 22); +- register unsigned long reg1_in asm ("1") = apinfo->val; +- register struct ap_queue_status reg1_out asm ("1"); ++ register union { ++ unsigned long value; ++ struct ap_queue_status status; ++ } reg1 asm ("1"); + register unsigned long reg2 asm ("2"); + ++ reg1.value = apinfo->val; ++ + asm volatile( + ".long 0xb2af0000" /* PQAP(QACT) */ +- : "+d" (reg1_in), "=d" (reg1_out), "=d" (reg2) ++ : "+d" (reg1), "=d" (reg2) + : "d" (reg0) + : "cc"); + apinfo->val = reg2; +- return reg1_out; ++ return reg1.status; + } + + /** +-- +2.20.1 + diff --git a/queue-4.19/s390-jump_label-use-jdd-constraint-on-gcc9.patch b/queue-4.19/s390-jump_label-use-jdd-constraint-on-gcc9.patch new file mode 100644 index 00000000000..41b940667e5 --- /dev/null +++ b/queue-4.19/s390-jump_label-use-jdd-constraint-on-gcc9.patch @@ -0,0 +1,75 @@ +From 42449e3341fdbe8209742950c33e313417c1284d Mon Sep 17 00:00:00 2001 +From: Ilya Leoshkevich +Date: Fri, 21 Jun 2019 17:39:12 +0200 +Subject: s390/jump_label: Use "jdd" constraint on gcc9 + +[ Upstream commit 146448524bddbf6dfc62de31957e428de001cbda ] + +[heiko.carstens@de.ibm.com]: +----- +Laura Abbott reported that the kernel doesn't build anymore with gcc 9, +due to the "X" constraint. Ilya provided the gcc 9 patch "S/390: +Introduce jdd constraint" which introduces the new "jdd" constraint +which fixes this. +----- + +The support for section anchors on S/390 introduced in gcc9 has changed +the behavior of "X" constraint, which can now produce register +references. Since existing constraints, in particular, "i", do not fit +the intended use case on S/390, the new machine-specific "jdd" +constraint was introduced. This patch makes jump labels use "jdd" +constraint when building with gcc9. + +Reported-by: Laura Abbott +Signed-off-by: Ilya Leoshkevich +Signed-off-by: Heiko Carstens +Signed-off-by: Martin Schwidefsky +Signed-off-by: Sasha Levin +--- + arch/s390/include/asm/jump_label.h | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h +index 40f651292aa7..9c7dc970e966 100644 +--- a/arch/s390/include/asm/jump_label.h ++++ b/arch/s390/include/asm/jump_label.h +@@ -10,6 +10,12 @@ + #define JUMP_LABEL_NOP_SIZE 6 + #define JUMP_LABEL_NOP_OFFSET 2 + ++#if __GNUC__ < 9 ++#define JUMP_LABEL_STATIC_KEY_CONSTRAINT "X" ++#else ++#define JUMP_LABEL_STATIC_KEY_CONSTRAINT "jdd" ++#endif ++ + /* + * We use a brcl 0,2 instruction for jump labels at compile time so it + * can be easily distinguished from a hotpatch generated instruction. +@@ -19,9 +25,9 @@ static __always_inline bool arch_static_branch(struct static_key *key, bool bran + asm_volatile_goto("0: brcl 0,"__stringify(JUMP_LABEL_NOP_OFFSET)"\n" + ".pushsection __jump_table, \"aw\"\n" + ".balign 8\n" +- ".quad 0b, %l[label], %0\n" ++ ".quad 0b, %l[label], %0+%1\n" + ".popsection\n" +- : : "X" (&((char *)key)[branch]) : : label); ++ : : JUMP_LABEL_STATIC_KEY_CONSTRAINT (key), "i" (branch) : : label); + + return false; + label: +@@ -33,9 +39,9 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool + asm_volatile_goto("0: brcl 15, %l[label]\n" + ".pushsection __jump_table, \"aw\"\n" + ".balign 8\n" +- ".quad 0b, %l[label], %0\n" ++ ".quad 0b, %l[label], %0+%1\n" + ".popsection\n" +- : : "X" (&((char *)key)[branch]) : : label); ++ : : JUMP_LABEL_STATIC_KEY_CONSTRAINT (key), "i" (branch) : : label); + + return false; + label: +-- +2.20.1 + diff --git a/queue-4.19/series b/queue-4.19/series index ce8ad94d48d..9b8bdec0f50 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -1,3 +1,11 @@ tracing-silence-gcc-9-array-bounds-warning.patch objtool-support-per-function-rodata-sections.patch gcc-9-silence-address-of-packed-member-warning.patch +ovl-support-the-fs_ioc_fs-sg-etxattr-ioctls.patch +ovl-fix-wrong-flags-check-in-fs_ioc_fs-sg-etxattr-io.patch +ovl-make-i_ino-consistent-with-st_ino-in-more-cases.patch +ovl-detect-overlapping-layers.patch +ovl-don-t-fail-with-disconnected-lower-nfs.patch +ovl-fix-bogus-wmaybe-unitialized-warning.patch +s390-jump_label-use-jdd-constraint-on-gcc9.patch +s390-ap-rework-assembler-functions-to-use-unions-for.patch