Re: [PATCH] vfs: fsmount: add missing mntget()

Eric Biggers
2019-07-09T23:00:35Z

On Thu, Jun 13, 2019 at 10:47:28AM +0200, Miklos Szeredi wrote:
> On Wed, Jun 12, 2019 at 11:43:13AM -0700, Eric Biggers wrote:
> > From: Eric Biggers <ebiggers@google.com>
> >
> > sys_fsmount() needs to take a reference to the new mount when adding it
> > to the anonymous mount namespace. Otherwise the filesystem can be
> > unmounted while it's still in use, as found by syzkaller.
>
> So it needs one count for the file (which dentry_open() obtains) and one for the
> attachment into the anonymous namespace. The latter one is dropped at cleanup
> time, so your patch appears to be correct at getting that ref.
>
> I wonder why such a blatant use-after-free was missed in normal testing. RCU
> delayed freeing, I guess?
>
> How about this additional sanity checking patch?
>
> Thanks,
> Miklos
>
>
> diff --git a/fs/namespace.c b/fs/namespace.c
> index b26778bdc236..c638f220805a 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -153,10 +153,10 @@ static inline void mnt_add_count(struct mount *mnt, int n)
> /*
> * vfsmount lock must be held for write
> */
> -unsigned int mnt_get_count(struct mount *mnt)
> +int mnt_get_count(struct mount *mnt)
> {
> #ifdef CONFIG_SMP
> - unsigned int count = 0;
> + int count = 0;
> int cpu;
>
> for_each_possible_cpu(cpu) {
> @@ -1140,6 +1140,8 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput);
>
> static void mntput_no_expire(struct mount *mnt)
> {
> + int count;
> +
> rcu_read_lock();
> if (likely(READ_ONCE(mnt->mnt_ns))) {
> /*
> @@ -1162,11 +1164,13 @@ static void mntput_no_expire(struct mount *mnt)
> */
> smp_mb();
> mnt_add_count(mnt, -1);
> - if (mnt_get_count(mnt)) {
> + count = mnt_get_count(mnt);
> + if (count > 0) {
> rcu_read_unlock();
> unlock_mount_hash();
> return;
> }
> + WARN_ON(count < 0);
> if (unlikely(mnt->mnt.mnt_flags & MNT_DOOMED)) {
> rcu_read_unlock();
> unlock_mount_hash();
> diff --git a/fs/pnode.h b/fs/pnode.h
> index 49a058c73e4c..26f74e092bd9 100644
> --- a/fs/pnode.h
> +++ b/fs/pnode.h
> @@ -44,7 +44,7 @@ int propagate_mount_busy(struct mount *, int);
> void propagate_mount_unlock(struct mount *);
> void mnt_release_group_id(struct mount *);
> int get_dominating_id(struct mount *mnt, const struct path *root);
> -unsigned int mnt_get_count(struct mount *mnt);
> +int mnt_get_count(struct mount *mnt);
> void mnt_set_mountpoint(struct mount *, struct mountpoint *,
> struct mount *);
> void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp,

Miklos, are you planning to send this as a formal patch?

- Eric