[PATCH v2 2/2] kasan: remove PG_skip_kasan_poison flag

Peter Collingbourne pcc at google.com
Tue Feb 28 16:36:01 PST 2023


On Tue, Feb 28, 2023 at 12:48 PM Andrey Konovalov <andreyknvl at gmail.com> wrote:
>
> On Tue, Feb 28, 2023 at 7:32 AM Peter Collingbourne <pcc at google.com> wrote:
> >
> > Code inspection reveals that PG_skip_kasan_poison is redundant with
> > kasantag, because the former is intended to be set iff the latter is
> > the match-all tag. It can also be observed that it's basically pointless
> > to poison pages which have kasantag=0, because any pages with this tag
> > would have been pointed to by pointers with match-all tags, so poisoning
> > the pages would have little to no effect in terms of bug detection.
> > Therefore, change the condition in should_skip_kasan_poison() to check
> > kasantag instead, and remove PG_skip_kasan_poison and associated flags.
> >
> > Signed-off-by: Peter Collingbourne <pcc at google.com>
> > Link: https://linux-review.googlesource.com/id/I57f825f2eaeaf7e8389d6cf4597c8a5821359838
> > ---
> > v2:
> > - also remove GFP_SKIP_KASAN_POISON and FPI_SKIP_KASAN_POISON
> > - rename GFP_SKIP_KASAN_UNPOISON to GFP_SKIP_KASAN
> > - update comments
> > - simplify control flow by removing reset_tags
> >
> >  include/linux/gfp_types.h      | 28 +++++-------
> >  include/linux/page-flags.h     |  9 ----
> >  include/trace/events/mmflags.h | 12 +-----
> >  mm/kasan/hw_tags.c             |  2 +-
> >  mm/page_alloc.c                | 79 +++++++++++++---------------------
> >  mm/vmalloc.c                   |  2 +-
> >  6 files changed, 44 insertions(+), 88 deletions(-)
> >
> > diff --git a/include/linux/gfp_types.h b/include/linux/gfp_types.h
> > index 5088637fe5c2..9bd45cdd19ac 100644
> > --- a/include/linux/gfp_types.h
> > +++ b/include/linux/gfp_types.h
> > @@ -47,16 +47,14 @@ typedef unsigned int __bitwise gfp_t;
> >  #define ___GFP_ACCOUNT         0x400000u
> >  #define ___GFP_ZEROTAGS                0x800000u
> >  #ifdef CONFIG_KASAN_HW_TAGS
> > -#define ___GFP_SKIP_ZERO               0x1000000u
> > -#define ___GFP_SKIP_KASAN_UNPOISON     0x2000000u
> > -#define ___GFP_SKIP_KASAN_POISON       0x4000000u
> > +#define ___GFP_SKIP_ZERO       0x1000000u
> > +#define ___GFP_SKIP_KASAN      0x2000000u
> >  #else
> > -#define ___GFP_SKIP_ZERO               0
> > -#define ___GFP_SKIP_KASAN_UNPOISON     0
> > -#define ___GFP_SKIP_KASAN_POISON       0
> > +#define ___GFP_SKIP_ZERO       0
> > +#define ___GFP_SKIP_KASAN      0
> >  #endif
> >  #ifdef CONFIG_LOCKDEP
> > -#define ___GFP_NOLOCKDEP       0x8000000u
> > +#define ___GFP_NOLOCKDEP       0x4000000u
> >  #else
> >  #define ___GFP_NOLOCKDEP       0
> >  #endif
> > @@ -234,25 +232,22 @@ typedef unsigned int __bitwise gfp_t;
> >   * memory tags at the same time as zeroing memory has minimal additional
> >   * performace impact.
> >   *
> > - * %__GFP_SKIP_KASAN_UNPOISON makes KASAN skip unpoisoning on page allocation.
> > - * Only effective in HW_TAGS mode.
> > - *
> > - * %__GFP_SKIP_KASAN_POISON makes KASAN skip poisoning on page deallocation.
> > - * Typically, used for userspace pages. Only effective in HW_TAGS mode.
> > + * %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation and
> > + * poisoning on page deallocation. Typically used for userspace and vmalloc
> > + * pages. Only effective in HW_TAGS mode.
>
> This is not entirely correct: for vmalloc pages, this flag doesn't
> result in poisoning being skipped, as the memory is unpoisoned and
> page tags are assigned by kasan_unpoison_vmalloc.

I see. I somehow missed that vmalloc was setting the tag itself.

> How about something like this:
>
> %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation.
> Used for userspace and vmalloc pages; the latter are unpoisoned by
> kasan_unpoison_vmalloc instead. For userspace pages, results in
> poisoning being skipped as well, see should_skip_kasan_poison for
> details. Only effective in HW_TAGS mode.

Yes, that sounds reasonable. Done in v3.

> >   */
> >  #define __GFP_NOWARN   ((__force gfp_t)___GFP_NOWARN)
> >  #define __GFP_COMP     ((__force gfp_t)___GFP_COMP)
> >  #define __GFP_ZERO     ((__force gfp_t)___GFP_ZERO)
> >  #define __GFP_ZEROTAGS ((__force gfp_t)___GFP_ZEROTAGS)
> >  #define __GFP_SKIP_ZERO ((__force gfp_t)___GFP_SKIP_ZERO)
> > -#define __GFP_SKIP_KASAN_UNPOISON ((__force gfp_t)___GFP_SKIP_KASAN_UNPOISON)
> > -#define __GFP_SKIP_KASAN_POISON   ((__force gfp_t)___GFP_SKIP_KASAN_POISON)
> > +#define __GFP_SKIP_KASAN ((__force gfp_t)___GFP_SKIP_KASAN)
> >
> >  /* Disable lockdep for GFP context tracking */
> >  #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP)
> >
> >  /* Room for N __GFP_FOO bits */
> > -#define __GFP_BITS_SHIFT (27 + IS_ENABLED(CONFIG_LOCKDEP))
> > +#define __GFP_BITS_SHIFT (26 + IS_ENABLED(CONFIG_LOCKDEP))
> >  #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
> >
> >  /**
> > @@ -335,8 +330,7 @@ typedef unsigned int __bitwise gfp_t;
> >  #define GFP_DMA                __GFP_DMA
> >  #define GFP_DMA32      __GFP_DMA32
> >  #define GFP_HIGHUSER   (GFP_USER | __GFP_HIGHMEM)
> > -#define GFP_HIGHUSER_MOVABLE   (GFP_HIGHUSER | __GFP_MOVABLE | \
> > -                        __GFP_SKIP_KASAN_POISON | __GFP_SKIP_KASAN_UNPOISON)
> > +#define GFP_HIGHUSER_MOVABLE   (GFP_HIGHUSER | __GFP_MOVABLE | __GFP_SKIP_KASAN)
> >  #define GFP_TRANSHUGE_LIGHT    ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | \
> >                          __GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM)
> >  #define GFP_TRANSHUGE  (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM)
> > diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> > index a7e3a3405520..74f81a52e7e1 100644
> > --- a/include/linux/page-flags.h
> > +++ b/include/linux/page-flags.h
> > @@ -135,9 +135,6 @@ enum pageflags {
> >  #ifdef CONFIG_ARCH_USES_PG_ARCH_X
> >         PG_arch_2,
> >         PG_arch_3,
> > -#endif
> > -#ifdef CONFIG_KASAN_HW_TAGS
> > -       PG_skip_kasan_poison,
> >  #endif
> >         __NR_PAGEFLAGS,
> >
> > @@ -594,12 +591,6 @@ TESTCLEARFLAG(Young, young, PF_ANY)
> >  PAGEFLAG(Idle, idle, PF_ANY)
> >  #endif
> >
> > -#ifdef CONFIG_KASAN_HW_TAGS
> > -PAGEFLAG(SkipKASanPoison, skip_kasan_poison, PF_HEAD)
> > -#else
> > -PAGEFLAG_FALSE(SkipKASanPoison, skip_kasan_poison)
> > -#endif
> > -
> >  /*
> >   * PageReported() is used to track reported free pages within the Buddy
> >   * allocator. We can use the non-atomic version of the test and set
> > diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
> > index 9db52bc4ce19..232bc8efc98e 100644
> > --- a/include/trace/events/mmflags.h
> > +++ b/include/trace/events/mmflags.h
> > @@ -55,8 +55,7 @@
> >  #ifdef CONFIG_KASAN_HW_TAGS
> >  #define __def_gfpflag_names_kasan ,                    \
> >         gfpflag_string(__GFP_SKIP_ZERO),                \
> > -       gfpflag_string(__GFP_SKIP_KASAN_POISON),        \
> > -       gfpflag_string(__GFP_SKIP_KASAN_UNPOISON)
> > +       gfpflag_string(__GFP_SKIP_KASAN)
> >  #else
> >  #define __def_gfpflag_names_kasan
> >  #endif
> > @@ -96,12 +95,6 @@
> >  #define IF_HAVE_PG_ARCH_X(flag,string)
> >  #endif
> >
> > -#ifdef CONFIG_KASAN_HW_TAGS
> > -#define IF_HAVE_PG_SKIP_KASAN_POISON(flag,string) ,{1UL << flag, string}
> > -#else
> > -#define IF_HAVE_PG_SKIP_KASAN_POISON(flag,string)
> > -#endif
> > -
> >  #define __def_pageflag_names                                           \
> >         {1UL << PG_locked,              "locked"        },              \
> >         {1UL << PG_waiters,             "waiters"       },              \
> > @@ -130,8 +123,7 @@ IF_HAVE_PG_HWPOISON(PG_hwpoison,    "hwpoison"      )               \
> >  IF_HAVE_PG_IDLE(PG_young,              "young"         )               \
> >  IF_HAVE_PG_IDLE(PG_idle,               "idle"          )               \
> >  IF_HAVE_PG_ARCH_X(PG_arch_2,           "arch_2"        )               \
> > -IF_HAVE_PG_ARCH_X(PG_arch_3,           "arch_3"        )               \
> > -IF_HAVE_PG_SKIP_KASAN_POISON(PG_skip_kasan_poison, "skip_kasan_poison")
> > +IF_HAVE_PG_ARCH_X(PG_arch_3,           "arch_3"        )
> >
> >  #define show_page_flags(flags)                                         \
> >         (flags) ? __print_flags(flags, "|",                             \
> > diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
> > index d1bcb0205327..bb4f56e5bdec 100644
> > --- a/mm/kasan/hw_tags.c
> > +++ b/mm/kasan/hw_tags.c
> > @@ -318,7 +318,7 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
> >          * Thus, for VM_ALLOC mappings, hardware tag-based KASAN only tags
> >          * the first virtual mapping, which is created by vmalloc().
> >          * Tagging the page_alloc memory backing that vmalloc() allocation is
> > -        * skipped, see ___GFP_SKIP_KASAN_UNPOISON.
> > +        * skipped, see ___GFP_SKIP_KASAN.
> >          *
> >          * For non-VM_ALLOC allocations, page_alloc memory is tagged as usual.
> >          */
> > diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> > index 7136c36c5d01..960e0edd413d 100644
> > --- a/mm/page_alloc.c
> > +++ b/mm/page_alloc.c
> > @@ -112,17 +112,6 @@ typedef int __bitwise fpi_t;
> >   */
> >  #define FPI_TO_TAIL            ((__force fpi_t)BIT(1))
> >
> > -/*
> > - * Don't poison memory with KASAN (only for the tag-based modes).
> > - * During boot, all non-reserved memblock memory is exposed to page_alloc.
> > - * Poisoning all that memory lengthens boot time, especially on systems with
> > - * large amount of RAM. This flag is used to skip that poisoning.
> > - * This is only done for the tag-based KASAN modes, as those are able to
> > - * detect memory corruptions with the memory tags assigned by default.
> > - * All memory allocated normally after boot gets poisoned as usual.
> > - */
> > -#define FPI_SKIP_KASAN_POISON  ((__force fpi_t)BIT(2))
> > -
> >  /* prevent >1 _updater_ of zone percpu pageset ->high and ->batch fields */
> >  static DEFINE_MUTEX(pcp_batch_high_lock);
> >  #define MIN_PERCPU_PAGELIST_HIGH_FRACTION (8)
> > @@ -1355,13 +1344,19 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
> >  /*
> >   * Skip KASAN memory poisoning when either:
> >   *
> > - * 1. Deferred memory initialization has not yet completed,
> > - *    see the explanation below.
> > - * 2. Skipping poisoning is requested via FPI_SKIP_KASAN_POISON,
> > - *    see the comment next to it.
> > - * 3. Skipping poisoning is requested via __GFP_SKIP_KASAN_POISON,
> > + * 1. For generic KASAN: deferred memory initialization has not yet completed.
> > + *    Tag-based KASAN modes skip pages freed via deferred memory initialization
> > + *    using page tags instead (see below).
> > + * 2. For tag-based KASAN: the page has a match-all KASAN tag, indicating
>
> For tag-based KASAN modes: ...

Done in v3.

> > + *    that error detection is disabled for accesses via the page address.
> > + *
> > + * Pages will have match-all tags in the following circumstances:
> > + *
> > + * 1. Skipping poisoning is requested via __GFP_SKIP_KASAN,
> >   *    see the comment next to it.
>
> According to the vmalloc thing I mentioned above, let's reword this to:
>
> The allocation was not unpoisoned due to __GFP_SKIP_KASAN, with the
> exception of pages unpoisoned by kasan_unpoison_vmalloc.

Done in v3.

> > - * 4. The allocation is excluded from being checked due to sampling,
> > + * 2. Pages are being initialized for the first time, including during deferred
> > + *    memory init; see the call to page_kasan_tag_reset in __init_single_page.
>
> Let's put this item first in the list.
>
> > + * 3. The allocation is excluded from being checked due to sampling,

Done in v3.

> "is" -> "was" possibly sounds better with "was" in #1.

Done in v3.

Peter



More information about the linux-arm-kernel mailing list