--- /dev/null
+From 0102ec399b0008a2bad0238c0bafd0ca06e55e0a Mon Sep 17 00:00:00 2001
+From: Amir Goldstein <amir73il@gmail.com>
+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 <amir73il@gmail.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c3860d1b1b755a9b4e1888bbc4b1d643a9f69fa6 Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <mszeredi@redhat.com>
+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 <antti@fennosys.fi>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Fixes: 146d62e5a586 ("ovl: detect overlapping layers")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 68836aa4843eeb2306649eedd4749410fdd14159 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+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 <arnd@arndb.de>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 16fa3104ff72cb88af22bc67d4fe47f6313262fc Mon Sep 17 00:00:00 2001
+From: Amir Goldstein <amir73il@gmail.com>
+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 <amir73il@gmail.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From aded172679503e13da91a5a12dda390b2e39de85 Mon Sep 17 00:00:00 2001
+From: Amir Goldstein <amir73il@gmail.com>
+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: <stable@vger.kernel.org> # v4.19
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1ca682dc2a92aaa68309e03485a5d4cddb5bc523 Mon Sep 17 00:00:00 2001
+From: Amir Goldstein <amir73il@gmail.com>
+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 <flags>" 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 <david@fromorbit.com>
+Fixes: d1d04ef8572b ("ovl: stack file ops")
+Cc: <stable@vger.kernel.org> # v4.19
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e2a889526f14636917cfce020a4f2ab912df0950 Mon Sep 17 00:00:00 2001
+From: Harald Freudenberger <freude@linux.ibm.com>
+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 <freude@linux.ibm.com>
+Acked-by: Ilya Leoshkevich <iii@linux.ibm.com>
+Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 42449e3341fdbe8209742950c33e313417c1284d Mon Sep 17 00:00:00 2001
+From: Ilya Leoshkevich <iii@linux.ibm.com>
+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 <labbott@redhat.com>
+Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
+Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
+Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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