diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2018-05-31 06:26:10 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2018-05-31 06:26:10 -0400 |
commit | 6becdb601bae2a043d7fb9762c4d48699528ea6e (patch) | |
tree | 7d136a357c374a82bb8e329811d84af96a920a36 /fs/fuse | |
parent | 8a301eb16d99983a4961f884690ec97b92e7dcfe (diff) |
fuse: fix control dir setup and teardown
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")
Cc: <stable@vger.kernel.org> # v2.6.18
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/control.c | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/fs/fuse/control.c b/fs/fuse/control.c index 78fb7a07c5ca..0b694655d988 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c | |||
@@ -211,10 +211,11 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | |||
211 | if (!dentry) | 211 | if (!dentry) |
212 | return NULL; | 212 | return NULL; |
213 | 213 | ||
214 | fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
215 | inode = new_inode(fuse_control_sb); | 214 | inode = new_inode(fuse_control_sb); |
216 | if (!inode) | 215 | if (!inode) { |
216 | dput(dentry); | ||
217 | return NULL; | 217 | return NULL; |
218 | } | ||
218 | 219 | ||
219 | inode->i_ino = get_next_ino(); | 220 | inode->i_ino = get_next_ino(); |
220 | inode->i_mode = mode; | 221 | inode->i_mode = mode; |
@@ -228,6 +229,9 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | |||
228 | set_nlink(inode, nlink); | 229 | set_nlink(inode, nlink); |
229 | inode->i_private = fc; | 230 | inode->i_private = fc; |
230 | d_add(dentry, inode); | 231 | d_add(dentry, inode); |
232 | |||
233 | fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
234 | |||
231 | return dentry; | 235 | return dentry; |
232 | } | 236 | } |
233 | 237 | ||
@@ -284,7 +288,10 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc) | |||
284 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { | 288 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { |
285 | struct dentry *dentry = fc->ctl_dentry[i]; | 289 | struct dentry *dentry = fc->ctl_dentry[i]; |
286 | d_inode(dentry)->i_private = NULL; | 290 | d_inode(dentry)->i_private = NULL; |
287 | d_drop(dentry); | 291 | if (!i) { |
292 | /* Get rid of submounts: */ | ||
293 | d_invalidate(dentry); | ||
294 | } | ||
288 | dput(dentry); | 295 | dput(dentry); |
289 | } | 296 | } |
290 | drop_nlink(d_inode(fuse_control_sb->s_root)); | 297 | drop_nlink(d_inode(fuse_control_sb->s_root)); |