Re: [bug report] fuse: get rid of fuse_mount refcount

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Nov 13, 2020 at 10:01 AM Dan Carpenter <dan.carpenter@xxxxxxxxxx> wrote:
>
> Hello Miklos Szeredi,
>
> The patch 514b5e3ff45e: "fuse: get rid of fuse_mount refcount" from
> Nov 11, 2020, leads to the following static checker warning:
>
>     fs/fuse/virtio_fs.c:1451 virtio_fs_get_tree()
>     error: double free of 'fm'
>
> fs/fuse/virtio_fs.c
>   1418          if (!fs) {
>   1419                  pr_info("virtio-fs: tag <%s> not found\n", fsc->source);
>   1420                  return -EINVAL;
>   1421          }
>   1422
>   1423          err = -ENOMEM;
>   1424          fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
>   1425          if (!fc)
>   1426                  goto out_err;
>   1427
>   1428          fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
>   1429          if (!fm)
>   1430                  goto out_err;
>   1431
>   1432          fuse_conn_init(fc, fm, get_user_ns(current_user_ns()),
>   1433                         &virtio_fs_fiq_ops, fs);
>   1434          fc->release = fuse_free_conn;
>   1435          fc->delete_stale = true;
>   1436          fc->auto_submounts = true;
>   1437
>   1438          fsc->s_fs_info = fm;
>   1439          sb = sget_fc(fsc, virtio_fs_test_super, set_anon_super_fc);
>   1440          if (fsc->s_fs_info) {
>   1441                  fuse_conn_put(fc);
>   1442                  kfree(fm);
>                         ^^^^^^^^^
> Freed here
>
>   1443          }
>   1444          if (IS_ERR(sb))
>   1445                  return PTR_ERR(sb);
>   1446
>   1447          if (!sb->s_root) {
>   1448                  err = virtio_fs_fill_super(sb, fsc);
>   1449                  if (err) {
>   1450                          fuse_conn_put(fc);
>   1451                          kfree(fm);
>                                 ^^^^^^^^^
> Double free

The code is correct but tricky to prove.  So here it goes:

We set fsc->s_fs_info to non-NULL before calling sget_fc().

sget_fc() will set fsc->s_fs_info to NULL only if we return a newly
allocated superblock (i.e. sb->s_root is NULL).

In case sget_fc() returns an old superblock, then that means
sb->s_root is non-NULL.  To prove this look at grab_super() which
checks SB_BORN.  SB_BORN is set in vfs_get_tree() after a successful
call to fsc->ops->get_tree() (i.e. virtio_fs_get_tree()), which in
turn will return success only if sb->s_root has been set to non-NULL
(see virtio_fs_fill_super() and fuse_fill_super_common()).

Now we know that sget_fc() will return with

  (a negative fsc->s_fs_info AND a negative sb->s_root) OR
  (a positive fsc->s_fs_info AND a (positive sb->s_root OR an error))

So the double free can never happen.  I wouldn't expect the static
checker to figure that out.

>
>   1452                          sb->s_fs_info = NULL;
>
> I'm sort of surprised this is setting "sb->" instead of "fsc->".

The reason is that deactivate_locked_super() will call ->kill_sb (i.e.
virtio_kill_sb()) and we don't want that function to mess with the
destruction of a half baked fuse_mount structure.  So we just free it
by hand and set sb->s_fs_info to NULL.

Thanks,
Miklos



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux