diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2018-07-26 10:13:11 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2018-07-26 10:13:11 -0400 |
commit | 63576c13bd17848376c8ba4a98f5d5151140c4ac (patch) | |
tree | 2b33da7010c6d8a18f0736cf80a967a4ce8ad50c /fs/fuse | |
parent | e8f3bd773d22f488724dffb886a1618da85c2966 (diff) |
fuse: fix initial parallel dirops
If parallel dirops are enabled in FUSE_INIT reply, then first operation may
leave fi->mutex held.
Reported-by: syzbot <syzbot+3f7b29af1baa9d0a55be@syzkaller.appspotmail.com>
Fixes: 5c672ab3f0ee ("fuse: serialize dirops by default")
Cc: <stable@vger.kernel.org> # v4.7
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dir.c | 10 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 4 | ||||
-rw-r--r-- | fs/fuse/inode.c | 14 |
3 files changed, 18 insertions, 10 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 56231b31f806..606909ed5f21 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -355,11 +355,12 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
355 | struct inode *inode; | 355 | struct inode *inode; |
356 | struct dentry *newent; | 356 | struct dentry *newent; |
357 | bool outarg_valid = true; | 357 | bool outarg_valid = true; |
358 | bool locked; | ||
358 | 359 | ||
359 | fuse_lock_inode(dir); | 360 | locked = fuse_lock_inode(dir); |
360 | err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, | 361 | err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, |
361 | &outarg, &inode); | 362 | &outarg, &inode); |
362 | fuse_unlock_inode(dir); | 363 | fuse_unlock_inode(dir, locked); |
363 | if (err == -ENOENT) { | 364 | if (err == -ENOENT) { |
364 | outarg_valid = false; | 365 | outarg_valid = false; |
365 | err = 0; | 366 | err = 0; |
@@ -1340,6 +1341,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) | |||
1340 | struct fuse_conn *fc = get_fuse_conn(inode); | 1341 | struct fuse_conn *fc = get_fuse_conn(inode); |
1341 | struct fuse_req *req; | 1342 | struct fuse_req *req; |
1342 | u64 attr_version = 0; | 1343 | u64 attr_version = 0; |
1344 | bool locked; | ||
1343 | 1345 | ||
1344 | if (is_bad_inode(inode)) | 1346 | if (is_bad_inode(inode)) |
1345 | return -EIO; | 1347 | return -EIO; |
@@ -1367,9 +1369,9 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) | |||
1367 | fuse_read_fill(req, file, ctx->pos, PAGE_SIZE, | 1369 | fuse_read_fill(req, file, ctx->pos, PAGE_SIZE, |
1368 | FUSE_READDIR); | 1370 | FUSE_READDIR); |
1369 | } | 1371 | } |
1370 | fuse_lock_inode(inode); | 1372 | locked = fuse_lock_inode(inode); |
1371 | fuse_request_send(fc, req); | 1373 | fuse_request_send(fc, req); |
1372 | fuse_unlock_inode(inode); | 1374 | fuse_unlock_inode(inode, locked); |
1373 | nbytes = req->out.args[0].size; | 1375 | nbytes = req->out.args[0].size; |
1374 | err = req->out.h.error; | 1376 | err = req->out.h.error; |
1375 | fuse_put_request(fc, req); | 1377 | fuse_put_request(fc, req); |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 582b1756a011..f78e9614bb5f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -975,8 +975,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, | |||
975 | 975 | ||
976 | void fuse_set_initialized(struct fuse_conn *fc); | 976 | void fuse_set_initialized(struct fuse_conn *fc); |
977 | 977 | ||
978 | void fuse_unlock_inode(struct inode *inode); | 978 | void fuse_unlock_inode(struct inode *inode, bool locked); |
979 | void fuse_lock_inode(struct inode *inode); | 979 | bool fuse_lock_inode(struct inode *inode); |
980 | 980 | ||
981 | int fuse_setxattr(struct inode *inode, const char *name, const void *value, | 981 | int fuse_setxattr(struct inode *inode, const char *name, const void *value, |
982 | size_t size, int flags); | 982 | size_t size, int flags); |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 0115c2f0a428..2dbd487390a3 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -357,15 +357,21 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid, | |||
357 | return 0; | 357 | return 0; |
358 | } | 358 | } |
359 | 359 | ||
360 | void fuse_lock_inode(struct inode *inode) | 360 | bool fuse_lock_inode(struct inode *inode) |
361 | { | 361 | { |
362 | if (!get_fuse_conn(inode)->parallel_dirops) | 362 | bool locked = false; |
363 | |||
364 | if (!get_fuse_conn(inode)->parallel_dirops) { | ||
363 | mutex_lock(&get_fuse_inode(inode)->mutex); | 365 | mutex_lock(&get_fuse_inode(inode)->mutex); |
366 | locked = true; | ||
367 | } | ||
368 | |||
369 | return locked; | ||
364 | } | 370 | } |
365 | 371 | ||
366 | void fuse_unlock_inode(struct inode *inode) | 372 | void fuse_unlock_inode(struct inode *inode, bool locked) |
367 | { | 373 | { |
368 | if (!get_fuse_conn(inode)->parallel_dirops) | 374 | if (locked) |
369 | mutex_unlock(&get_fuse_inode(inode)->mutex); | 375 | mutex_unlock(&get_fuse_inode(inode)->mutex); |
370 | } | 376 | } |
371 | 377 | ||