aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2018-07-26 10:13:11 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-07-26 10:13:11 -0400
commit63576c13bd17848376c8ba4a98f5d5151140c4ac (patch)
tree2b33da7010c6d8a18f0736cf80a967a4ce8ad50c /fs/fuse
parente8f3bd773d22f488724dffb886a1618da85c2966 (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.c10
-rw-r--r--fs/fuse/fuse_i.h4
-rw-r--r--fs/fuse/inode.c14
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
976void fuse_set_initialized(struct fuse_conn *fc); 976void fuse_set_initialized(struct fuse_conn *fc);
977 977
978void fuse_unlock_inode(struct inode *inode); 978void fuse_unlock_inode(struct inode *inode, bool locked);
979void fuse_lock_inode(struct inode *inode); 979bool fuse_lock_inode(struct inode *inode);
980 980
981int fuse_setxattr(struct inode *inode, const char *name, const void *value, 981int 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
360void fuse_lock_inode(struct inode *inode) 360bool 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
366void fuse_unlock_inode(struct inode *inode) 372void 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