]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 6 Sep 2021 08:55:09 +0000 (10:55 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 6 Sep 2021 08:55:09 +0000 (10:55 +0200)
added patches:
fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch
new-helper-inode_wrong_type.patch

queue-5.10/fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch [new file with mode: 0644]
queue-5.10/new-helper-inode_wrong_type.patch [new file with mode: 0644]
queue-5.10/series

diff --git a/queue-5.10/fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch b/queue-5.10/fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch
new file mode 100644 (file)
index 0000000..eaa0492
--- /dev/null
@@ -0,0 +1,114 @@
+From 15db16837a35d8007cb8563358787412213db25e Mon Sep 17 00:00:00 2001
+From: Amir Goldstein <amir73il@gmail.com>
+Date: Mon, 21 Jun 2021 14:03:53 +0300
+Subject: fuse: fix illegal access to inode with reused nodeid
+
+From: Amir Goldstein <amir73il@gmail.com>
+
+commit 15db16837a35d8007cb8563358787412213db25e upstream.
+
+Server responds to LOOKUP and other ops (READDIRPLUS/CREATE/MKNOD/...)
+with ourarg containing nodeid and generation.
+
+If a fuse inode is found in inode cache with the same nodeid but different
+generation, the existing fuse inode should be unhashed and marked "bad" and
+a new inode with the new generation should be hashed instead.
+
+This can happen, for example, with passhrough fuse filesystem that returns
+the real filesystem ino/generation on lookup and where real inode numbers
+can get recycled due to real files being unlinked not via the fuse
+passthrough filesystem.
+
+With current code, this situation will not be detected and an old fuse
+dentry that used to point to an older generation real inode, can be used to
+access a completely new inode, which should be accessed only via the new
+dentry.
+
+Note that because the FORGET message carries the nodeid w/o generation, the
+server should wait to get FORGET counts for the nlookup counts of the old
+and reused inodes combined, before it can free the resources associated to
+that nodeid.
+
+Stable backport notes:
+* This is not a regression. The bug has been in fuse forever, but only
+  a certain class of low level fuse filesystems can trigger this bug
+* Because there is no way to check if this fix is applied in runtime,
+  libfuse test_examples.py tests this fix with hardcoded check for
+  kernel version >= 5.14
+* After backport to stable kernel(s), the libfuse test can be updated
+  to also check minimal stable kernel version(s)
+* Depends on "fuse: fix bad inode" which is already applied to stable
+  kernels v5.4.y and v5.10.y
+* Required backporting helper inode_wrong_type()
+
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/linux-fsdevel/CAOQ4uxi8DymG=JO_sAU+wS8akFdzh+PuXwW3Ebgahd2Nwnh7zA@mail.gmail.com/
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/fuse/dir.c     |    2 +-
+ fs/fuse/fuse_i.h  |    7 +++++++
+ fs/fuse/inode.c   |    4 ++--
+ fs/fuse/readdir.c |    7 +++++--
+ 4 files changed, 15 insertions(+), 5 deletions(-)
+
+--- a/fs/fuse/dir.c
++++ b/fs/fuse/dir.c
+@@ -252,7 +252,7 @@ static int fuse_dentry_revalidate(struct
+               if (ret == -ENOMEM)
+                       goto out;
+               if (ret || fuse_invalid_attr(&outarg.attr) ||
+-                  inode_wrong_type(inode, outarg.attr.mode))
++                  fuse_stale_inode(inode, outarg.generation, &outarg.attr))
+                       goto invalid;
+               forget_all_cached_acls(inode);
+--- a/fs/fuse/fuse_i.h
++++ b/fs/fuse/fuse_i.h
+@@ -860,6 +860,13 @@ static inline u64 fuse_get_attr_version(
+       return atomic64_read(&fc->attr_version);
+ }
++static inline bool fuse_stale_inode(const struct inode *inode, int generation,
++                                  struct fuse_attr *attr)
++{
++      return inode->i_generation != generation ||
++              inode_wrong_type(inode, attr->mode);
++}
++
+ static inline void fuse_make_bad(struct inode *inode)
+ {
+       remove_inode_hash(inode);
+--- a/fs/fuse/inode.c
++++ b/fs/fuse/inode.c
+@@ -340,8 +340,8 @@ retry:
+               inode->i_generation = generation;
+               fuse_init_inode(inode, attr);
+               unlock_new_inode(inode);
+-      } else if (inode_wrong_type(inode, attr->mode)) {
+-              /* Inode has changed type, any I/O on the old should fail */
++      } else if (fuse_stale_inode(inode, generation, attr)) {
++              /* nodeid was reused, any I/O on the old inode should fail */
+               fuse_make_bad(inode);
+               iput(inode);
+               goto retry;
+--- a/fs/fuse/readdir.c
++++ b/fs/fuse/readdir.c
+@@ -200,9 +200,12 @@ retry:
+       if (!d_in_lookup(dentry)) {
+               struct fuse_inode *fi;
+               inode = d_inode(dentry);
++              if (inode && get_node_id(inode) != o->nodeid)
++                      inode = NULL;
+               if (!inode ||
+-                  get_node_id(inode) != o->nodeid ||
+-                  inode_wrong_type(inode, o->attr.mode)) {
++                  fuse_stale_inode(inode, o->generation, &o->attr)) {
++                      if (inode)
++                              fuse_make_bad(inode);
+                       d_invalidate(dentry);
+                       dput(dentry);
+                       goto retry;
diff --git a/queue-5.10/new-helper-inode_wrong_type.patch b/queue-5.10/new-helper-inode_wrong_type.patch
new file mode 100644 (file)
index 0000000..d505bca
--- /dev/null
@@ -0,0 +1,215 @@
+From 6e3e2c4362e41a2f18e3f7a5ad81bd2f49a47b85 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Mon, 1 Mar 2021 20:37:10 -0500
+Subject: new helper: inode_wrong_type()
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit 6e3e2c4362e41a2f18e3f7a5ad81bd2f49a47b85 upstream.
+
+inode_wrong_type(inode, mode) returns true if setting inode->i_mode
+to given value would've changed the inode type.  We have enough of
+those checks open-coded to make a helper worthwhile.
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/9p/vfs_inode.c      |    4 ++--
+ fs/9p/vfs_inode_dotl.c |    4 ++--
+ fs/cifs/inode.c        |    5 ++---
+ fs/fuse/dir.c          |    6 +++---
+ fs/fuse/inode.c        |    2 +-
+ fs/fuse/readdir.c      |    2 +-
+ fs/nfs/inode.c         |    6 +++---
+ fs/nfsd/nfsproc.c      |    2 +-
+ fs/overlayfs/namei.c   |    4 ++--
+ include/linux/fs.h     |    5 +++++
+ 10 files changed, 22 insertions(+), 18 deletions(-)
+
+--- a/fs/9p/vfs_inode.c
++++ b/fs/9p/vfs_inode.c
+@@ -398,7 +398,7 @@ static int v9fs_test_inode(struct inode
+       umode = p9mode2unixmode(v9ses, st, &rdev);
+       /* don't match inode of different type */
+-      if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
++      if (inode_wrong_type(inode, umode))
+               return 0;
+       /* compare qid details */
+@@ -1360,7 +1360,7 @@ int v9fs_refresh_inode(struct p9_fid *fi
+        * Don't update inode if the file type is different
+        */
+       umode = p9mode2unixmode(v9ses, st, &rdev);
+-      if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
++      if (inode_wrong_type(inode, umode))
+               goto out;
+       /*
+--- a/fs/9p/vfs_inode_dotl.c
++++ b/fs/9p/vfs_inode_dotl.c
+@@ -59,7 +59,7 @@ static int v9fs_test_inode_dotl(struct i
+       struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
+       /* don't match inode of different type */
+-      if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
++      if (inode_wrong_type(inode, st->st_mode))
+               return 0;
+       if (inode->i_generation != st->st_gen)
+@@ -933,7 +933,7 @@ int v9fs_refresh_inode_dotl(struct p9_fi
+       /*
+        * Don't update inode if the file type is different
+        */
+-      if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
++      if (inode_wrong_type(inode, st->st_mode))
+               goto out;
+       /*
+--- a/fs/cifs/inode.c
++++ b/fs/cifs/inode.c
+@@ -425,8 +425,7 @@ int cifs_get_inode_info_unix(struct inod
+               }
+               /* if filetype is different, return error */
+-              if (unlikely(((*pinode)->i_mode & S_IFMT) !=
+-                  (fattr.cf_mode & S_IFMT))) {
++              if (unlikely(inode_wrong_type(*pinode, fattr.cf_mode))) {
+                       CIFS_I(*pinode)->time = 0; /* force reval */
+                       rc = -ESTALE;
+                       goto cgiiu_exit;
+@@ -1243,7 +1242,7 @@ cifs_find_inode(struct inode *inode, voi
+               return 0;
+       /* don't match inode of different type */
+-      if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
++      if (inode_wrong_type(inode, fattr->cf_mode))
+               return 0;
+       /* if it's not a directory or has no dentries, then flag it */
+--- a/fs/fuse/dir.c
++++ b/fs/fuse/dir.c
+@@ -252,7 +252,7 @@ static int fuse_dentry_revalidate(struct
+               if (ret == -ENOMEM)
+                       goto out;
+               if (ret || fuse_invalid_attr(&outarg.attr) ||
+-                  (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
++                  inode_wrong_type(inode, outarg.attr.mode))
+                       goto invalid;
+               forget_all_cached_acls(inode);
+@@ -1062,7 +1062,7 @@ static int fuse_do_getattr(struct inode
+       err = fuse_simple_request(fm, &args);
+       if (!err) {
+               if (fuse_invalid_attr(&outarg.attr) ||
+-                  (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
++                  inode_wrong_type(inode, outarg.attr.mode)) {
+                       fuse_make_bad(inode);
+                       err = -EIO;
+               } else {
+@@ -1699,7 +1699,7 @@ int fuse_do_setattr(struct dentry *dentr
+       }
+       if (fuse_invalid_attr(&outarg.attr) ||
+-          (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
++          inode_wrong_type(inode, outarg.attr.mode)) {
+               fuse_make_bad(inode);
+               err = -EIO;
+               goto error;
+--- a/fs/fuse/inode.c
++++ b/fs/fuse/inode.c
+@@ -340,7 +340,7 @@ retry:
+               inode->i_generation = generation;
+               fuse_init_inode(inode, attr);
+               unlock_new_inode(inode);
+-      } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
++      } else if (inode_wrong_type(inode, attr->mode)) {
+               /* Inode has changed type, any I/O on the old should fail */
+               fuse_make_bad(inode);
+               iput(inode);
+--- a/fs/fuse/readdir.c
++++ b/fs/fuse/readdir.c
+@@ -202,7 +202,7 @@ retry:
+               inode = d_inode(dentry);
+               if (!inode ||
+                   get_node_id(inode) != o->nodeid ||
+-                  ((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
++                  inode_wrong_type(inode, o->attr.mode)) {
+                       d_invalidate(dentry);
+                       dput(dentry);
+                       goto retry;
+--- a/fs/nfs/inode.c
++++ b/fs/nfs/inode.c
+@@ -322,7 +322,7 @@ nfs_find_actor(struct inode *inode, void
+       if (NFS_FILEID(inode) != fattr->fileid)
+               return 0;
+-      if ((S_IFMT & inode->i_mode) != (S_IFMT & fattr->mode))
++      if (inode_wrong_type(inode, fattr->mode))
+               return 0;
+       if (nfs_compare_fh(NFS_FH(inode), fh))
+               return 0;
+@@ -1446,7 +1446,7 @@ static int nfs_check_inode_attributes(st
+                       return 0;
+               return -ESTALE;
+       }
+-      if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
++      if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode))
+               return -ESTALE;
+@@ -1861,7 +1861,7 @@ static int nfs_update_inode(struct inode
+       /*
+        * Make sure the inode's type hasn't changed.
+        */
+-      if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
++      if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode)) {
+               /*
+               * Big trouble! The inode has become a different object.
+               */
+--- a/fs/nfsd/nfsproc.c
++++ b/fs/nfsd/nfsproc.c
+@@ -376,7 +376,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
+               /* Make sure the type and device matches */
+               resp->status = nfserr_exist;
+-              if (inode && type != (inode->i_mode & S_IFMT))
++              if (inode && inode_wrong_type(inode, type))
+                       goto out_unlock;
+       }
+--- a/fs/overlayfs/namei.c
++++ b/fs/overlayfs/namei.c
+@@ -366,7 +366,7 @@ int ovl_check_origin_fh(struct ovl_fs *o
+               return PTR_ERR(origin);
+       if (upperdentry && !ovl_is_whiteout(upperdentry) &&
+-          ((d_inode(origin)->i_mode ^ d_inode(upperdentry)->i_mode) & S_IFMT))
++          inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
+               goto invalid;
+       if (!*stackp)
+@@ -724,7 +724,7 @@ struct dentry *ovl_lookup_index(struct o
+               index = ERR_PTR(-ESTALE);
+               goto out;
+       } else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
+-                 ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
++                 inode_wrong_type(inode, d_inode(origin)->i_mode)) {
+               /*
+                * Index should always be of the same file type as origin
+                * except for the case of a whiteout index. A whiteout
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -2768,6 +2768,11 @@ static inline bool execute_ok(struct ino
+       return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
+ }
++static inline bool inode_wrong_type(const struct inode *inode, umode_t mode)
++{
++      return (inode->i_mode ^ mode) & S_IFMT;
++}
++
+ static inline void file_start_write(struct file *file)
+ {
+       if (!S_ISREG(file_inode(file)->i_mode))
index c94a7e403ecc5827b7c901c41a025e8ab3e52fe3..3eff55a9c9f8d8532ba402d7241beb20962247f4 100644 (file)
@@ -24,3 +24,5 @@ alsa-hda-realtek-workaround-for-conflicting-ssid-on-asus-rog-strix-g17.patch
 alsa-pcm-fix-divide-error-in-snd_pcm_lib_ioctl.patch
 serial-8250-8250_omap-fix-possible-array-out-of-bounds-access.patch
 spi-switch-to-signed-types-for-_native_cs-spi-controller-fields.patch
+new-helper-inode_wrong_type.patch
+fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch