| From: Miklos Szeredi <mszeredi@redhat.com> |
| Date: Thu, 31 May 2018 12:26:10 +0200 |
| Subject: fuse: fix control dir setup and teardown |
| |
| commit 6becdb601bae2a043d7fb9762c4d48699528ea6e upstream. |
| |
| syzbot is reporting NULL pointer dereference at fuse_ctl_remove_conn() [1]. |
| Since fc->ctl_ndents is incremented by fuse_ctl_add_conn() when new_inode() |
| failed, fuse_ctl_remove_conn() reaches an inode-less dentry and tries to |
| clear d_inode(dentry)->i_private field. |
| |
| Fix by only adding the dentry to the array after being fully set up. |
| |
| When tearing down the control directory, do d_invalidate() on it to get rid |
| of any mounts that might have been added. |
| |
| [1] https://syzkaller.appspot.com/bug?id=f396d863067238959c91c0b7cfc10b163638cac6 |
| Reported-by: syzbot <syzbot+32c236387d66c4516827@syzkaller.appspotmail.com> |
| Fixes: bafa96541b25 ("[PATCH] fuse: add control filesystem") |
| Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/fuse/control.c | 13 ++++++++++--- |
| 1 file changed, 10 insertions(+), 3 deletions(-) |
| |
| --- a/fs/fuse/control.c |
| +++ b/fs/fuse/control.c |
| @@ -211,10 +211,11 @@ static struct dentry *fuse_ctl_add_dentr |
| if (!dentry) |
| return NULL; |
| |
| - fc->ctl_dentry[fc->ctl_ndents++] = dentry; |
| inode = new_inode(fuse_control_sb); |
| - if (!inode) |
| + if (!inode) { |
| + dput(dentry); |
| return NULL; |
| + } |
| |
| inode->i_ino = get_next_ino(); |
| inode->i_mode = mode; |
| @@ -228,6 +229,9 @@ static struct dentry *fuse_ctl_add_dentr |
| set_nlink(inode, nlink); |
| inode->i_private = fc; |
| d_add(dentry, inode); |
| + |
| + fc->ctl_dentry[fc->ctl_ndents++] = dentry; |
| + |
| return dentry; |
| } |
| |
| @@ -284,7 +288,10 @@ void fuse_ctl_remove_conn(struct fuse_co |
| for (i = fc->ctl_ndents - 1; i >= 0; i--) { |
| struct dentry *dentry = fc->ctl_dentry[i]; |
| dentry->d_inode->i_private = NULL; |
| - d_drop(dentry); |
| + if (!i) { |
| + /* Get rid of submounts: */ |
| + d_invalidate(dentry); |
| + } |
| dput(dentry); |
| } |
| drop_nlink(fuse_control_sb->s_root->d_inode); |