From: Greg Kroah-Hartman Date: Mon, 6 Sep 2021 08:55:09 +0000 (+0200) Subject: 5.10-stable patches X-Git-Tag: v5.10.63~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=de6e65979768cfb814c1d22d9fdbf22c41467fe1;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch new-helper-inode_wrong_type.patch --- 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 index 00000000000..eaa04921526 --- /dev/null +++ b/queue-5.10/fuse-fix-illegal-access-to-inode-with-reused-nodeid.patch @@ -0,0 +1,114 @@ +From 15db16837a35d8007cb8563358787412213db25e Mon Sep 17 00:00:00 2001 +From: Amir Goldstein +Date: Mon, 21 Jun 2021 14:03:53 +0300 +Subject: fuse: fix illegal access to inode with reused nodeid + +From: Amir Goldstein + +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 +Signed-off-by: Miklos Szeredi +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..d505bcaf9d7 --- /dev/null +++ b/queue-5.10/new-helper-inode_wrong_type.patch @@ -0,0 +1,215 @@ +From 6e3e2c4362e41a2f18e3f7a5ad81bd2f49a47b85 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Mon, 1 Mar 2021 20:37:10 -0500 +Subject: new helper: inode_wrong_type() + +From: Al Viro + +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 +Signed-off-by: Amir Goldstein +Signed-off-by: Greg Kroah-Hartman +--- + 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)) diff --git a/queue-5.10/series b/queue-5.10/series index c94a7e403ec..3eff55a9c9f 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -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