Re: [PATCH v3 2/4] kasan: record and print the free track

From: Dmitry Vyukov
Date: Mon May 18 2020 - 06:18:55 EST


On Mon, May 18, 2020 at 8:27 AM Walter Wu <walter-zh.wu@xxxxxxxxxxxx> wrote:
>
> Move free track from slub alloc meta-data to slub free meta-data in
> order to make struct kasan_free_meta size is 16 bytes. It is a good
> size because it is the minimal redzone size and a good number of
> alignment.
>
> For free track in generic KASAN, we do the modification in struct
> kasan_alloc_meta and kasan_free_meta:
> - remove free track from kasan_alloc_meta.
> - add free track into kasan_free_meta.
>
> [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437
>
> Signed-off-by: Walter Wu <walter-zh.wu@xxxxxxxxxxxx>
> Suggested-by: Dmitry Vyukov <dvyukov@xxxxxxxxxx>
> Cc: Andrey Ryabinin <aryabinin@xxxxxxxxxxxxx>
> Cc: Dmitry Vyukov <dvyukov@xxxxxxxxxx>
> Cc: Alexander Potapenko <glider@xxxxxxxxxx>
> ---
> mm/kasan/common.c | 33 ++++++++++-----------------------
> mm/kasan/generic.c | 18 ++++++++++++++++++
> mm/kasan/kasan.h | 7 +++++++
> mm/kasan/report.c | 20 --------------------
> mm/kasan/tags.c | 37 +++++++++++++++++++++++++++++++++++++
> 5 files changed, 72 insertions(+), 43 deletions(-)
>
> diff --git a/mm/kasan/common.c b/mm/kasan/common.c
> index 8bc618289bb1..6500bc2bb70c 100644
> --- a/mm/kasan/common.c
> +++ b/mm/kasan/common.c
> @@ -51,7 +51,7 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags)
> return stack_depot_save(entries, nr_entries, flags);
> }
>
> -static inline void set_track(struct kasan_track *track, gfp_t flags)
> +void kasan_set_track(struct kasan_track *track, gfp_t flags)
> {
> track->pid = current->pid;
> track->stack = kasan_save_stack(flags);
> @@ -249,9 +249,7 @@ void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
> *size += sizeof(struct kasan_alloc_meta);
>
> /* Add free meta. */
> - if (IS_ENABLED(CONFIG_KASAN_GENERIC) &&
> - (cache->flags & SLAB_TYPESAFE_BY_RCU || cache->ctor ||
> - cache->object_size < sizeof(struct kasan_free_meta))) {
> + if (IS_ENABLED(CONFIG_KASAN_GENERIC)) {

Why do we need to increase object size unconditionally?
We only store info in free track when the object is free, so I would
assume we still can generally overlap free track and the object
itself. We store free track at the same time we use the quarantine
link, and the quarantine link was overlapped with the object just
fine.
With this change we indeed increase object size, which we do not want
in general.


> cache->kasan_info.free_meta_offset = *size;
> *size += sizeof(struct kasan_free_meta);
> }
> @@ -299,24 +297,6 @@ struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
> return (void *)object + cache->kasan_info.free_meta_offset;
> }
>
> -
> -static void kasan_set_free_info(struct kmem_cache *cache,
> - void *object, u8 tag)
> -{
> - struct kasan_alloc_meta *alloc_meta;
> - u8 idx = 0;
> -
> - alloc_meta = get_alloc_info(cache, object);
> -
> -#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> - idx = alloc_meta->free_track_idx;
> - alloc_meta->free_pointer_tag[idx] = tag;
> - alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS;
> -#endif
> -
> - set_track(&alloc_meta->free_track[idx], GFP_NOWAIT);
> -}
> -
> void kasan_poison_slab(struct page *page)
> {
> unsigned long i;
> @@ -396,6 +376,13 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
> alloc_info = get_alloc_info(cache, object);
> __memset(alloc_info, 0, sizeof(*alloc_info));
>
> + if (IS_ENABLED(CONFIG_KASAN_GENERIC)) {
> + struct kasan_free_meta *free_info;
> +
> + free_info = get_free_info(cache, object);
> + __memset(free_info, 0, sizeof(*free_info));

If we overlap free track with object, this will not be needed as well, right?

> + }
> +
> if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
> object = set_tag(object,
> assign_tag(cache, object, true, false));
> @@ -492,7 +479,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
> KASAN_KMALLOC_REDZONE);
>
> if (cache->flags & SLAB_KASAN)
> - set_track(&get_alloc_info(cache, object)->alloc_track, flags);
> + kasan_set_track(&get_alloc_info(cache, object)->alloc_track, flags);
>
> return set_tag(object, tag);
> }
> diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
> index 78d8e0a75a8a..988bc095b738 100644
> --- a/mm/kasan/generic.c
> +++ b/mm/kasan/generic.c
> @@ -345,3 +345,21 @@ void kasan_record_aux_stack(void *addr)
> alloc_info->rcu_stack[1] = alloc_info->rcu_stack[0];
> alloc_info->rcu_stack[0] = kasan_save_stack(GFP_NOWAIT);
> }
> +
> +void kasan_set_free_info(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_free_meta *free_meta;
> +
> + free_meta = get_free_info(cache, object);
> + kasan_set_track(&free_meta->free_track, GFP_NOWAIT);
> +}
> +
> +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_free_meta *free_meta;
> +
> + free_meta = get_free_info(cache, object);
> + return &free_meta->free_track;
> +}
> diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> index 870c5dd07756..87ee3626b8b0 100644
> --- a/mm/kasan/kasan.h
> +++ b/mm/kasan/kasan.h
> @@ -127,6 +127,9 @@ struct kasan_free_meta {
> * Otherwise it might be used for the allocator freelist.
> */
> struct qlist_node quarantine_link;
> +#ifdef CONFIG_KASAN_GENERIC
> + struct kasan_track free_track;
> +#endif
> };
>
> struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
> @@ -168,6 +171,10 @@ void kasan_report_invalid_free(void *object, unsigned long ip);
> struct page *kasan_addr_to_page(const void *addr);
>
> depot_stack_handle_t kasan_save_stack(gfp_t flags);
> +void kasan_set_track(struct kasan_track *track, gfp_t flags);
> +void kasan_set_free_info(struct kmem_cache *cache, void *object, u8 tag);
> +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> + void *object, u8 tag);
>
> #if defined(CONFIG_KASAN_GENERIC) && \
> (defined(CONFIG_SLAB) || defined(CONFIG_SLUB))
> diff --git a/mm/kasan/report.c b/mm/kasan/report.c
> index 5ee66cf7e27c..7e9f9f6d5e85 100644
> --- a/mm/kasan/report.c
> +++ b/mm/kasan/report.c
> @@ -159,26 +159,6 @@ static void describe_object_addr(struct kmem_cache *cache, void *object,
> (void *)(object_addr + cache->object_size));
> }
>
> -static struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> - void *object, u8 tag)
> -{
> - struct kasan_alloc_meta *alloc_meta;
> - int i = 0;
> -
> - alloc_meta = get_alloc_info(cache, object);
> -
> -#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> - for (i = 0; i < KASAN_NR_FREE_STACKS; i++) {
> - if (alloc_meta->free_pointer_tag[i] == tag)
> - break;
> - }
> - if (i == KASAN_NR_FREE_STACKS)
> - i = alloc_meta->free_track_idx;
> -#endif
> -
> - return &alloc_meta->free_track[i];
> -}
> -
> #ifdef CONFIG_KASAN_GENERIC
> static void print_stack(depot_stack_handle_t stack)
> {
> diff --git a/mm/kasan/tags.c b/mm/kasan/tags.c
> index 25b7734e7013..201dee5d6ae0 100644
> --- a/mm/kasan/tags.c
> +++ b/mm/kasan/tags.c
> @@ -162,3 +162,40 @@ void __hwasan_tag_memory(unsigned long addr, u8 tag, unsigned long size)
> kasan_poison_shadow((void *)addr, size, tag);
> }
> EXPORT_SYMBOL(__hwasan_tag_memory);
> +
> +void kasan_set_free_info(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_alloc_meta *alloc_meta;
> + u8 idx = 0;
> +
> + alloc_meta = get_alloc_info(cache, object);
> +
> +#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> + idx = alloc_meta->free_track_idx;
> + alloc_meta->free_pointer_tag[idx] = tag;
> + alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS;
> +#endif
> +
> + kasan_set_track(&alloc_meta->free_track[idx], GFP_NOWAIT);
> +}
> +
> +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_alloc_meta *alloc_meta;
> + int i = 0;
> +
> + alloc_meta = get_alloc_info(cache, object);
> +
> +#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> + for (i = 0; i < KASAN_NR_FREE_STACKS; i++) {
> + if (alloc_meta->free_pointer_tag[i] == tag)
> + break;
> + }
> + if (i == KASAN_NR_FREE_STACKS)
> + i = alloc_meta->free_track_idx;
> +#endif
> +
> + return &alloc_meta->free_track[i];
> +}
> --
> 2.18.0