All of lore.kernel.org
 help / color / mirror / Atom feed
* [bug report] fuse: get rid of fuse_mount refcount
@ 2020-11-13  9:00 Dan Carpenter
  2020-11-13 10:08 ` Miklos Szeredi
  0 siblings, 1 reply; 2+ messages in thread
From: Dan Carpenter @ 2020-11-13  9:00 UTC (permalink / raw)
  To: mszeredi; +Cc: linux-fsdevel

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

  1452                          sb->s_fs_info = NULL;

I'm sort of surprised this is setting "sb->" instead of "fsc->".

  1453                          deactivate_locked_super(sb);
  1454                          return err;
  1455                  }
  1456  
  1457                  sb->s_flags |= SB_ACTIVE;
  1458          }

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [bug report] fuse: get rid of fuse_mount refcount
  2020-11-13  9:00 [bug report] fuse: get rid of fuse_mount refcount Dan Carpenter
@ 2020-11-13 10:08 ` Miklos Szeredi
  0 siblings, 0 replies; 2+ messages in thread
From: Miklos Szeredi @ 2020-11-13 10:08 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: Miklos Szeredi, linux-fsdevel

On Fri, Nov 13, 2020 at 10:01 AM Dan Carpenter <dan.carpenter@oracle.com> 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

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-11-13 10:09 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-11-13  9:00 [bug report] fuse: get rid of fuse_mount refcount Dan Carpenter
2020-11-13 10:08 ` Miklos Szeredi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.