2024-04-13 06:42:31

by syzbot

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

For archival purposes, forwarding an incoming command email to
[email protected].

***

Subject: Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify
Author: [email protected]

On Sat, Apr 13, 2024 at 4:41 AM Hillf Danton <[email protected]> wrote:
>
> On Thu, 11 Apr 2024 01:11:20 -0700
> > syzbot found the following issue on:
> >
> > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > git tree: linux-next
> > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1621af9d180000
>
> #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-nextgit 6ebf211bb11d
>
> --- x/fs/notify/fsnotify.c
> +++ y/fs/notify/fsnotify.c
> @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> wait_var_event(fsnotify_sb_watched_objects(sb),
> !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> - FSNOTIFY_PRIO_PRE_CONTENT));
> + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> + synchronize_srcu(&fsnotify_mark_srcu);
> kfree(sbinfo);
> }
>
> @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> {
> const struct path *path = fsnotify_data_path(data, data_type);
> struct super_block *sb = fsnotify_data_sb(data, data_type);
> - struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
> + struct fsnotify_sb_info *sbinfo;
> struct fsnotify_iter_info iter_info = {};
> struct mount *mnt = NULL;
> struct inode *inode2 = NULL;
> @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> inode2_type = FSNOTIFY_ITER_TYPE_PARENT;
> }
>
> + iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
> + sbinfo = fsnotify_sb_info(sb);
> /*
> * Optimization: srcu_read_lock() has a memory barrier which can
> * be expensive. It protects walking the *_fsnotify_marks lists.


See comment above. This kills the optimization.
It is not worth letting all the fsnotify hooks suffer the consequence
for the edge case of calling fsnotify hook during fs shutdown.

Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
is also not protected and using srcu_read_lock() there completely
nullifies the purpose of fsnotify_sb_info.

Here is a simplified fix for fsnotify_sb_error() rebased on the
pending mm fixes for this syzbot boot failure:

#syz test: https://github.com/amir73il/linux fsnotify-fixes

Jan,

I think that all the functions called from fs shutdown context
should observe that SB_ACTIVE is cleared but wasn't sure?

Thanks,
Amir.


2024-04-13 08:46:18

by Hillf Danton

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> >
> > On Thu, 11 Apr 2024 01:11:20 -0700
> > > syzbot found the following issue on:
> > >
> > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > git tree: linux-next
> > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> >
> > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> >
> > --- x/fs/notify/fsnotify.c
> > +++ y/fs/notify/fsnotify.c
> > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > wait_var_event(fsnotify_sb_watched_objects(sb),
> > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > - FSNOTIFY_PRIO_PRE_CONTENT));
> > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > + synchronize_srcu(&fsnotify_mark_srcu);
> > kfree(sbinfo);
> > }
> >
> > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > {
> > const struct path *path =3D fsnotify_data_path(data, data_type);
> > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > + struct fsnotify_sb_info *sbinfo;
> > struct fsnotify_iter_info iter_info = {};
> > struct mount *mnt =3D NULL;
> > struct inode *inode2 =3D NULL;
> > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > }
> >
> > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > + sbinfo =3D fsnotify_sb_info(sb);
> > /*
> > * Optimization: srcu_read_lock() has a memory barrier which can
> > * be expensive. It protects walking the *_fsnotify_marks lists.
>
>
> See comment above. This kills the optimization.
> It is not worth letting all the fsnotify hooks suffer the consequence
> for the edge case of calling fsnotify hook during fs shutdown.

Say nothing before reading your fix.
>
> Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> is also not protected and using srcu_read_lock() there completely
> nullifies the purpose of fsnotify_sb_info.
>
> Here is a simplified fix for fsnotify_sb_error() rebased on the
> pending mm fixes for this syzbot boot failure:
>
> #syz test: https://github.com/amir73il/linux fsnotify-fixes

Feel free to post your patch at lore because not everyone has
access to sites like github.
>
> Jan,
>
> I think that all the functions called from fs shutdown context
> should observe that SB_ACTIVE is cleared but wasn't sure?

If you composed fix based on SB_ACTIVE that is cleared in
generic_shutdown_super() with &sb->s_umount held for write,
I wonder what simpler serialization than srcu you could
find/create in fsnotify.

2024-04-13 09:32:55

by Amir Goldstein

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <[email protected]> wrote:
>
> On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > >
> > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > syzbot found the following issue on:
> > > >
> > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > git tree: linux-next
> > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > >
> > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > >
> > > --- x/fs/notify/fsnotify.c
> > > +++ y/fs/notify/fsnotify.c
> > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > kfree(sbinfo);
> > > }
> > >
> > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > {
> > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > + struct fsnotify_sb_info *sbinfo;
> > > struct fsnotify_iter_info iter_info = {};
> > > struct mount *mnt =3D NULL;
> > > struct inode *inode2 =3D NULL;
> > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > }
> > >
> > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > + sbinfo =3D fsnotify_sb_info(sb);
> > > /*
> > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > * be expensive. It protects walking the *_fsnotify_marks lists.
> >
> >
> > See comment above. This kills the optimization.
> > It is not worth letting all the fsnotify hooks suffer the consequence
> > for the edge case of calling fsnotify hook during fs shutdown.
>
> Say nothing before reading your fix.
> >
> > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > is also not protected and using srcu_read_lock() there completely
> > nullifies the purpose of fsnotify_sb_info.
> >
> > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > pending mm fixes for this syzbot boot failure:
> >
> > #syz test: https://github.com/amir73il/linux fsnotify-fixes
>
> Feel free to post your patch at lore because not everyone has
> access to sites like github.
> >
> > Jan,
> >
> > I think that all the functions called from fs shutdown context
> > should observe that SB_ACTIVE is cleared but wasn't sure?
>
> If you composed fix based on SB_ACTIVE that is cleared in
> generic_shutdown_super() with &sb->s_umount held for write,
> I wonder what simpler serialization than srcu you could
> find/create in fsnotify.

As far as I can tell there is no need for serialisation.

The problem is that fsnotify_sb_error() can be called from the
context of ->put_super() call from generic_shutdown_super().

It's true that in the repro the thread calling fsnotify_sb_error()
in the worker thread running quota deferred work from put_super()
but I think there are sufficient barriers for this worker thread to
observer the cleared SB_ACTIVE flag.

Anyway, according to syzbot, repro does not trigger the UAF
with my last fix.

To be clear, any fsnotify_sb_error() that is a result of a user operation
would be holding an active reference to sb so cannot race with
fsnotify_sb_delete(), but I am not sure that same is true for ext4
worker threads.

Jan,

You wrote that "In theory these two calls can even run in parallel
and fsnotify() can be holding fsnotify_sb_info pointer while
fsnotify_sb_delete() is freeing".

Can you give an example of this case?

Thanks,
Amir.

2024-04-13 12:05:37

by Hillf Danton

[permalink] [raw]
Subject: Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Sat, 13 Apr 2024 12:32:32 +0300 Amir Goldstein
> On Sat, Apr 13, 2024 at 11:45=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> >
> > If you composed fix based on SB_ACTIVE that is cleared in
> > generic_shutdown_super() with &sb->s_umount held for write,
> > I wonder what simpler serialization than srcu you could
> > find/create in fsnotify.
>
> As far as I can tell there is no need for serialisation.
>
> The problem is that fsnotify_sb_error() can be called from the
> context of ->put_super() call from generic_shutdown_super().
>
> It's true that in the repro the thread calling fsnotify_sb_error()
> in the worker thread running quota deferred work from put_super()
> but I think there are sufficient barriers for this worker thread to
> observer the cleared SB_ACTIVE flag.
>
do_exit quota_release_workfn
--- ---
cleanup_mnt() ext4_release_dquot()
__super_lock_excl(s); __ext4_error()
deactivate_locked_super(s); fsnotify_sb_error()
ext4_kill_sb()
kill_block_super()
generic_shutdown_super()
if (!(sb->s_flags & SB_ACTIVE))
return;
sb->s_flags &= ~SB_ACTIVE;
fsnotify_sb_delete()
fsnotify()

Because of no sync like taking &sb->s_umount in the worker context,
checking SB_ACTIVE added in your fix is unable to close the race.

> Anyway, according to syzbot, repro does not trigger the UAF
> with my last fix.
>
Note: testing is done by a robot and is best-effort only.

2024-04-15 14:11:59

by Jan Kara

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Sat 13-04-24 12:32:32, Amir Goldstein wrote:
> On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <[email protected]> wrote:
> > On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > > syzbot found the following issue on:
> > > > >
> > > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > > git tree: linux-next
> > > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > > >
> > > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > > >
> > > > --- x/fs/notify/fsnotify.c
> > > > +++ y/fs/notify/fsnotify.c
> > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > kfree(sbinfo);
> > > > }
> > > >
> > > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > > {
> > > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > > + struct fsnotify_sb_info *sbinfo;
> > > > struct fsnotify_iter_info iter_info = {};
> > > > struct mount *mnt =3D NULL;
> > > > struct inode *inode2 =3D NULL;
> > > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > > }
> > > >
> > > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > > + sbinfo =3D fsnotify_sb_info(sb);
> > > > /*
> > > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > > * be expensive. It protects walking the *_fsnotify_marks lists.
> > >
> > >
> > > See comment above. This kills the optimization.
> > > It is not worth letting all the fsnotify hooks suffer the consequence
> > > for the edge case of calling fsnotify hook during fs shutdown.
> >
> > Say nothing before reading your fix.
> > >
> > > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > > is also not protected and using srcu_read_lock() there completely
> > > nullifies the purpose of fsnotify_sb_info.
> > >
> > > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > > pending mm fixes for this syzbot boot failure:
> > >
> > > #syz test: https://github.com/amir73il/linux fsnotify-fixes
> >
> > Feel free to post your patch at lore because not everyone has
> > access to sites like github.
> > >
> > > Jan,
> > >
> > > I think that all the functions called from fs shutdown context
> > > should observe that SB_ACTIVE is cleared but wasn't sure?
> >
> > If you composed fix based on SB_ACTIVE that is cleared in
> > generic_shutdown_super() with &sb->s_umount held for write,
> > I wonder what simpler serialization than srcu you could
> > find/create in fsnotify.
>
> As far as I can tell there is no need for serialisation.
>
> The problem is that fsnotify_sb_error() can be called from the
> context of ->put_super() call from generic_shutdown_super().
>
> It's true that in the repro the thread calling fsnotify_sb_error()
> in the worker thread running quota deferred work from put_super()
> but I think there are sufficient barriers for this worker thread to
> observer the cleared SB_ACTIVE flag.
>
> Anyway, according to syzbot, repro does not trigger the UAF
> with my last fix.
>
> To be clear, any fsnotify_sb_error() that is a result of a user operation
> would be holding an active reference to sb so cannot race with
> fsnotify_sb_delete(), but I am not sure that same is true for ext4
> worker threads.
>
> Jan,
>
> You wrote that "In theory these two calls can even run in parallel
> and fsnotify() can be holding fsnotify_sb_info pointer while
> fsnotify_sb_delete() is freeing".
>
> Can you give an example of this case?

Yeah, basically what Hilf writes:

Task 1 Task 2
umount() some delayed work, transaction
commit, whatever is still running
before ext4_put_super() completes
... ext4_error()
fsnotify_sb_error()
fsnotify()
fetches fsnotify_sb_info
generic_shutdown_super()
fsnotify_sb_delete()
frees fsnotify_sb_info

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2024-04-15 14:57:07

by Amir Goldstein

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Mon, Apr 15, 2024 at 5:03 PM Jan Kara <[email protected]> wrote:
>
> On Sat 13-04-24 12:32:32, Amir Goldstein wrote:
> > On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <[email protected]> wrote:
> > > On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > > > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > > > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > > > syzbot found the following issue on:
> > > > > >
> > > > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > > > git tree: linux-next
> > > > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > > > >
> > > > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > > > >
> > > > > --- x/fs/notify/fsnotify.c
> > > > > +++ y/fs/notify/fsnotify.c
> > > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > > kfree(sbinfo);
> > > > > }
> > > > >
> > > > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > > > {
> > > > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > > > + struct fsnotify_sb_info *sbinfo;
> > > > > struct fsnotify_iter_info iter_info = {};
> > > > > struct mount *mnt =3D NULL;
> > > > > struct inode *inode2 =3D NULL;
> > > > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > > > }
> > > > >
> > > > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > > > + sbinfo =3D fsnotify_sb_info(sb);
> > > > > /*
> > > > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > > > * be expensive. It protects walking the *_fsnotify_marks lists.
> > > >
> > > >
> > > > See comment above. This kills the optimization.
> > > > It is not worth letting all the fsnotify hooks suffer the consequence
> > > > for the edge case of calling fsnotify hook during fs shutdown.
> > >
> > > Say nothing before reading your fix.
> > > >
> > > > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > > > is also not protected and using srcu_read_lock() there completely
> > > > nullifies the purpose of fsnotify_sb_info.
> > > >
> > > > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > > > pending mm fixes for this syzbot boot failure:
> > > >
> > > > #syz test: https://github.com/amir73il/linux fsnotify-fixes
> > >
> > > Feel free to post your patch at lore because not everyone has
> > > access to sites like github.
> > > >
> > > > Jan,
> > > >
> > > > I think that all the functions called from fs shutdown context
> > > > should observe that SB_ACTIVE is cleared but wasn't sure?
> > >
> > > If you composed fix based on SB_ACTIVE that is cleared in
> > > generic_shutdown_super() with &sb->s_umount held for write,
> > > I wonder what simpler serialization than srcu you could
> > > find/create in fsnotify.
> >
> > As far as I can tell there is no need for serialisation.
> >
> > The problem is that fsnotify_sb_error() can be called from the
> > context of ->put_super() call from generic_shutdown_super().
> >
> > It's true that in the repro the thread calling fsnotify_sb_error()
> > in the worker thread running quota deferred work from put_super()
> > but I think there are sufficient barriers for this worker thread to
> > observer the cleared SB_ACTIVE flag.
> >
> > Anyway, according to syzbot, repro does not trigger the UAF
> > with my last fix.
> >
> > To be clear, any fsnotify_sb_error() that is a result of a user operation
> > would be holding an active reference to sb so cannot race with
> > fsnotify_sb_delete(), but I am not sure that same is true for ext4
> > worker threads.
> >
> > Jan,
> >
> > You wrote that "In theory these two calls can even run in parallel
> > and fsnotify() can be holding fsnotify_sb_info pointer while
> > fsnotify_sb_delete() is freeing".
> >
> > Can you give an example of this case?
>
> Yeah, basically what Hilf writes:
>
> Task 1 Task 2
> umount() some delayed work, transaction
> commit, whatever is still running
> before ext4_put_super() completes
> ... ext4_error()
> fsnotify_sb_error()
> fsnotify()
> fetches fsnotify_sb_info
> generic_shutdown_super()
> fsnotify_sb_delete()
> frees fsnotify_sb_info

OK, so what do you say about Hillf's fix patch?

Maybe it is ok to let go of the optimization in fsnotify(), considering
that we now have stronger optimizations in the inline hooks and
in __fsnotify_parent()?

I think that Hillf's patch is missing setting s_fsnotify_info to NULL?

@@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
wait_var_event(fsnotify_sb_watched_objects(sb),
!atomic_long_read(fsnotify_sb_watched_objects(sb)));
WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
+ WRITE_ONCE(sb->s_fsnotify_info, NULL);
+ synchronize_srcu(&fsnotify_mark_srcu);
kfree(sbinfo);
}

Thanks,
Amir.

2024-04-16 10:48:18

by Amir Goldstein

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Mon, Apr 15, 2024 at 5:47 PM Amir Goldstein <[email protected]> wrote:
>
> On Mon, Apr 15, 2024 at 5:03 PM Jan Kara <[email protected]> wrote:
> >
> > On Sat 13-04-24 12:32:32, Amir Goldstein wrote:
> > > On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <[email protected]> wrote:
> > > > On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > > > > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > > > > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > > > > syzbot found the following issue on:
> > > > > > >
> > > > > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > > > > git tree: linux-next
> > > > > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > > > > >
> > > > > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > > > > >
> > > > > > --- x/fs/notify/fsnotify.c
> > > > > > +++ y/fs/notify/fsnotify.c
> > > > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > > > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > > > kfree(sbinfo);
> > > > > > }
> > > > > >
> > > > > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > {
> > > > > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > > > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > > > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > > > > + struct fsnotify_sb_info *sbinfo;
> > > > > > struct fsnotify_iter_info iter_info = {};
> > > > > > struct mount *mnt =3D NULL;
> > > > > > struct inode *inode2 =3D NULL;
> > > > > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > > > > }
> > > > > >
> > > > > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > > > > + sbinfo =3D fsnotify_sb_info(sb);
> > > > > > /*
> > > > > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > > > > * be expensive. It protects walking the *_fsnotify_marks lists.
> > > > >
> > > > >
> > > > > See comment above. This kills the optimization.
> > > > > It is not worth letting all the fsnotify hooks suffer the consequence
> > > > > for the edge case of calling fsnotify hook during fs shutdown.
> > > >
> > > > Say nothing before reading your fix.
> > > > >
> > > > > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > > > > is also not protected and using srcu_read_lock() there completely
> > > > > nullifies the purpose of fsnotify_sb_info.
> > > > >
> > > > > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > > > > pending mm fixes for this syzbot boot failure:
> > > > >
> > > > > #syz test: https://github.com/amir73il/linux fsnotify-fixes
> > > >
> > > > Feel free to post your patch at lore because not everyone has
> > > > access to sites like github.
> > > > >
> > > > > Jan,
> > > > >
> > > > > I think that all the functions called from fs shutdown context
> > > > > should observe that SB_ACTIVE is cleared but wasn't sure?
> > > >
> > > > If you composed fix based on SB_ACTIVE that is cleared in
> > > > generic_shutdown_super() with &sb->s_umount held for write,
> > > > I wonder what simpler serialization than srcu you could
> > > > find/create in fsnotify.
> > >
> > > As far as I can tell there is no need for serialisation.
> > >
> > > The problem is that fsnotify_sb_error() can be called from the
> > > context of ->put_super() call from generic_shutdown_super().
> > >
> > > It's true that in the repro the thread calling fsnotify_sb_error()
> > > in the worker thread running quota deferred work from put_super()
> > > but I think there are sufficient barriers for this worker thread to
> > > observer the cleared SB_ACTIVE flag.
> > >
> > > Anyway, according to syzbot, repro does not trigger the UAF
> > > with my last fix.
> > >
> > > To be clear, any fsnotify_sb_error() that is a result of a user operation
> > > would be holding an active reference to sb so cannot race with
> > > fsnotify_sb_delete(), but I am not sure that same is true for ext4
> > > worker threads.
> > >
> > > Jan,
> > >
> > > You wrote that "In theory these two calls can even run in parallel
> > > and fsnotify() can be holding fsnotify_sb_info pointer while
> > > fsnotify_sb_delete() is freeing".
> > >
> > > Can you give an example of this case?
> >
> > Yeah, basically what Hilf writes:
> >
> > Task 1 Task 2
> > umount() some delayed work, transaction
> > commit, whatever is still running
> > before ext4_put_super() completes
> > ... ext4_error()
> > fsnotify_sb_error()
> > fsnotify()
> > fetches fsnotify_sb_info
> > generic_shutdown_super()
> > fsnotify_sb_delete()
> > frees fsnotify_sb_info
>
> OK, so what do you say about Hillf's fix patch?
>
> Maybe it is ok to let go of the optimization in fsnotify(), considering
> that we now have stronger optimizations in the inline hooks and
> in __fsnotify_parent()?
>
> I think that Hillf's patch is missing setting s_fsnotify_info to NULL?
>
> @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> wait_var_event(fsnotify_sb_watched_objects(sb),
> !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> + WRITE_ONCE(sb->s_fsnotify_info, NULL);
> + synchronize_srcu(&fsnotify_mark_srcu);
> kfree(sbinfo);
> }
>

I reworked Hillf's patch so it won't break the optimizations for
the common case and added setting s_fsnotify_info to NULL (attached).

Let's see what syzbot has to say:

#syz test: https://github.com/amir73il/linux fsnotify-fixes

Thanks,
Amir.


Attachments:
0001-fsnotify-fix-UAF-from-FS_ERROR-event-on-a-shutting-d.patch (3.27 kB)

2024-04-16 13:22:41

by Jan Kara

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Mon 15-04-24 17:47:45, Amir Goldstein wrote:
> On Mon, Apr 15, 2024 at 5:03 PM Jan Kara <[email protected]> wrote:
> >
> > On Sat 13-04-24 12:32:32, Amir Goldstein wrote:
> > > On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <[email protected]> wrote:
> > > > On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > > > > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > > > > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > > > > syzbot found the following issue on:
> > > > > > >
> > > > > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > > > > git tree: linux-next
> > > > > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > > > > >
> > > > > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > > > > >
> > > > > > --- x/fs/notify/fsnotify.c
> > > > > > +++ y/fs/notify/fsnotify.c
> > > > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > > > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > > > kfree(sbinfo);
> > > > > > }
> > > > > >
> > > > > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > {
> > > > > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > > > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > > > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > > > > + struct fsnotify_sb_info *sbinfo;
> > > > > > struct fsnotify_iter_info iter_info = {};
> > > > > > struct mount *mnt =3D NULL;
> > > > > > struct inode *inode2 =3D NULL;
> > > > > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > > > > }
> > > > > >
> > > > > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > > > > + sbinfo =3D fsnotify_sb_info(sb);
> > > > > > /*
> > > > > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > > > > * be expensive. It protects walking the *_fsnotify_marks lists.
> > > > >
> > > > >
> > > > > See comment above. This kills the optimization.
> > > > > It is not worth letting all the fsnotify hooks suffer the consequence
> > > > > for the edge case of calling fsnotify hook during fs shutdown.
> > > >
> > > > Say nothing before reading your fix.
> > > > >
> > > > > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > > > > is also not protected and using srcu_read_lock() there completely
> > > > > nullifies the purpose of fsnotify_sb_info.
> > > > >
> > > > > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > > > > pending mm fixes for this syzbot boot failure:
> > > > >
> > > > > #syz test: https://github.com/amir73il/linux fsnotify-fixes
> > > >
> > > > Feel free to post your patch at lore because not everyone has
> > > > access to sites like github.
> > > > >
> > > > > Jan,
> > > > >
> > > > > I think that all the functions called from fs shutdown context
> > > > > should observe that SB_ACTIVE is cleared but wasn't sure?
> > > >
> > > > If you composed fix based on SB_ACTIVE that is cleared in
> > > > generic_shutdown_super() with &sb->s_umount held for write,
> > > > I wonder what simpler serialization than srcu you could
> > > > find/create in fsnotify.
> > >
> > > As far as I can tell there is no need for serialisation.
> > >
> > > The problem is that fsnotify_sb_error() can be called from the
> > > context of ->put_super() call from generic_shutdown_super().
> > >
> > > It's true that in the repro the thread calling fsnotify_sb_error()
> > > in the worker thread running quota deferred work from put_super()
> > > but I think there are sufficient barriers for this worker thread to
> > > observer the cleared SB_ACTIVE flag.
> > >
> > > Anyway, according to syzbot, repro does not trigger the UAF
> > > with my last fix.
> > >
> > > To be clear, any fsnotify_sb_error() that is a result of a user operation
> > > would be holding an active reference to sb so cannot race with
> > > fsnotify_sb_delete(), but I am not sure that same is true for ext4
> > > worker threads.
> > >
> > > Jan,
> > >
> > > You wrote that "In theory these two calls can even run in parallel
> > > and fsnotify() can be holding fsnotify_sb_info pointer while
> > > fsnotify_sb_delete() is freeing".
> > >
> > > Can you give an example of this case?
> >
> > Yeah, basically what Hilf writes:
> >
> > Task 1 Task 2
> > umount() some delayed work, transaction
> > commit, whatever is still running
> > before ext4_put_super() completes
> > ... ext4_error()
> > fsnotify_sb_error()
> > fsnotify()
> > fetches fsnotify_sb_info
> > generic_shutdown_super()
> > fsnotify_sb_delete()
> > frees fsnotify_sb_info
>
> OK, so what do you say about Hillf's fix patch?
>
> Maybe it is ok to let go of the optimization in fsnotify(), considering
> that we now have stronger optimizations in the inline hooks and
> in __fsnotify_parent()?
>
> I think that Hillf's patch is missing setting s_fsnotify_info to NULL?
>
> @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> wait_var_event(fsnotify_sb_watched_objects(sb),
> !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> + WRITE_ONCE(sb->s_fsnotify_info, NULL);
> + synchronize_srcu(&fsnotify_mark_srcu);
> kfree(sbinfo);
> }

So I had a look into this. Yes, something like this should work. We'll see
whether synchronize_srcu() won't slow down umount too much. If someone will
complain, we'll have to find a better solution.

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2024-04-16 16:27:46

by Amir Goldstein

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Tue, Apr 16, 2024 at 4:22 PM Jan Kara <[email protected]> wrote:
>
> On Mon 15-04-24 17:47:45, Amir Goldstein wrote:
> > On Mon, Apr 15, 2024 at 5:03 PM Jan Kara <[email protected]> wrote:
> > >
> > > On Sat 13-04-24 12:32:32, Amir Goldstein wrote:
> > > > On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <hdanton@sinacom> wrote:
> > > > > On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > > > > > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > > > > > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > > > > > syzbot found the following issue on:
> > > > > > > >
> > > > > > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > > > > > git tree: linux-next
> > > > > > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > > > > > >
> > > > > > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > > > > > >
> > > > > > > --- x/fs/notify/fsnotify.c
> > > > > > > +++ y/fs/notify/fsnotify.c
> > > > > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > > > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > > > > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > > > > kfree(sbinfo);
> > > > > > > }
> > > > > > >
> > > > > > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > > {
> > > > > > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > > > > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > > > > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > > > > > + struct fsnotify_sb_info *sbinfo;
> > > > > > > struct fsnotify_iter_info iter_info = {};
> > > > > > > struct mount *mnt =3D NULL;
> > > > > > > struct inode *inode2 =3D NULL;
> > > > > > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > > > > > }
> > > > > > >
> > > > > > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > > > > > + sbinfo =3D fsnotify_sb_info(sb);
> > > > > > > /*
> > > > > > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > > > > > * be expensive. It protects walking the *_fsnotify_marks lists.
> > > > > >
> > > > > >
> > > > > > See comment above. This kills the optimization.
> > > > > > It is not worth letting all the fsnotify hooks suffer the consequence
> > > > > > for the edge case of calling fsnotify hook during fs shutdown.
> > > > >
> > > > > Say nothing before reading your fix.
> > > > > >
> > > > > > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > > > > > is also not protected and using srcu_read_lock() there completely
> > > > > > nullifies the purpose of fsnotify_sb_info.
> > > > > >
> > > > > > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > > > > > pending mm fixes for this syzbot boot failure:
> > > > > >
> > > > > > #syz test: https://github.com/amir73il/linux fsnotify-fixes
> > > > >
> > > > > Feel free to post your patch at lore because not everyone has
> > > > > access to sites like github.
> > > > > >
> > > > > > Jan,
> > > > > >
> > > > > > I think that all the functions called from fs shutdown context
> > > > > > should observe that SB_ACTIVE is cleared but wasn't sure?
> > > > >
> > > > > If you composed fix based on SB_ACTIVE that is cleared in
> > > > > generic_shutdown_super() with &sb->s_umount held for write,
> > > > > I wonder what simpler serialization than srcu you could
> > > > > find/create in fsnotify.
> > > >
> > > > As far as I can tell there is no need for serialisation.
> > > >
> > > > The problem is that fsnotify_sb_error() can be called from the
> > > > context of ->put_super() call from generic_shutdown_super().
> > > >
> > > > It's true that in the repro the thread calling fsnotify_sb_error()
> > > > in the worker thread running quota deferred work from put_super()
> > > > but I think there are sufficient barriers for this worker thread to
> > > > observer the cleared SB_ACTIVE flag.
> > > >
> > > > Anyway, according to syzbot, repro does not trigger the UAF
> > > > with my last fix.
> > > >
> > > > To be clear, any fsnotify_sb_error() that is a result of a user operation
> > > > would be holding an active reference to sb so cannot race with
> > > > fsnotify_sb_delete(), but I am not sure that same is true for ext4
> > > > worker threads.
> > > >
> > > > Jan,
> > > >
> > > > You wrote that "In theory these two calls can even run in parallel
> > > > and fsnotify() can be holding fsnotify_sb_info pointer while
> > > > fsnotify_sb_delete() is freeing".
> > > >
> > > > Can you give an example of this case?
> > >
> > > Yeah, basically what Hilf writes:
> > >
> > > Task 1 Task 2
> > > umount() some delayed work, transaction
> > > commit, whatever is still running
> > > before ext4_put_super() completes
> > > ... ext4_error()
> > > fsnotify_sb_error()
> > > fsnotify()
> > > fetches fsnotify_sb_info
> > > generic_shutdown_super()
> > > fsnotify_sb_delete()
> > > frees fsnotify_sb_info
> >
> > OK, so what do you say about Hillf's fix patch?
> >
> > Maybe it is ok to let go of the optimization in fsnotify(), considering
> > that we now have stronger optimizations in the inline hooks and
> > in __fsnotify_parent()?
> >
> > I think that Hillf's patch is missing setting s_fsnotify_info to NULL?
> >
> > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > wait_var_event(fsnotify_sb_watched_objects(sb),
> > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > + WRITE_ONCE(sb->s_fsnotify_info, NULL);
> > + synchronize_srcu(&fsnotify_mark_srcu);
> > kfree(sbinfo);
> > }
>
> So I had a look into this. Yes, something like this should work. We'll see
> whether synchronize_srcu() won't slow down umount too much. If someone will
> complain, we'll have to find a better solution.
>

Actually, kfree_rcu(sbinfo) may be enough.
We do not actually access sbinfo during mark iteration and
event handling, we only access it to get to the sb connector.

Something like the attached patch?

Thanks,
Amir.


Attachments:
v2-0001-fsnotify-fix-UAF-from-FS_ERROR-event-on-a-shuttin.patch (3.71 kB)

2024-04-16 17:32:37

by Jan Kara

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Tue 16-04-24 19:27:05, Amir Goldstein wrote:
> On Tue, Apr 16, 2024 at 4:22 PM Jan Kara <[email protected]> wrote:
> >
> > On Mon 15-04-24 17:47:45, Amir Goldstein wrote:
> > > On Mon, Apr 15, 2024 at 5:03 PM Jan Kara <[email protected]> wrote:
> > > >
> > > > On Sat 13-04-24 12:32:32, Amir Goldstein wrote:
> > > > > On Sat, Apr 13, 2024 at 11:45 AM Hillf Danton <[email protected]> wrote:
> > > > > > On Fri, 12 Apr 2024 23:42:19 -0700 Amir Goldstein
> > > > > > > On Sat, Apr 13, 2024 at 4:41=E2=80=AFAM Hillf Danton <[email protected]> wrote:
> > > > > > > > On Thu, 11 Apr 2024 01:11:20 -0700
> > > > > > > > > syzbot found the following issue on:
> > > > > > > > >
> > > > > > > > > HEAD commit: 6ebf211bb11d Add linux-next specific files for 20240410
> > > > > > > > > git tree: linux-next
> > > > > > > > > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=3D1621af9d180000
> > > > > > > >
> > > > > > > > #syz test https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 6ebf211bb11d
> > > > > > > >
> > > > > > > > --- x/fs/notify/fsnotify.c
> > > > > > > > +++ y/fs/notify/fsnotify.c
> > > > > > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > > > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > > > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > > > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > > > > > - WARN_ON(fsnotify_sb_has_priority_watchers(sb,
> > > > > > > > - FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > > > + WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_PRE_CONTENT));
> > > > > > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > > > > > kfree(sbinfo);
> > > > > > > > }
> > > > > > > >
> > > > > > > > @@ -499,7 +499,7 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > > > {
> > > > > > > > const struct path *path =3D fsnotify_data_path(data, data_type);
> > > > > > > > struct super_block *sb =3D fsnotify_data_sb(data, data_type);
> > > > > > > > - struct fsnotify_sb_info *sbinfo =3D fsnotify_sb_info(sb);
> > > > > > > > + struct fsnotify_sb_info *sbinfo;
> > > > > > > > struct fsnotify_iter_info iter_info = {};
> > > > > > > > struct mount *mnt =3D NULL;
> > > > > > > > struct inode *inode2 =3D NULL;
> > > > > > > > @@ -529,6 +529,8 @@ int fsnotify(__u32 mask, const void *dat
> > > > > > > > inode2_type =3D FSNOTIFY_ITER_TYPE_PARENT;
> > > > > > > > }
> > > > > > > >
> > > > > > > > + iter_info.srcu_idx =3D srcu_read_lock(&fsnotify_mark_srcu);
> > > > > > > > + sbinfo =3D fsnotify_sb_info(sb);
> > > > > > > > /*
> > > > > > > > * Optimization: srcu_read_lock() has a memory barrier which can
> > > > > > > > * be expensive. It protects walking the *_fsnotify_marks lists.
> > > > > > >
> > > > > > >
> > > > > > > See comment above. This kills the optimization.
> > > > > > > It is not worth letting all the fsnotify hooks suffer the consequence
> > > > > > > for the edge case of calling fsnotify hook during fs shutdown.
> > > > > >
> > > > > > Say nothing before reading your fix.
> > > > > > >
> > > > > > > Also, fsnotify_sb_info(sb) in fsnotify_sb_has_priority_watchers()
> > > > > > > is also not protected and using srcu_read_lock() there completely
> > > > > > > nullifies the purpose of fsnotify_sb_info.
> > > > > > >
> > > > > > > Here is a simplified fix for fsnotify_sb_error() rebased on the
> > > > > > > pending mm fixes for this syzbot boot failure:
> > > > > > >
> > > > > > > #syz test: https://github.com/amir73il/linux fsnotify-fixes
> > > > > >
> > > > > > Feel free to post your patch at lore because not everyone has
> > > > > > access to sites like github.
> > > > > > >
> > > > > > > Jan,
> > > > > > >
> > > > > > > I think that all the functions called from fs shutdown context
> > > > > > > should observe that SB_ACTIVE is cleared but wasn't sure?
> > > > > >
> > > > > > If you composed fix based on SB_ACTIVE that is cleared in
> > > > > > generic_shutdown_super() with &sb->s_umount held for write,
> > > > > > I wonder what simpler serialization than srcu you could
> > > > > > find/create in fsnotify.
> > > > >
> > > > > As far as I can tell there is no need for serialisation.
> > > > >
> > > > > The problem is that fsnotify_sb_error() can be called from the
> > > > > context of ->put_super() call from generic_shutdown_super().
> > > > >
> > > > > It's true that in the repro the thread calling fsnotify_sb_error()
> > > > > in the worker thread running quota deferred work from put_super()
> > > > > but I think there are sufficient barriers for this worker thread to
> > > > > observer the cleared SB_ACTIVE flag.
> > > > >
> > > > > Anyway, according to syzbot, repro does not trigger the UAF
> > > > > with my last fix.
> > > > >
> > > > > To be clear, any fsnotify_sb_error() that is a result of a user operation
> > > > > would be holding an active reference to sb so cannot race with
> > > > > fsnotify_sb_delete(), but I am not sure that same is true for ext4
> > > > > worker threads.
> > > > >
> > > > > Jan,
> > > > >
> > > > > You wrote that "In theory these two calls can even run in parallel
> > > > > and fsnotify() can be holding fsnotify_sb_info pointer while
> > > > > fsnotify_sb_delete() is freeing".
> > > > >
> > > > > Can you give an example of this case?
> > > >
> > > > Yeah, basically what Hilf writes:
> > > >
> > > > Task 1 Task 2
> > > > umount() some delayed work, transaction
> > > > commit, whatever is still running
> > > > before ext4_put_super() completes
> > > > ... ext4_error()
> > > > fsnotify_sb_error()
> > > > fsnotify()
> > > > fetches fsnotify_sb_info
> > > > generic_shutdown_super()
> > > > fsnotify_sb_delete()
> > > > frees fsnotify_sb_info
> > >
> > > OK, so what do you say about Hillf's fix patch?
> > >
> > > Maybe it is ok to let go of the optimization in fsnotify(), considering
> > > that we now have stronger optimizations in the inline hooks and
> > > in __fsnotify_parent()?
> > >
> > > I think that Hillf's patch is missing setting s_fsnotify_info to NULL?
> > >
> > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > + WRITE_ONCE(sb->s_fsnotify_info, NULL);
> > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > kfree(sbinfo);
> > > }
> >
> > So I had a look into this. Yes, something like this should work. We'll see
> > whether synchronize_srcu() won't slow down umount too much. If someone will
> > complain, we'll have to find a better solution.
> >
>
> Actually, kfree_rcu(sbinfo) may be enough.
> We do not actually access sbinfo during mark iteration and
> event handling, we only access it to get to the sb connector.
>
> Something like the attached patch?

Hum, thinking about this some more - what if we just freed sb_info from
destroy_super_work()? By then we definitely are not getting fsnotify()
calls for the superblock so all the problems are solved.


Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2024-04-16 18:00:20

by Amir Goldstein

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

> > > > Maybe it is ok to let go of the optimization in fsnotify(), considering
> > > > that we now have stronger optimizations in the inline hooks and
> > > > in __fsnotify_parent()?
> > > >
> > > > I think that Hillf's patch is missing setting s_fsnotify_info to NULL?
> > > >
> > > > @@ -101,8 +101,8 @@ void fsnotify_sb_delete(struct super_blo
> > > > wait_var_event(fsnotify_sb_watched_objects(sb),
> > > > !atomic_long_read(fsnotify_sb_watched_objects(sb)));
> > > > WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
> > > > + WRITE_ONCE(sb->s_fsnotify_info, NULL);
> > > > + synchronize_srcu(&fsnotify_mark_srcu);
> > > > kfree(sbinfo);
> > > > }
> > >
> > > So I had a look into this. Yes, something like this should work. We'll see
> > > whether synchronize_srcu() won't slow down umount too much. If someone will
> > > complain, we'll have to find a better solution.
> > >
> >
> > Actually, kfree_rcu(sbinfo) may be enough.
> > We do not actually access sbinfo during mark iteration and
> > event handling, we only access it to get to the sb connector.
> >
> > Something like the attached patch?
>
> Hum, thinking about this some more - what if we just freed sb_info from
> destroy_super_work()? By then we definitely are not getting fsnotify()
> calls for the superblock so all the problems are solved.
>

Considering that this is the solution for security_sb_free()
I don't see why not have fsnotify_sb_free().
I'll prepare a patch.

Thanks!
Amir.

2024-04-16 22:51:28

by Hillf Danton

[permalink] [raw]
Subject: Re: [syzbot] Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

On Tue, 16 Apr 2024 19:32:11 +0200 Jan Kara <[email protected]>
>
> Hum, thinking about this some more - what if we just freed sb_info from
> destroy_super_work()? By then we definitely are not getting fsnotify()
> calls for the superblock so all the problems are solved.
>
Sounds better :)

2024-04-17 02:03:15

by syzbot

[permalink] [raw]
Subject: Re: [syzbot] [ext4?] KASAN: slab-use-after-free Read in fsnotify

Hello,

syzbot tried to test the proposed patch but the build/boot failed:

failed to apply patch:
checking file fs/notify/fsnotify.c
Hunk #1 FAILED at 103.
Hunk #2 succeeded at 510 (offset 4 lines).
Hunk #3 succeeded at 540 (offset 4 lines).
Hunk #4 succeeded at 569 (offset 4 lines).
1 out of 4 hunks FAILED
checking file include/linux/fsnotify_backend.h



Tested on:

commit: 2f012b22 fsnotify: fix UAF from FS_ERROR event on a sh..
git tree: https://github.com/amir73il/linux fsnotify-fixes
kernel config: https://syzkaller.appspot.com/x/.config?x=16ca158ef7e08662
dashboard link: https://syzkaller.appspot.com/bug?extid=5e3f9b2a67b45f16d4e6
compiler:
patch: https://syzkaller.appspot.com/x/patch.diff?x=10103657180000