diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2020-12-10 17:33:14 +0300 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2020-12-10 17:33:14 +0300 |
commit | 5d069dbe8aaf2a197142558b6fb2978189ba3454 (patch) | |
tree | 4c9ddb84e8fde7b3120e76c733fca542a5ff7ca2 /fs/fuse/fuse_i.h | |
parent | 9d769e6aa2524e1762e3b8681e0ed78f8acf6cad (diff) | |
download | linux-5d069dbe8aaf2a197142558b6fb2978189ba3454.tar.xz |
fuse: fix bad inode
Jan Kara's analysis of the syzbot report (edited):
The reproducer opens a directory on FUSE filesystem, it then attaches
dnotify mark to the open directory. After that a fuse_do_getattr() call
finds that attributes returned by the server are inconsistent, and calls
make_bad_inode() which, among other things does:
inode->i_mode = S_IFREG;
This then confuses dnotify which doesn't tear down its structures
properly and eventually crashes.
Avoid calling make_bad_inode() on a live inode: switch to a private flag on
the fuse inode. Also add the test to ops which the bad_inode_ops would
have caught.
This bug goes back to the initial merge of fuse in 2.6.14...
Reported-by: syzbot+f427adf9324b92652ccc@syzkaller.appspotmail.com
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Tested-by: Jan Kara <jack@suse.cz>
Cc: <stable@vger.kernel.org>
Diffstat (limited to 'fs/fuse/fuse_i.h')
-rw-r--r-- | fs/fuse/fuse_i.h | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d414c787e362..7c4b8cb93f9f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -172,6 +172,8 @@ enum { FUSE_I_INIT_RDPLUS, /** An operation changing file size is in progress */ FUSE_I_SIZE_UNSTABLE, + /* Bad inode */ + FUSE_I_BAD, }; struct fuse_conn; @@ -859,6 +861,16 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc) return atomic64_read(&fc->attr_version); } +static inline void fuse_make_bad(struct inode *inode) +{ + set_bit(FUSE_I_BAD, &get_fuse_inode(inode)->state); +} + +static inline bool fuse_is_bad(struct inode *inode) +{ + return unlikely(test_bit(FUSE_I_BAD, &get_fuse_inode(inode)->state)); +} + /** Device operations */ extern const struct file_operations fuse_dev_operations; |