diff mbox series

kasan: fix quarantine conflicting with init_on_free

Message ID a746b5baebbf79f8160c1fe09d6f8a5ab7bde1d7.1640017993.git.andreyknvl@google.com (mailing list archive)
State New
Headers show
Series kasan: fix quarantine conflicting with init_on_free | expand

Commit Message

andrey.konovalov@linux.dev Dec. 20, 2021, 4:37 p.m. UTC
From: Andrey Konovalov <andreyknvl@google.com>

KASAN's quarantine might save its metadata inside freed objects. As
this happens after the memory is zeroed by the slab allocator when
init_on_free is enabled, the memory coming out of quarantine is not
properly zeroed.

This causes lib/test_meminit.c tests to fail with Generic KASAN.

Zero the metadata when the object is removed from quarantine.

Fixes: 6471384af2a6 ("mm: security: introduce init_on_alloc=1 and init_on_free=1 boot options")
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
---
 mm/kasan/quarantine.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

Comments

Marco Elver Dec. 20, 2021, 5:07 p.m. UTC | #1
On Mon, 20 Dec 2021 at 17:37, <andrey.konovalov@linux.dev> wrote:
>
> From: Andrey Konovalov <andreyknvl@google.com>
>
> KASAN's quarantine might save its metadata inside freed objects. As
> this happens after the memory is zeroed by the slab allocator when
> init_on_free is enabled, the memory coming out of quarantine is not
> properly zeroed.
>
> This causes lib/test_meminit.c tests to fail with Generic KASAN.
>
> Zero the metadata when the object is removed from quarantine.
>
> Fixes: 6471384af2a6 ("mm: security: introduce init_on_alloc=1 and init_on_free=1 boot options")
> Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
> ---
>  mm/kasan/quarantine.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>
> diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
> index 587da8995f2d..2e50869fd8e2 100644
> --- a/mm/kasan/quarantine.c
> +++ b/mm/kasan/quarantine.c
> @@ -132,11 +132,22 @@ static void *qlink_to_object(struct qlist_node *qlink, struct kmem_cache *cache)
>  static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)
>  {
>         void *object = qlink_to_object(qlink, cache);
> +       struct kasan_free_meta *meta = kasan_get_free_meta(cache, object);
>         unsigned long flags;
>
>         if (IS_ENABLED(CONFIG_SLAB))
>                 local_irq_save(flags);
>
> +       /*
> +        * If init_on_free is enabled and KASAN's free metadata is stored in
> +        * the object, zero the metadata. Otherwise, the object's memory will
> +        * not be properly zeroed, as KASAN saves the metadata after the slab
> +        * allocator zeroes the object.
> +        */
> +       if (slab_want_init_on_free(cache) &&
> +           cache->kasan_info.free_meta_offset == 0)
> +               memset(meta, 0, sizeof(*meta));

memzero_explicit()

although in this case it probably doesn't matter much, because AFAIK
memzero_explicit() only exists to prevent the compiler from eliding
the zeroing. Up to you.

> +
>         /*
>          * As the object now gets freed from the quarantine, assume that its
>          * free track is no longer valid.
> --
> 2.25.1
>
Andrey Konovalov Dec. 20, 2021, 5:15 p.m. UTC | #2
On Mon, Dec 20, 2021 at 6:07 PM Marco Elver <elver@google.com> wrote:
>
> On Mon, 20 Dec 2021 at 17:37, <andrey.konovalov@linux.dev> wrote:
> >
> > From: Andrey Konovalov <andreyknvl@google.com>
> >
> > KASAN's quarantine might save its metadata inside freed objects. As
> > this happens after the memory is zeroed by the slab allocator when
> > init_on_free is enabled, the memory coming out of quarantine is not
> > properly zeroed.
> >
> > This causes lib/test_meminit.c tests to fail with Generic KASAN.
> >
> > Zero the metadata when the object is removed from quarantine.
> >
> > Fixes: 6471384af2a6 ("mm: security: introduce init_on_alloc=1 and init_on_free=1 boot options")
> > Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
> > ---
> >  mm/kasan/quarantine.c | 11 +++++++++++
> >  1 file changed, 11 insertions(+)
> >
> > diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
> > index 587da8995f2d..2e50869fd8e2 100644
> > --- a/mm/kasan/quarantine.c
> > +++ b/mm/kasan/quarantine.c
> > @@ -132,11 +132,22 @@ static void *qlink_to_object(struct qlist_node *qlink, struct kmem_cache *cache)
> >  static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)
> >  {
> >         void *object = qlink_to_object(qlink, cache);
> > +       struct kasan_free_meta *meta = kasan_get_free_meta(cache, object);
> >         unsigned long flags;
> >
> >         if (IS_ENABLED(CONFIG_SLAB))
> >                 local_irq_save(flags);
> >
> > +       /*
> > +        * If init_on_free is enabled and KASAN's free metadata is stored in
> > +        * the object, zero the metadata. Otherwise, the object's memory will
> > +        * not be properly zeroed, as KASAN saves the metadata after the slab
> > +        * allocator zeroes the object.
> > +        */
> > +       if (slab_want_init_on_free(cache) &&
> > +           cache->kasan_info.free_meta_offset == 0)
> > +               memset(meta, 0, sizeof(*meta));
>
> memzero_explicit()
>
> although in this case it probably doesn't matter much, because AFAIK
> memzero_explicit() only exists to prevent the compiler from eliding
> the zeroing. Up to you.

I've thought about using memzero_explicit(), but the rest of
init_on_alloc/free code uses memset(0) so I decided to use it as well.
If we decide to switch to memzero_explicit(), it makes sense to do it
everywhere.

Thanks!
Marco Elver Dec. 20, 2021, 5:19 p.m. UTC | #3
On Mon, 20 Dec 2021 at 18:16, Andrey Konovalov <andreyknvl@gmail.com> wrote:
>
> On Mon, Dec 20, 2021 at 6:07 PM Marco Elver <elver@google.com> wrote:
> >
> > On Mon, 20 Dec 2021 at 17:37, <andrey.konovalov@linux.dev> wrote:
> > >
> > > From: Andrey Konovalov <andreyknvl@google.com>
> > >
> > > KASAN's quarantine might save its metadata inside freed objects. As
> > > this happens after the memory is zeroed by the slab allocator when
> > > init_on_free is enabled, the memory coming out of quarantine is not
> > > properly zeroed.
> > >
> > > This causes lib/test_meminit.c tests to fail with Generic KASAN.
> > >
> > > Zero the metadata when the object is removed from quarantine.
> > >
> > > Fixes: 6471384af2a6 ("mm: security: introduce init_on_alloc=1 and init_on_free=1 boot options")
> > > Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
> > > ---
> > >  mm/kasan/quarantine.c | 11 +++++++++++
> > >  1 file changed, 11 insertions(+)
> > >
> > > diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
> > > index 587da8995f2d..2e50869fd8e2 100644
> > > --- a/mm/kasan/quarantine.c
> > > +++ b/mm/kasan/quarantine.c
> > > @@ -132,11 +132,22 @@ static void *qlink_to_object(struct qlist_node *qlink, struct kmem_cache *cache)
> > >  static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)
> > >  {
> > >         void *object = qlink_to_object(qlink, cache);
> > > +       struct kasan_free_meta *meta = kasan_get_free_meta(cache, object);
> > >         unsigned long flags;
> > >
> > >         if (IS_ENABLED(CONFIG_SLAB))
> > >                 local_irq_save(flags);
> > >
> > > +       /*
> > > +        * If init_on_free is enabled and KASAN's free metadata is stored in
> > > +        * the object, zero the metadata. Otherwise, the object's memory will
> > > +        * not be properly zeroed, as KASAN saves the metadata after the slab
> > > +        * allocator zeroes the object.
> > > +        */
> > > +       if (slab_want_init_on_free(cache) &&
> > > +           cache->kasan_info.free_meta_offset == 0)
> > > +               memset(meta, 0, sizeof(*meta));
> >
> > memzero_explicit()
> >
> > although in this case it probably doesn't matter much, because AFAIK
> > memzero_explicit() only exists to prevent the compiler from eliding
> > the zeroing. Up to you.
>
> I've thought about using memzero_explicit(), but the rest of
> init_on_alloc/free code uses memset(0) so I decided to use it as well.
> If we decide to switch to memzero_explicit(), it makes sense to do it
> everywhere.

memzero_explicit() is newer than those existing memset(0) -- new code
should probably start using it.

So I'd opt for just using it here. Who knows what other optimizations
future compilers may come up with.
diff mbox series

Patch

diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c
index 587da8995f2d..2e50869fd8e2 100644
--- a/mm/kasan/quarantine.c
+++ b/mm/kasan/quarantine.c
@@ -132,11 +132,22 @@  static void *qlink_to_object(struct qlist_node *qlink, struct kmem_cache *cache)
 static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)
 {
 	void *object = qlink_to_object(qlink, cache);
+	struct kasan_free_meta *meta = kasan_get_free_meta(cache, object);
 	unsigned long flags;
 
 	if (IS_ENABLED(CONFIG_SLAB))
 		local_irq_save(flags);
 
+	/*
+	 * If init_on_free is enabled and KASAN's free metadata is stored in
+	 * the object, zero the metadata. Otherwise, the object's memory will
+	 * not be properly zeroed, as KASAN saves the metadata after the slab
+	 * allocator zeroes the object.
+	 */
+	if (slab_want_init_on_free(cache) &&
+	    cache->kasan_info.free_meta_offset == 0)
+		memset(meta, 0, sizeof(*meta));
+
 	/*
 	 * As the object now gets freed from the quarantine, assume that its
 	 * free track is no longer valid.