kasan: switch tag-based modes to stack ring from per-object metadata
Subsystems (6)
v3 cover kasan: switch tag-based modes to stack ring from per-object metadata
From: Andrey Konovalov <[email protected]>
This series makes the tag-based KASAN modes use a ring buffer for storing stack depot handles for alloc/free stack traces for slab objects instead of per-object metadata. This ring buffer is referred to as the stack ring.
On each alloc/free of a slab object, the tagged address of the object and the current stack trace are recorded in the stack ring.
On each bug report, if the accessed address belongs to a slab object, the stack ring is scanned for matching entries. The newest entries are used to print the alloc/free stack traces in the report: one entry for alloc and one for free.
The advantages of this approach over storing stack trace handles in per-object metadata with the tag-based KASAN modes:
- Allows to find relevant stack traces for use-after-free bugs without using quarantine for freed memory. (Currently, if the object was reallocated multiple times, the report contains the latest alloc/free stack traces, not necessarily the ones relevant to the buggy allocation.)
- Allows to better identify and mark use-after-free bugs, effectively making the CONFIG_KASAN_TAGS_IDENTIFY functionality always-on.
- Has fixed memory overhead.
The disadvantage:
- If the affected object was allocated/freed long before the bug happened and the stack trace events were purged from the stack ring, the report will have no stack traces.
Discussion
==========
The proposed implementation of the stack ring uses a single ring buffer for the whole kernel. This might lead to contention due to atomic accesses to the ring buffer index on multicore systems.
At this point, it is unknown whether the performance impact from this contention would be significant compared to the slowdown introduced by collecting stack traces due to the planned changes to the latter part, see the section below.
For now, the proposed implementation is deemed to be good enough, but this might need to be revisited once the stack collection becomes faster.
A considered alternative is to keep a separate ring buffer for each CPU and then iterate over all of them when printing a bug report. This approach requires somehow figuring out which of the stack rings has the freshest stack traces for an object if multiple stack rings have them.
Further plans
=============
This series is a part of an effort to make KASAN stack trace collection suitable for production. This requires stack trace collection to be fast and memory-bounded.
The planned steps are:
1. Speed up stack trace collection (potentially, by using SCS; patches on-hold until steps #2 and #3 are completed).
2. Keep stack trace handles in the stack ring (this series).
3. Add a memory-bounded mode to stack depot or provide an alternative memory-bounded stack storage.
4. Potentially, implement stack trace collection sampling to minimize the performance impact.
Thanks!
---
Changes v2->v3:
- Addressed Marco's comments, see the last 3 patches for list of changes.
Changes v1->v2:
- Rework synchronization in the stack ring implementation.
- Dynamically allocate stack ring based on the kasan.stack_ring_size command-line parameter.
- Multiple less significant changes, see the notes in patches for details.
Andrey Konovalov (34):
kasan: check KASAN_NO_FREE_META in __kasan_metadata_size
kasan: rename kasan_set_*_info to kasan_save_*_info
kasan: move is_kmalloc check out of save_alloc_info
kasan: split save_alloc_info implementations
kasan: drop CONFIG_KASAN_TAGS_IDENTIFY
kasan: introduce kasan_print_aux_stacks
kasan: introduce kasan_get_alloc_track
kasan: introduce kasan_init_object_meta
kasan: clear metadata functions for tag-based modes
kasan: move kasan_get_*_meta to generic.c
kasan: introduce kasan_requires_meta
kasan: introduce kasan_init_cache_meta
kasan: drop CONFIG_KASAN_GENERIC check from kasan_init_cache_meta
kasan: only define kasan_metadata_size for Generic mode
kasan: only define kasan_never_merge for Generic mode
kasan: only define metadata offsets for Generic mode
kasan: only define metadata structs for Generic mode
kasan: only define kasan_cache_create for Generic mode
kasan: pass tagged pointers to kasan_save_alloc/free_info
kasan: move kasan_get_alloc/free_track definitions
kasan: cosmetic changes in report.c
kasan: use virt_addr_valid in kasan_addr_to_page/slab
kasan: use kasan_addr_to_slab in print_address_description
kasan: make kasan_addr_to_page static
kasan: simplify print_report
kasan: introduce complete_report_info
kasan: fill in cache and object in complete_report_info
kasan: rework function arguments in report.c
kasan: introduce kasan_complete_mode_report_info
kasan: implement stack ring for tag-based modes
kasan: support kasan.stacktrace for SW_TAGS
kasan: dynamically allocate stack ring entries
kasan: better identify bug types for tag-based modes
kasan: add another use-after-free test
Documentation/dev-tools/kasan.rst | 17 ++- include/linux/kasan.h | 55 ++++------ include/linux/slab.h | 2 +- lib/Kconfig.kasan | 8 -- lib/test_kasan.c | 24 ++++ mm/kasan/common.c | 175 +++--------------------------- mm/kasan/generic.c | 154 ++++++++++++++++++++++++-- mm/kasan/hw_tags.c | 39 +------ mm/kasan/kasan.h | 171 ++++++++++++++++++++--------- mm/kasan/report.c | 117 +++++++++----------- mm/kasan/report_generic.c | 45 +++++++- mm/kasan/report_tags.c | 123 ++++++++++++++++----- mm/kasan/sw_tags.c | 5 +- mm/kasan/tags.c | 141 +++++++++++++++++++----- 14 files changed, 642 insertions(+), 434 deletions(-)
Signature · 1 line
2.25.1
PATCH v3 1/34 kasan: check KASAN_NO_FREE_META in __kasan_metadata_size
From: Andrey Konovalov <[email protected]>
__kasan_metadata_size() calculates the size of the redzone for objects in a slab cache.
When accounting for presence of kasan_free_meta in the redzone, this function only compares free_meta_offset with 0. But free_meta_offset could also be equal to KASAN_NO_FREE_META, which indicates that kasan_free_meta is not present at all.
Add a comparison with KASAN_NO_FREE_META into __kasan_metadata_size().
---
This is a minor fix that only affects slub_debug runs, so it is probably not worth backporting.
--- mm/kasan/common.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
mm/kasan/common.c +3 -2
@@ -224,8 +224,9 @@ size_t __kasan_metadata_size(struct kmem_cache *cache)return 0 ;return (cache->kasan_info.alloc_meta_offset ?sizeof (struct kasan_alloc_meta ) :0 ) + - (cache->kasan_info.free_meta_offset ? -sizeof (struct kasan_free_meta ) :0 ); + ((cache->kasan_info.free_meta_offset && + cache->kasan_info.free_meta_offset !=KASAN_NO_FREE_META ) ? +sizeof (struct kasan_free_meta ) :0 ); } struct kasan_alloc_meta *kasan_get_alloc_meta(struct kmem_cache *cache,
Signature · 1 line
2.25.1
PATCH v3 2/34 kasan: rename kasan_set_*_info to kasan_save_*_info
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Rename set_alloc_info() and kasan_set_free_info() to save_alloc_info() and kasan_save_free_info(). The new names make more sense.
--- mm/kasan/common.c | 8 ++++---- mm/kasan/generic.c | 2 +- mm/kasan/kasan.h | 2 +- mm/kasan/tags.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-)
mm/kasan/common.c +4 -4
@@ -365,7 +365,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,return false;if (kasan_stack_collection_enabled ()) -kasan_set_free_info (cache, object, tag); +kasan_save_free_info (cache, object, tag);return kasan_quarantine_put (cache, object); }
@@ -424,7 +424,7 @@ void __kasan_slab_free_mempool(void *ptr, unsigned long ip) } } -static void set_alloc_info (struct kmem_cache *cache,void *object, +static void save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags,bool is_kmalloc) {struct kasan_alloc_meta *alloc_meta;
@@ -468,7 +468,7 @@ void * __must_check __kasan_slab_alloc(struct kmem_cache *cache,/* Save alloc info (if possible) for non-kmalloc() allocations. */ if (kasan_stack_collection_enabled ()) -set_alloc_info (cache, (void *)object, flags, false); +save_alloc_info (cache, (void *)object, flags, false);return tagged_object; }
@@ -514,7 +514,7 @@ static inline void *____kasan_kmalloc(struct kmem_cache *cache,* This also rewrites the alloc info when called from kasan_krealloc(). */ if (kasan_stack_collection_enabled ()) -set_alloc_info (cache, (void *)object, flags, true); +save_alloc_info (cache, (void *)object, flags, true);/* Keep the tag that was set by kasan_slab_alloc(). */ return (void *)object;
mm/kasan/generic.c +1 -1
@@ -358,7 +358,7 @@ void kasan_record_aux_stack_noalloc(void *addr)return __kasan_record_aux_stack (addr, false); } -void kasan_set_free_info (struct kmem_cache *cache, +void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag) {struct kasan_free_meta *free_meta;
mm/kasan/kasan.h +1 -1
@@ -285,7 +285,7 @@ struct slab *kasan_addr_to_slab(const void *addr);depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags); -void kasan_set_free_info (struct kmem_cache *cache,void *object,u8 tag); +void kasan_save_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);
mm/kasan/tags.c +1 -1
@@ -17,7 +17,7 @@#include "kasan.h" -void kasan_set_free_info (struct kmem_cache *cache, +void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag) {struct kasan_alloc_meta *alloc_meta;
Signature · 1 line
2.25.1
PATCH v3 3/34 kasan: move is_kmalloc check out of save_alloc_info
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Move kasan_info.is_kmalloc check out of save_alloc_info().
This is a preparatory change that simplifies the following patches in this series.
--- mm/kasan/common.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-)
mm/kasan/common.c +5 -10
@@ -424,15 +424,10 @@ void __kasan_slab_free_mempool(void *ptr, unsigned long ip) } } -static void save_alloc_info (struct kmem_cache *cache,void *object, -gfp_t flags,bool is_kmalloc) +static void save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) {struct kasan_alloc_meta *alloc_meta; -/* Don't save alloc info for kmalloc caches in kasan_slab_alloc(). */ -if (cache->kasan_info.is_kmalloc && !is_kmalloc) -return ; - alloc_meta =kasan_get_alloc_meta (cache, object);if (alloc_meta)kasan_set_track (&alloc_meta->alloc_track, flags);
@@ -467,8 +462,8 @@ void * __must_check __kasan_slab_alloc(struct kmem_cache *cache,kasan_unpoison (tagged_object, cache->object_size, init);/* Save alloc info (if possible) for non-kmalloc() allocations. */ -if (kasan_stack_collection_enabled ()) -save_alloc_info (cache, (void *)object, flags, false); +if (kasan_stack_collection_enabled () && !cache->kasan_info.is_kmalloc) +save_alloc_info (cache, (void *)object, flags);return tagged_object; }
@@ -513,8 +508,8 @@ static inline void *____kasan_kmalloc(struct kmem_cache *cache,* Save alloc info (if possible) for kmalloc() allocations. * This also rewrites the alloc info when called from kasan_krealloc(). */ -if (kasan_stack_collection_enabled ()) -save_alloc_info (cache, (void *)object, flags, true); +if (kasan_stack_collection_enabled () && cache->kasan_info.is_kmalloc) +save_alloc_info (cache, (void *)object, flags);/* Keep the tag that was set by kasan_slab_alloc(). */ return (void *)object;
Signature · 1 line
2.25.1
PATCH v3 4/34 kasan: split save_alloc_info implementations
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Provide standalone implementations of save_alloc_info() for the Generic and tag-based modes.
For now, the implementations are the same, but they will diverge later in the series.
--- mm/kasan/common.c | 13 ++----------- mm/kasan/generic.c | 9 +++++++++ mm/kasan/kasan.h | 1 + mm/kasan/tags.c | 9 +++++++++ 4 files changed, 21 insertions(+), 11 deletions(-)
mm/kasan/common.c +2 -11
@@ -424,15 +424,6 @@ void __kasan_slab_free_mempool(void *ptr, unsigned long ip) } } -static void save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) -{ -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (alloc_meta) -kasan_set_track (&alloc_meta->alloc_track, flags); -} -void *__must_check __kasan_slab_alloc (struct kmem_cache *cache,void *object,gfp_t flags,bool init) {
@@ -463,7 +454,7 @@ void * __must_check __kasan_slab_alloc(struct kmem_cache *cache,/* Save alloc info (if possible) for non-kmalloc() allocations. */ if (kasan_stack_collection_enabled () && !cache->kasan_info.is_kmalloc) -save_alloc_info (cache, (void *)object, flags); +kasan_save_alloc_info (cache, (void *)object, flags);return tagged_object; }
@@ -509,7 +500,7 @@ static inline void *____kasan_kmalloc(struct kmem_cache *cache,* This also rewrites the alloc info when called from kasan_krealloc(). */ if (kasan_stack_collection_enabled () && cache->kasan_info.is_kmalloc) -save_alloc_info (cache, (void *)object, flags); +kasan_save_alloc_info (cache, (void *)object, flags);/* Keep the tag that was set by kasan_slab_alloc(). */ return (void *)object;
mm/kasan/generic.c +9 -0
@@ -358,6 +358,15 @@ void kasan_record_aux_stack_noalloc(void *addr)return __kasan_record_aux_stack (addr, false); } +void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (alloc_meta) +kasan_set_track (&alloc_meta->alloc_track, flags); +} +void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag) {
mm/kasan/kasan.h +1 -0
@@ -285,6 +285,7 @@ struct slab *kasan_addr_to_slab(const void *addr);depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags); +void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags);void kasan_save_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);
mm/kasan/tags.c +9 -0
@@ -17,6 +17,15 @@#include "kasan.h" +void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (alloc_meta) +kasan_set_track (&alloc_meta->alloc_track, flags); +} +void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag) {
Signature · 1 line
2.25.1
PATCH v3 5/34 kasan: drop CONFIG_KASAN_TAGS_IDENTIFY
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Drop CONFIG_KASAN_TAGS_IDENTIFY and related code to simplify making changes to the reporting code.
The dropped functionality will be restored in the following patches in this series.
--- lib/Kconfig.kasan | 8 -------- mm/kasan/kasan.h | 12 +----------- mm/kasan/report_tags.c | 28 ---------------------------- mm/kasan/tags.c | 21 ++------------------- 4 files changed, 3 insertions(+), 66 deletions(-)
lib/Kconfig.kasan +0 -8
@@ -167,14 +167,6 @@ config KASAN_STACK as well, as it adds inline-style instrumentation that is run unconditionally. -config KASAN_TAGS_IDENTIFY - bool "Memory corruption type identification" - depends on KASAN_SW_TAGS || KASAN_HW_TAGS - help - Enables best-effort identification of the bug types (use-after-free - or out-of-bounds) at the cost of increased memory consumption. - Only applicable for the tag-based KASAN modes. - config KASAN_VMALLOC bool "Check accesses to vmalloc allocations" depends on HAVE_ARCH_KASAN_VMALLOC
mm/kasan/kasan.h +1 -11
@@ -169,23 +169,13 @@ struct kasan_track {depot_stack_handle_t stack; }; -#if defined(CONFIG_KASAN_TAGS_IDENTIFY ) && defined(CONFIG_KASAN_SW_TAGS ) -#define KASAN_NR_FREE_STACKS 5 -#else -#define KASAN_NR_FREE_STACKS 1 -#endif -struct kasan_alloc_meta {struct kasan_track alloc_track;/* Generic mode stores free track in kasan_free_meta. */ #ifdef CONFIG_KASAN_GENERIC depot_stack_handle_t aux_stack[2 ];#else -struct kasan_track free_track[KASAN_NR_FREE_STACKS ]; -#endif -#ifdef CONFIG_KASAN_TAGS_IDENTIFY -u8 free_pointer_tag[KASAN_NR_FREE_STACKS ]; -u8 free_track_idx; +struct kasan_track free_track;#endif };
mm/kasan/report_tags.c +0 -28
@@ -5,37 +5,9 @@*/ #include "kasan.h" -#include "../slab.h" const char *kasan_get_bug_type (struct kasan_report_info *info) { -#ifdef CONFIG_KASAN_TAGS_IDENTIFY -struct kasan_alloc_meta *alloc_meta; -struct kmem_cache *cache; -struct slab *slab; -const void *addr; -void *object; -u8 tag; -int i; - - tag =get_tag (info->access_addr); - addr =kasan_reset_tag (info->access_addr); - slab =kasan_addr_to_slab (addr); -if (slab) { - cache = slab->slab_cache; - object =nearest_obj (cache, slab, (void *)addr); - alloc_meta =kasan_get_alloc_meta (cache, object); - -if (alloc_meta) { -for (i =0 ; i <KASAN_NR_FREE_STACKS ; i++) { -if (alloc_meta->free_pointer_tag[i] == tag) -return "use-after-free" ; - } - } -return "out-of-bounds" ; - } -#endif -/* * If access_size is a negative number, then it has reason to be * defined as out-of-bounds bug type.
mm/kasan/tags.c +2 -19
@@ -30,39 +30,22 @@ void kasan_save_free_info(struct kmem_cache *cache, void *object, u8 tag) {struct kasan_alloc_meta *alloc_meta; -u8 idx =0 ; alloc_meta =kasan_get_alloc_meta (cache, object);if (!alloc_meta)return ; -#ifdef CONFIG_KASAN_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 ); +kasan_set_track (&alloc_meta->free_track,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 =kasan_get_alloc_meta (cache, object);if (!alloc_meta)return NULL ; -#ifdef CONFIG_KASAN_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]; +return &alloc_meta->free_track; }
Signature · 1 line
2.25.1
PATCH v3 6/34 kasan: introduce kasan_print_aux_stacks
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add a kasan_print_aux_stacks() helper that prints the auxiliary stack traces for the Generic mode.
This change hides references to alloc_meta from the common reporting code. This is desired as only the Generic mode will be using per-object metadata after this series.
--- mm/kasan/kasan.h | 6 ++++++ mm/kasan/report.c | 15 +-------------- mm/kasan/report_generic.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 14 deletions(-)
mm/kasan/kasan.h +6 -0
@@ -266,6 +266,12 @@ void kasan_print_address_stack_frame(const void *addr);static inline void kasan_print_address_stack_frame (const void *addr) { }#endif +#ifdef CONFIG_KASAN_GENERIC +void kasan_print_aux_stacks (struct kmem_cache *cache,const void *object); +#else +static inline void kasan_print_aux_stacks (struct kmem_cache *cache,const void *object) { } +#endif +bool kasan_report (unsigned long addr,size_t size,bool is_write,unsigned long ip);void kasan_report_invalid_free (void *object,unsigned long ip,enum kasan_report_type type);
mm/kasan/report.c +1 -14
@@ -270,20 +270,7 @@ static void describe_object_stacks(struct kmem_cache *cache, void *object,pr_err ("\n" ); } -#ifdef CONFIG_KASAN_GENERIC -if (!alloc_meta) -return ; -if (alloc_meta->aux_stack[0 ]) { -pr_err ("Last potentially related work creation:\n" ); -stack_depot_print (alloc_meta->aux_stack[0 ]); -pr_err ("\n" ); - } -if (alloc_meta->aux_stack[1 ]) { -pr_err ("Second to last potentially related work creation:\n" ); -stack_depot_print (alloc_meta->aux_stack[1 ]); -pr_err ("\n" ); - } -#endif +kasan_print_aux_stacks (cache, object); } static void describe_object(struct kmem_cache *cache, void *object,
mm/kasan/report_generic.c +20 -0
@@ -132,6 +132,26 @@ void kasan_metadata_fetch_row(char *buffer, void *row)memcpy (buffer,kasan_mem_to_shadow (row),META_BYTES_PER_ROW ); } +void kasan_print_aux_stacks (struct kmem_cache *cache,const void *object) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return ; + +if (alloc_meta->aux_stack[0 ]) { +pr_err ("Last potentially related work creation:\n" ); +stack_depot_print (alloc_meta->aux_stack[0 ]); +pr_err ("\n" ); + } +if (alloc_meta->aux_stack[1 ]) { +pr_err ("Second to last potentially related work creation:\n" ); +stack_depot_print (alloc_meta->aux_stack[1 ]); +pr_err ("\n" ); + } +} +#ifdef CONFIG_KASAN_STACK static bool __must_check tokenize_frame_descr(const char **frame_descr, char *token, size_t max_tok_len,
Signature · 1 line
2.25.1
PATCH v3 7/34 kasan: introduce kasan_get_alloc_track
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add a kasan_get_alloc_track() helper that fetches alloc_track for a slab object and use this helper in the common reporting code.
For now, the implementations of this helper are the same for the Generic and tag-based modes, but they will diverge later in the series.
This change hides references to alloc_meta from the common reporting code. This is desired as only the Generic mode will be using per-object metadata after this series.
--- mm/kasan/generic.c | 14 +++++++++++++- mm/kasan/kasan.h | 4 +++- mm/kasan/report.c | 8 ++++---- mm/kasan/tags.c | 14 +++++++++++++- 4 files changed, 33 insertions(+), 7 deletions(-)
mm/kasan/generic.c +13 -1
@@ -381,8 +381,20 @@ void kasan_save_free_info(struct kmem_cache *cache, *(u8 *)kasan_mem_to_shadow (object) =KASAN_SLAB_FREETRACK ; } +struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, +void *object) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return NULL ; + +return &alloc_meta->alloc_track; +} +struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag) +void *object,u8 tag) {if (*(u8 *)kasan_mem_to_shadow (object) !=KASAN_SLAB_FREETRACK )return NULL ;
mm/kasan/kasan.h +3 -1
@@ -283,8 +283,10 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags, bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags);void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags);void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag); +struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, +void *object);struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag); +void *object,u8 tag);#if defined(CONFIG_KASAN_GENERIC ) && \ (defined(CONFIG_SLAB ) || defined(CONFIG_SLUB ))
mm/kasan/report.c +4 -4
@@ -255,12 +255,12 @@ static void describe_object_addr(struct kmem_cache *cache, void *object,static void describe_object_stacks (struct kmem_cache *cache,void *object,const void *addr,u8 tag) { -struct kasan_alloc_meta *alloc_meta; +struct kasan_track *alloc_track;struct kasan_track *free_track; - alloc_meta =kasan_get_alloc_meta (cache, object); -if (alloc_meta) { -print_track (&alloc_meta->alloc_track,"Allocated" ); + alloc_track =kasan_get_alloc_track (cache, object); +if (alloc_track) { +print_track (alloc_track,"Allocated" );pr_err ("\n" ); }
mm/kasan/tags.c +13 -1
@@ -38,8 +38,20 @@ void kasan_save_free_info(struct kmem_cache *cache,kasan_set_track (&alloc_meta->free_track,GFP_NOWAIT ); } +struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, +void *object) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return NULL ; + +return &alloc_meta->alloc_track; +} +struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag) +void *object,u8 tag) {struct kasan_alloc_meta *alloc_meta;
Signature · 1 line
2.25.1
PATCH v3 8/34 kasan: introduce kasan_init_object_meta
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add a kasan_init_object_meta() helper that initializes metadata for a slab object and use it in the common code.
For now, the implementations of this helper are the same for the Generic and tag-based modes, but they will diverge later in the series.
This change hides references to alloc_meta from the common code. This is desired as only the Generic mode will be using per-object metadata after this series.
--- mm/kasan/common.c | 10 +++------- mm/kasan/generic.c | 9 +++++++++ mm/kasan/kasan.h | 2 ++ mm/kasan/tags.c | 9 +++++++++ 4 files changed, 23 insertions(+), 7 deletions(-)
mm/kasan/common.c +3 -7
@@ -313,13 +313,9 @@ static inline u8 assign_tag(struct kmem_cache *cache,void *__must_check __kasan_init_slab_obj (struct kmem_cache *cache,const void *object) { -struct kasan_alloc_meta *alloc_meta; - -if (kasan_stack_collection_enabled ()) { - alloc_meta =kasan_get_alloc_meta (cache, object); -if (alloc_meta) -__memset (alloc_meta,0 ,sizeof (*alloc_meta)); - } +/* Initialize per-object metadata if it is present. */ +if (kasan_stack_collection_enabled ()) +kasan_init_object_meta (cache, object);/* Tag is ignored in set_tag() without CONFIG_KASAN_SW/HW_TAGS */ object =set_tag (object,assign_tag (cache, object, true));
mm/kasan/generic.c +9 -0
@@ -328,6 +328,15 @@ DEFINE_ASAN_SET_SHADOW(f3);DEFINE_ASAN_SET_SHADOW (f5);DEFINE_ASAN_SET_SHADOW (f8); +void kasan_init_object_meta (struct kmem_cache *cache,const void *object) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (alloc_meta) +__memset (alloc_meta,0 ,sizeof (*alloc_meta)); +} +static void __kasan_record_aux_stack (void *addr,bool can_alloc) {struct slab *slab =kasan_addr_to_slab (addr);
mm/kasan/kasan.h +2 -0
@@ -279,6 +279,8 @@ void kasan_report_invalid_free(void *object, unsigned long ip, enum kasan_reportstruct page *kasan_addr_to_page (const void *addr);struct slab *kasan_addr_to_slab (const void *addr); +void kasan_init_object_meta (struct kmem_cache *cache,const void *object); +depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags);void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags);
mm/kasan/tags.c +9 -0
@@ -17,6 +17,15 @@#include "kasan.h" +void kasan_init_object_meta (struct kmem_cache *cache,const void *object) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (alloc_meta) +__memset (alloc_meta,0 ,sizeof (*alloc_meta)); +} +void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) {struct kasan_alloc_meta *alloc_meta;
Signature · 1 line
2.25.1
PATCH v3 9/34 kasan: clear metadata functions for tag-based modes
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Remove implementations of the metadata-related functions for the tag-based modes.
The following patches in the series will provide alternative implementations.
As of this patch, the tag-based modes no longer collect alloc and free stack traces. This functionality will be restored later in the series.
--- mm/kasan/tags.c | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-)
mm/kasan/tags.c +2 -31
@@ -19,54 +19,25 @@void kasan_init_object_meta (struct kmem_cache *cache,const void *object) { -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (alloc_meta) -__memset (alloc_meta,0 ,sizeof (*alloc_meta)); }void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) { -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (alloc_meta) -kasan_set_track (&alloc_meta->alloc_track, flags); }void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag) { -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (!alloc_meta) -return ; - -kasan_set_track (&alloc_meta->free_track,GFP_NOWAIT ); }struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache,void *object) { -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (!alloc_meta) -return NULL ; - -return &alloc_meta->alloc_track; +return NULL ; }struct kasan_track *kasan_get_free_track (struct kmem_cache *cache,void *object,u8 tag) { -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (!alloc_meta) -return NULL ; - -return &alloc_meta->free_track; +return NULL ; }
Signature · 1 line
2.25.1
PATCH v3 10/34 kasan: move kasan_get_*_meta to generic.c
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Move the implementations of kasan_get_alloc/free_meta() to generic.c, as the common KASAN code does not use these functions anymore.
Also drop kasan_reset_tag() from the implementation, as the Generic mode does not tag pointers.
--- mm/kasan/common.c | 19 ------------------- mm/kasan/generic.c | 17 +++++++++++++++++ mm/kasan/kasan.h | 14 +++++++------- 3 files changed, 24 insertions(+), 26 deletions(-)
mm/kasan/common.c +0 -19
@@ -229,25 +229,6 @@ size_t __kasan_metadata_size(struct kmem_cache *cache) sizeof(struct kasan_free_meta) : 0); } -struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache, -const void *object) -{ -if (!cache->kasan_info.alloc_meta_offset) -return NULL ; -return kasan_reset_tag (object) + cache->kasan_info.alloc_meta_offset; -} - -#ifdef CONFIG_KASAN_GENERIC -struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache, -const void *object) -{ -BUILD_BUG_ON (sizeof (struct kasan_free_meta ) >32 ); -if (cache->kasan_info.free_meta_offset ==KASAN_NO_FREE_META ) -return NULL ; -return kasan_reset_tag (object) + cache->kasan_info.free_meta_offset; -} -#endif -void __kasan_poison_slab (struct slab *slab) {struct page *page =slab_page (slab);
mm/kasan/generic.c +17 -0
@@ -328,6 +328,23 @@ DEFINE_ASAN_SET_SHADOW(f3);DEFINE_ASAN_SET_SHADOW (f5);DEFINE_ASAN_SET_SHADOW (f8); +struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache, +const void *object) +{ +if (!cache->kasan_info.alloc_meta_offset) +return NULL ; +return (void *)object + cache->kasan_info.alloc_meta_offset; +} + +struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache, +const void *object) +{ +BUILD_BUG_ON (sizeof (struct kasan_free_meta ) >32 ); +if (cache->kasan_info.free_meta_offset ==KASAN_NO_FREE_META ) +return NULL ; +return (void *)object + cache->kasan_info.free_meta_offset; +} +void kasan_init_object_meta (struct kmem_cache *cache,const void *object) {struct kasan_alloc_meta *alloc_meta;
mm/kasan/kasan.h +7 -7
@@ -209,13 +209,6 @@ struct kunit_kasan_status { };#endif -struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache, -const void *object); -#ifdef CONFIG_KASAN_GENERIC -struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache, -const void *object); -#endif -#if defined(CONFIG_KASAN_GENERIC ) || defined(CONFIG_KASAN_SW_TAGS )static inline const void *kasan_shadow_to_mem (const void *shadow_addr)
@@ -281,6 +274,13 @@ struct slab *kasan_addr_to_slab(const void *addr);void kasan_init_object_meta (struct kmem_cache *cache,const void *object); +#ifdef CONFIG_KASAN_GENERIC +struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache, +const void *object); +struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache, +const void *object); +#endif +depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags);void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags);
Signature · 1 line
2.25.1
PATCH v3 11/34 kasan: introduce kasan_requires_meta
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add a kasan_requires_meta() helper that indicates whether the enabled KASAN mode requires per-object metadata and use this helper in the common code.
Also hide kasan_init_object_meta() under CONFIG_KASAN_GENERIC ifdef check, as Generic is the only mode that uses per-object metadata.
To allow for a potential future change that makes Generic KASAN support the kasan.stacktrace command-line parameter, let kasan_requires_meta() return kasan_stack_collection_enabled() instead of simply returning true.
--- mm/kasan/common.c | 13 +++++-------- mm/kasan/kasan.h | 33 +++++++++++++++++++++++++++++---- mm/kasan/tags.c | 4 ---- 3 files changed, 34 insertions(+), 16 deletions(-)
mm/kasan/common.c +5 -8
@@ -88,13 +88,10 @@ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark) }#endif /* CONFIG_KASAN_STACK */ -/* -* Only allow cache merging when stack collection is disabled and no metadata -* is present. -*/ +/* Only allow cache merging when no per-object metadata is present. */ slab_flags_t __kasan_never_merge (void ) { -if (kasan_stack_collection_enabled ()) +if (kasan_requires_meta ())return SLAB_KASAN ;return 0 ; }
@@ -152,7 +149,7 @@ void __kasan_cache_create(struct kmem_cache *cache, unsigned int *size,*/ *flags |=SLAB_KASAN ; -if (!kasan_stack_collection_enabled ()) +if (!kasan_requires_meta ())return ; ok_size = *size;
@@ -220,7 +217,7 @@ void __kasan_cache_create_kmalloc(struct kmem_cache *cache)size_t __kasan_metadata_size (struct kmem_cache *cache) { -if (!kasan_stack_collection_enabled ()) +if (!kasan_requires_meta ())return 0 ;return (cache->kasan_info.alloc_meta_offset ? sizeof(struct kasan_alloc_meta) : 0) +
@@ -295,7 +292,7 @@ void * __must_check __kasan_init_slab_obj(struct kmem_cache *cache, const void *object) {/* Initialize per-object metadata if it is present. */ -if (kasan_stack_collection_enabled ()) +if (kasan_requires_meta ())kasan_init_object_meta (cache, object);/* Tag is ignored in set_tag() without CONFIG_KASAN_SW/HW_TAGS */
mm/kasan/kasan.h +29 -4
@@ -43,7 +43,7 @@ static inline bool kasan_sync_fault_possible(void) return kasan_mode == KASAN_MODE_SYNC || kasan_mode == KASAN_MODE_ASYMM; } -#else +#else /* CONFIG_KASAN_HW_TAGS */ static inline bool kasan_stack_collection_enabled (void ) {
@@ -60,7 +60,31 @@ static inline bool kasan_sync_fault_possible(void) return true; } -#endif +#endif /* CONFIG_KASAN_HW_TAGS */ + +#ifdef CONFIG_KASAN_GENERIC + +/* Generic KASAN uses per-object metadata to store stack traces. */ +static inline bool kasan_requires_meta (void ) +{ +/* +* Technically, Generic KASAN always collects stack traces right now. +* However, let's use kasan_stack_collection_enabled() in case the +* kasan.stacktrace command-line argument is changed to affect +* Generic KASAN. +*/ +return kasan_stack_collection_enabled (); +} + +#else /* CONFIG_KASAN_GENERIC */ + +/* Tag-based KASAN modes do not use per-object metadata. */ +static inline bool kasan_requires_meta (void ) +{ +return false; +} + +#endif /* CONFIG_KASAN_GENERIC */ #if defined(CONFIG_KASAN_GENERIC ) || defined(CONFIG_KASAN_SW_TAGS )#define KASAN_GRANULE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
@@ -272,13 +296,14 @@ void kasan_report_invalid_free(void *object, unsigned long ip, enum kasan_reportstruct page *kasan_addr_to_page (const void *addr);struct slab *kasan_addr_to_slab (const void *addr); -void kasan_init_object_meta (struct kmem_cache *cache,const void *object); -#ifdef CONFIG_KASAN_GENERIC +void kasan_init_object_meta (struct kmem_cache *cache,const void *object);struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache,const void *object);struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache,const void *object); +#else +static inline void kasan_init_object_meta (struct kmem_cache *cache,const void *object) { }#endif depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc);
mm/kasan/tags.c +0 -4
@@ -17,10 +17,6 @@#include "kasan.h" -void kasan_init_object_meta (struct kmem_cache *cache,const void *object) -{ -} -void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) { }
Signature · 1 line
2.25.1
PATCH v3 12/34 kasan: introduce kasan_init_cache_meta
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add a kasan_init_cache_meta() helper that initializes metadata-related cache parameters and use this helper in the common KASAN code.
Put the implementation of this new helper into generic.c, as only the Generic mode uses per-object metadata.
--- mm/kasan/common.c | 80 ++-------------------------------------------- mm/kasan/generic.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ mm/kasan/kasan.h | 2 ++ 3 files changed, 83 insertions(+), 78 deletions(-)
mm/kasan/common.c +2 -78
@@ -118,28 +118,9 @@ void __kasan_poison_pages(struct page *page, unsigned int order, bool init) KASAN_PAGE_FREE, init); } -/* -* Adaptive redzone policy taken from the userspace AddressSanitizer runtime. -* For larger allocations larger redzones are used. -*/ -static inline unsigned int optimal_redzone (unsigned int object_size) -{ -return - object_size <=64 -16 ?16 : - object_size <=128 -32 ?32 : - object_size <=512 -64 ?64 : - object_size <=4096 -128 ?128 : - object_size <= (1 <<14 ) -256 ?256 : - object_size <= (1 <<15 ) -512 ?512 : - object_size <= (1 <<16 ) -1024 ?1024 :2048 ; -} -void __kasan_cache_create (struct kmem_cache *cache,unsigned int *size,slab_flags_t *flags) { -unsigned int ok_size; -unsigned int optimal_size; -/* * SLAB_KASAN is used to mark caches as ones that are sanitized by * KASAN. Currently this flag is used in two places:
@@ -149,65 +130,8 @@ void __kasan_cache_create(struct kmem_cache *cache, unsigned int *size,*/ *flags |=SLAB_KASAN ; -if (!kasan_requires_meta ()) -return ; - - ok_size = *size; - -/* Add alloc meta into redzone. */ - cache->kasan_info.alloc_meta_offset = *size; - *size +=sizeof (struct kasan_alloc_meta ); - -/* -* If alloc meta doesn't fit, don't add it. -* This can only happen with SLAB, as it has KMALLOC_MAX_SIZE equal -* to KMALLOC_MAX_CACHE_SIZE and doesn't fall back to page_alloc for -* larger sizes. -*/ -if (*size >KMALLOC_MAX_SIZE ) { - cache->kasan_info.alloc_meta_offset =0 ; - *size = ok_size; -/* Continue, since free meta might still fit. */ - } - -/* Only the generic mode uses free meta or flexible redzones. */ -if (!IS_ENABLED (CONFIG_KASAN_GENERIC )) { - cache->kasan_info.free_meta_offset =KASAN_NO_FREE_META ; -return ; - } - -/* -* Add free meta into redzone when it's not possible to store -* it in the object. This is the case when: -* 1. Object is SLAB_TYPESAFE_BY_RCU, which means that it can -* be touched after it was freed, or -* 2. Object has a constructor, which means it's expected to -* retain its content until the next allocation, or -* 3. Object is too small. -* Otherwise cache->kasan_info.free_meta_offset = 0 is implied. -*/ -if ((cache->flags &SLAB_TYPESAFE_BY_RCU ) || cache->ctor || - cache->object_size <sizeof (struct kasan_free_meta )) { - ok_size = *size; - - cache->kasan_info.free_meta_offset = *size; - *size +=sizeof (struct kasan_free_meta ); - -/* If free meta doesn't fit, don't add it. */ -if (*size >KMALLOC_MAX_SIZE ) { - cache->kasan_info.free_meta_offset =KASAN_NO_FREE_META ; - *size = ok_size; - } - } - -/* Calculate size with optimal redzone. */ - optimal_size = cache->object_size +optimal_redzone (cache->object_size); -/* Limit it with KMALLOC_MAX_SIZE (relevant for SLAB only). */ -if (optimal_size >KMALLOC_MAX_SIZE ) - optimal_size =KMALLOC_MAX_SIZE ; -/* Use optimal size if the size with added metas is not large enough. */ -if (*size < optimal_size) - *size = optimal_size; +if (kasan_requires_meta ()) +kasan_init_cache_meta (cache, size); }void __kasan_cache_create_kmalloc (struct kmem_cache *cache)
mm/kasan/generic.c +79 -0
85 lines not on this page · raw message
mm/kasan/kasan.h +2 -0
@@ -297,12 +297,14 @@ struct page *kasan_addr_to_page(const void *addr);struct slab *kasan_addr_to_slab (const void *addr);#ifdef CONFIG_KASAN_GENERIC +void kasan_init_cache_meta (struct kmem_cache *cache,unsigned int *size);void kasan_init_object_meta (struct kmem_cache *cache,const void *object);struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache,const void *object);struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache,const void *object);#else +static inline void kasan_init_cache_meta (struct kmem_cache *cache,unsigned int *size) { }static inline void kasan_init_object_meta (struct kmem_cache *cache,const void *object) { }#endif
Signature · 1 line
2.25.1
PATCH v3 13/34 kasan: drop CONFIG_KASAN_GENERIC check from kasan_init_cache_meta
In reply to [email protected]From: Andrey Konovalov <[email protected]>
As kasan_init_cache_meta() is only defined for the Generic mode, it does not require the CONFIG_KASAN_GENERIC check.
--- mm/kasan/generic.c | 6 ------ 1 file changed, 6 deletions(-)
mm/kasan/generic.c +0 -6
@@ -367,12 +367,6 @@ void kasan_init_cache_meta(struct kmem_cache *cache, unsigned int *size)/* Continue, since free meta might still fit. */ } -/* Only the generic mode uses free meta or flexible redzones. */ -if (!IS_ENABLED (CONFIG_KASAN_GENERIC )) { - cache->kasan_info.free_meta_offset =KASAN_NO_FREE_META ; -return ; - } -/* * Add free meta into redzone when it's not possible to store * it in the object. This is the case when:
Signature · 1 line
2.25.1
PATCH v3 14/34 kasan: only define kasan_metadata_size for Generic mode
In reply to [email protected]From: Andrey Konovalov <[email protected]>
KASAN provides a helper for calculating the size of per-object metadata stored in the redzone.
As now only the Generic mode uses per-object metadata, only define kasan_metadata_size() for this mode.
--- include/linux/kasan.h | 17 ++++++++--------- mm/kasan/common.c | 11 ----------- mm/kasan/generic.c | 11 +++++++++++ 3 files changed, 19 insertions(+), 20 deletions(-)
include/linux/kasan.h +8 -9
@@ -150,14 +150,6 @@ static __always_inline void kasan_cache_create_kmalloc(struct kmem_cache *cache)__kasan_cache_create_kmalloc (cache); } -size_t __kasan_metadata_size (struct kmem_cache *cache); -static __always_inline size_t kasan_metadata_size(struct kmem_cache *cache) -{ -if (kasan_enabled ()) -return __kasan_metadata_size (cache); -return 0 ; -} -void __kasan_poison_slab (struct slab *slab); static __always_inline void kasan_poison_slab(struct slab *slab) {
@@ -282,7 +274,6 @@ static inline void kasan_cache_create(struct kmem_cache *cache, unsigned int *size, slab_flags_t *flags) {}static inline void kasan_cache_create_kmalloc (struct kmem_cache *cache) {} -static inline size_t kasan_metadata_size (struct kmem_cache *cache) {return 0 ; }static inline void kasan_poison_slab (struct slab *slab) {}static inline void kasan_unpoison_object_data (struct kmem_cache *cache,void *object) {}
@@ -333,6 +324,8 @@ static inline void kasan_unpoison_task_stack(struct task_struct *task) {}#ifdef CONFIG_KASAN_GENERIC +size_t kasan_metadata_size (struct kmem_cache *cache); +void kasan_cache_shrink (struct kmem_cache *cache);void kasan_cache_shutdown (struct kmem_cache *cache);void kasan_record_aux_stack (void *ptr);
@@ -340,6 +333,12 @@ void kasan_record_aux_stack_noalloc(void *ptr);#else /* CONFIG_KASAN_GENERIC */ +/* Tag-based KASAN modes do not use per-object metadata. */ +static inline size_t kasan_metadata_size (struct kmem_cache *cache) +{ +return 0 ; +} +static inline void kasan_cache_shrink (struct kmem_cache *cache) {}static inline void kasan_cache_shutdown (struct kmem_cache *cache) {}static inline void kasan_record_aux_stack (void *ptr) {}
mm/kasan/common.c +0 -11
@@ -139,17 +139,6 @@ void __kasan_cache_create_kmalloc(struct kmem_cache *cache) cache->kasan_info.is_kmalloc = true; } -size_t __kasan_metadata_size (struct kmem_cache *cache) -{ -if (!kasan_requires_meta ()) -return 0 ; -return (cache->kasan_info.alloc_meta_offset ? -sizeof (struct kasan_alloc_meta ) :0 ) + - ((cache->kasan_info.free_meta_offset && - cache->kasan_info.free_meta_offset !=KASAN_NO_FREE_META ) ? -sizeof (struct kasan_free_meta ) :0 ); -} -void __kasan_poison_slab (struct slab *slab) {struct page *page =slab_page (slab);
mm/kasan/generic.c +11 -0
@@ -427,6 +427,17 @@ void kasan_init_object_meta(struct kmem_cache *cache, const void *object)__memset (alloc_meta,0 ,sizeof (*alloc_meta)); } +size_t kasan_metadata_size (struct kmem_cache *cache) +{ +if (!kasan_requires_meta ()) +return 0 ; +return (cache->kasan_info.alloc_meta_offset ? +sizeof (struct kasan_alloc_meta ) :0 ) + + ((cache->kasan_info.free_meta_offset && + cache->kasan_info.free_meta_offset !=KASAN_NO_FREE_META ) ? +sizeof (struct kasan_free_meta ) :0 ); +} +static void __kasan_record_aux_stack (void *addr,bool can_alloc) {struct slab *slab =kasan_addr_to_slab (addr);
Signature · 1 line
2.25.1
PATCH v3 15/34 kasan: only define kasan_never_merge for Generic mode
In reply to [email protected]From: Andrey Konovalov <[email protected]>
KASAN prevents merging of slab caches whose objects have per-object metadata stored in redzones.
As now only the Generic mode uses per-object metadata, define kasan_never_merge() only for this mode.
--- include/linux/kasan.h | 18 ++++++------------ mm/kasan/common.c | 8 -------- mm/kasan/generic.c | 8 ++++++++ 3 files changed, 14 insertions(+), 20 deletions(-)
include/linux/kasan.h +6 -12
@@ -103,14 +103,6 @@ struct kasan_cache {bool is_kmalloc; }; -slab_flags_t __kasan_never_merge (void ); -static __always_inline slab_flags_t kasan_never_merge(void) -{ -if (kasan_enabled ()) -return __kasan_never_merge (); -return 0 ; -} -void __kasan_unpoison_range (const void *addr,size_t size); static __always_inline void kasan_unpoison_range(const void *addr, size_t size) {
@@ -261,10 +253,6 @@ static __always_inline bool kasan_check_byte(const void *addr)#else /* CONFIG_KASAN */ -static inline slab_flags_t kasan_never_merge (void ) -{ -return 0 ; -}static inline void kasan_unpoison_range (const void *address,size_t size) {}static inline void kasan_poison_pages (struct page *page,unsigned int order,bool init) {}
@@ -325,6 +313,7 @@ static inline void kasan_unpoison_task_stack(struct task_struct *task) {}#ifdef CONFIG_KASAN_GENERIC size_t kasan_metadata_size (struct kmem_cache *cache); +slab_flags_t kasan_never_merge (void );void kasan_cache_shrink (struct kmem_cache *cache);void kasan_cache_shutdown (struct kmem_cache *cache);
@@ -338,6 +327,11 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) {return 0 ; } +/* And thus nothing prevents cache merging. */ +static inline slab_flags_t kasan_never_merge (void ) +{ +return 0 ; +}static inline void kasan_cache_shrink (struct kmem_cache *cache) {}static inline void kasan_cache_shutdown (struct kmem_cache *cache) {}
mm/kasan/common.c +0 -8
@@ -88,14 +88,6 @@ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark) }#endif /* CONFIG_KASAN_STACK */ -/* Only allow cache merging when no per-object metadata is present. */ -slab_flags_t __kasan_never_merge (void ) -{ -if (kasan_requires_meta ()) -return SLAB_KASAN ; -return 0 ; -} -void __kasan_unpoison_pages (struct page *page,unsigned int order,bool init) {u8 tag;
mm/kasan/generic.c +8 -0
@@ -328,6 +328,14 @@ DEFINE_ASAN_SET_SHADOW(f3);DEFINE_ASAN_SET_SHADOW (f5);DEFINE_ASAN_SET_SHADOW (f8); +/* Only allow cache merging when no per-object metadata is present. */ +slab_flags_t kasan_never_merge (void ) +{ +if (!kasan_requires_meta ()) +return 0 ; +return SLAB_KASAN ; +} +/* * Adaptive redzone policy taken from the userspace AddressSanitizer runtime. * For larger allocations larger redzones are used.
Signature · 1 line
2.25.1
PATCH v3 16/34 kasan: only define metadata offsets for Generic mode
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Hide the definitions of alloc_meta_offset and free_meta_offset under an ifdef CONFIG_KASAN_GENERIC check, as these fields are now only used when the Generic mode is enabled.
--- include/linux/kasan.h | 2 ++ 1 file changed, 2 insertions(+)
include/linux/kasan.h +2 -0
@@ -98,8 +98,10 @@ static inline bool kasan_has_integrated_init(void)#ifdef CONFIG_KASAN struct kasan_cache { +#ifdef CONFIG_KASAN_GENERIC int alloc_meta_offset;int free_meta_offset; +#endif bool is_kmalloc; };
Signature · 1 line
2.25.1
PATCH v3 17/34 kasan: only define metadata structs for Generic mode
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Hide the definitions of kasan_alloc_meta and kasan_free_meta under an ifdef CONFIG_KASAN_GENERIC check, as these structures are now only used when the Generic mode is enabled.
--- mm/kasan/kasan.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
mm/kasan/kasan.h +5 -7
@@ -193,14 +193,12 @@ struct kasan_track {depot_stack_handle_t stack; }; +#ifdef CONFIG_KASAN_GENERIC +struct kasan_alloc_meta {struct kasan_track alloc_track; -/* Generic mode stores free track in kasan_free_meta. */ -#ifdef CONFIG_KASAN_GENERIC +/* Free track is stored in kasan_free_meta. */ depot_stack_handle_t aux_stack[2 ]; -#else -struct kasan_track free_track; -#endif }; struct qlist_node {
@@ -219,12 +217,12 @@ struct qlist_node {* After that, slab allocator stores the freelist pointer in the object. */ struct kasan_free_meta { -#ifdef CONFIG_KASAN_GENERIC struct qlist_node quarantine_link;struct kasan_track free_track; -#endif }; +#endif /* CONFIG_KASAN_GENERIC */ +#if IS_ENABLED (CONFIG_KASAN_KUNIT_TEST )/* Used in KUnit-compatible KASAN tests. */ struct kunit_kasan_status {
Signature · 1 line
2.25.1
PATCH v3 18/34 kasan: only define kasan_cache_create for Generic mode
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Right now, kasan_cache_create() assigns SLAB_KASAN for all KASAN modes and then sets up metadata-related cache parameters for the Generic mode.
SLAB_KASAN is used in two places:
1. In slab_ksize() to account for per-object metadata when calculating the size of the accessible memory within the object.
2. In slab_common.c via kasan_never_merge() to prevent merging of caches with per-object metadata.
Both cases are only relevant when per-object metadata is present, which is only the case with the Generic mode.
Thus, assign SLAB_KASAN and define kasan_cache_create() only for the Generic mode.
Also update the SLAB_KASAN-related comment.
--- include/linux/kasan.h | 18 ++++++------------ include/linux/slab.h | 2 +- mm/kasan/common.c | 16 ---------------- mm/kasan/generic.c | 17 ++++++++++++++++- 4 files changed, 23 insertions(+), 30 deletions(-)
include/linux/kasan.h +6 -12
@@ -128,15 +128,6 @@ static __always_inline void kasan_unpoison_pages(struct page *page,__kasan_unpoison_pages (page, order, init); } -void __kasan_cache_create (struct kmem_cache *cache,unsigned int *size, -slab_flags_t *flags); -static __always_inline void kasan_cache_create(struct kmem_cache *cache, -unsigned int *size,slab_flags_t *flags) -{ -if (kasan_enabled ()) -__kasan_cache_create (cache, size, flags); -} -void __kasan_cache_create_kmalloc (struct kmem_cache *cache); static __always_inline void kasan_cache_create_kmalloc(struct kmem_cache *cache) {
@@ -260,9 +251,6 @@ static inline void kasan_poison_pages(struct page *page, unsigned int order, bool init) {}static inline void kasan_unpoison_pages (struct page *page,unsigned int order,bool init) {} -static inline void kasan_cache_create (struct kmem_cache *cache, -unsigned int *size, -slab_flags_t *flags) {}static inline void kasan_cache_create_kmalloc (struct kmem_cache *cache) {}static inline void kasan_poison_slab (struct slab *slab) {} static inline void kasan_unpoison_object_data(struct kmem_cache *cache,
@@ -316,6 +304,8 @@ static inline void kasan_unpoison_task_stack(struct task_struct *task) {}size_t kasan_metadata_size (struct kmem_cache *cache);slab_flags_t kasan_never_merge (void ); +void kasan_cache_create (struct kmem_cache *cache,unsigned int *size, +slab_flags_t *flags);void kasan_cache_shrink (struct kmem_cache *cache);void kasan_cache_shutdown (struct kmem_cache *cache);
@@ -334,6 +324,10 @@ static inline slab_flags_t kasan_never_merge(void) {return 0 ; } +/* And no cache-related metadata initialization is required. */ +static inline void kasan_cache_create (struct kmem_cache *cache, +unsigned int *size, +slab_flags_t *flags) {}static inline void kasan_cache_shrink (struct kmem_cache *cache) {}static inline void kasan_cache_shutdown (struct kmem_cache *cache) {}
include/linux/slab.h +1 -1
@@ -106,7 +106,7 @@# define SLAB_ACCOUNT 0#endif -#ifdef CONFIG_KASAN +#ifdef CONFIG_KASAN_GENERIC #define SLAB_KASAN ((slab_flags_t __force)0x08000000U)#else #define SLAB_KASAN 0
mm/kasan/common.c +0 -16
@@ -110,22 +110,6 @@ void __kasan_poison_pages(struct page *page, unsigned int order, bool init) KASAN_PAGE_FREE, init); } -void __kasan_cache_create (struct kmem_cache *cache,unsigned int *size, -slab_flags_t *flags) -{ -/* -* SLAB_KASAN is used to mark caches as ones that are sanitized by -* KASAN. Currently this flag is used in two places: -* 1. In slab_ksize() when calculating the size of the accessible -* memory within the object. -* 2. In slab_common.c to prevent merging of sanitized caches. -*/ - *flags |=SLAB_KASAN ; - -if (kasan_requires_meta ()) -kasan_init_cache_meta (cache, size); -} -void __kasan_cache_create_kmalloc (struct kmem_cache *cache) { cache->kasan_info.is_kmalloc = true;
mm/kasan/generic.c +16 -1
@@ -352,11 +352,26 @@ static inline unsigned int optimal_redzone(unsigned int object_size) object_size <= (1 <<16 ) -1024 ?1024 :2048 ; } -void kasan_init_cache_meta (struct kmem_cache *cache,unsigned int *size) +void kasan_cache_create (struct kmem_cache *cache,unsigned int *size, +slab_flags_t *flags) {unsigned int ok_size;unsigned int optimal_size; +if (!kasan_requires_meta ()) +return ; + +/* +* SLAB_KASAN is used to mark caches that are sanitized by KASAN +* and that thus have per-object metadata. +* Currently this flag is used in two places: +* 1. In slab_ksize() to account for per-object metadata when +* calculating the size of the accessible memory within the object. +* 2. In slab_common.c via kasan_never_merge() to prevent merging of +* caches with per-object metadata. +*/ + *flags |=SLAB_KASAN ; + ok_size = *size;/* Add alloc meta into redzone. */
Signature · 1 line
2.25.1
PATCH v3 19/34 kasan: pass tagged pointers to kasan_save_alloc/free_info
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Pass tagged pointers to kasan_save_alloc/free_info().
This is a preparatory patch to simplify other changes in the series.
---
Changes v1->v2:
- Drop unused variable tag from ____kasan_slab_free().
--- mm/kasan/common.c | 6 ++---- mm/kasan/generic.c | 3 +-- mm/kasan/kasan.h | 2 +- mm/kasan/tags.c | 3 +-- 4 files changed, 5 insertions(+), 9 deletions(-)
mm/kasan/common.c +2 -4
@@ -193,13 +193,11 @@ void * __must_check __kasan_init_slab_obj(struct kmem_cache *cache,static inline bool ____kasan_slab_free (struct kmem_cache *cache,void *object,unsigned long ip,bool quarantine,bool init) { -u8 tag;void *tagged_object;if (!kasan_arch_is_ready ())return false; - tag =get_tag (object); tagged_object = object; object =kasan_reset_tag (object);
@@ -228,7 +226,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,return false;if (kasan_stack_collection_enabled ()) -kasan_save_free_info (cache, object, tag); +kasan_save_free_info (cache, tagged_object);return kasan_quarantine_put (cache, object); }
@@ -317,7 +315,7 @@ void * __must_check __kasan_slab_alloc(struct kmem_cache *cache,/* Save alloc info (if possible) for non-kmalloc() allocations. */ if (kasan_stack_collection_enabled () && !cache->kasan_info.is_kmalloc) -kasan_save_alloc_info (cache, (void *)object, flags); +kasan_save_alloc_info (cache, tagged_object, flags);return tagged_object; }
mm/kasan/generic.c +1 -2
@@ -500,8 +500,7 @@ void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags)kasan_set_track (&alloc_meta->alloc_track, flags); } -void kasan_save_free_info (struct kmem_cache *cache, -void *object,u8 tag) +void kasan_save_free_info (struct kmem_cache *cache,void *object) {struct kasan_free_meta *free_meta;
mm/kasan/kasan.h +1 -1
@@ -309,7 +309,7 @@ static inline void kasan_init_object_meta(struct kmem_cache *cache, const void *depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags);void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags); -void kasan_save_free_info (struct kmem_cache *cache,void *object,u8 tag); +void kasan_save_free_info (struct kmem_cache *cache,void *object);struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache,void *object); struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
mm/kasan/tags.c +1 -2
@@ -21,8 +21,7 @@ void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags) { } -void kasan_save_free_info (struct kmem_cache *cache, -void *object,u8 tag) +void kasan_save_free_info (struct kmem_cache *cache,void *object) { }
Signature · 1 line
2.25.1
PATCH v3 20/34 kasan: move kasan_get_alloc/free_track definitions
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Move the definitions of kasan_get_alloc/free_track() to report_*.c, as they belong with other the reporting code.
--- mm/kasan/generic.c | 21 --------------------- mm/kasan/report_generic.c | 21 +++++++++++++++++++++ mm/kasan/report_tags.c | 12 ++++++++++++ mm/kasan/tags.c | 12 ------------ 4 files changed, 33 insertions(+), 33 deletions(-)
mm/kasan/generic.c +0 -21
@@ -512,24 +512,3 @@ void kasan_save_free_info(struct kmem_cache *cache, void *object)/* The object was freed and has free track set. */ *(u8 *)kasan_mem_to_shadow (object) =KASAN_SLAB_FREETRACK ; } - -struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, -void *object) -{ -struct kasan_alloc_meta *alloc_meta; - - alloc_meta =kasan_get_alloc_meta (cache, object); -if (!alloc_meta) -return NULL ; - -return &alloc_meta->alloc_track; -} - -struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag) -{ -if (*(u8 *)kasan_mem_to_shadow (object) !=KASAN_SLAB_FREETRACK ) -return NULL ; -/* Free meta must be present with KASAN_SLAB_FREETRACK. */ -return &kasan_get_free_meta (cache, object)->free_track; -}
mm/kasan/report_generic.c +21 -0
@@ -127,6 +127,27 @@ const char *kasan_get_bug_type(struct kasan_report_info *info)return get_wild_bug_type (info); } +struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, +void *object) +{ +struct kasan_alloc_meta *alloc_meta; + + alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return NULL ; + +return &alloc_meta->alloc_track; +} + +struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, +void *object,u8 tag) +{ +if (*(u8 *)kasan_mem_to_shadow (object) !=KASAN_SLAB_FREETRACK ) +return NULL ; +/* Free meta must be present with KASAN_SLAB_FREETRACK. */ +return &kasan_get_free_meta (cache, object)->free_track; +} +void kasan_metadata_fetch_row (char *buffer,void *row) {memcpy (buffer,kasan_mem_to_shadow (row),META_BYTES_PER_ROW );
mm/kasan/report_tags.c +12 -0
@@ -21,3 +21,15 @@ const char *kasan_get_bug_type(struct kasan_report_info *info)return "invalid-access" ; } + +struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, +void *object) +{ +return NULL ; +} + +struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, +void *object,u8 tag) +{ +return NULL ; +}
mm/kasan/tags.c +0 -12
@@ -24,15 +24,3 @@ void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags)void kasan_save_free_info (struct kmem_cache *cache,void *object) { } - -struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, -void *object) -{ -return NULL ; -} - -struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag) -{ -return NULL ; -}
Signature · 1 line
2.25.1
PATCH v3 21/34 kasan: cosmetic changes in report.c
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Do a few non-functional style fixes for the code in report.c.
--- mm/kasan/report.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
mm/kasan/report.c +4 -7
@@ -200,25 +200,22 @@ static void print_error_description(struct kasan_report_info *info)static void print_track (struct kasan_track *track,const char *prefix) {pr_err ("%s by task %u:\n" , prefix, track->pid); -if (track->stack) { +if (track->stack)stack_depot_print (track->stack); - }else { +else pr_err ("(stack is not available)\n" ); - } }struct page *kasan_addr_to_page (const void *addr) { -if ((addr >= (void *)PAGE_OFFSET ) && - (addr < high_memory)) +if ((addr >= (void *)PAGE_OFFSET ) && (addr < high_memory))return virt_to_head_page (addr);return NULL ; }struct slab *kasan_addr_to_slab (const void *addr) { -if ((addr >= (void *)PAGE_OFFSET ) && - (addr < high_memory)) +if ((addr >= (void *)PAGE_OFFSET ) && (addr < high_memory))return virt_to_slab (addr);return NULL ; }
Signature · 1 line
2.25.1
PATCH v3 22/34 kasan: use virt_addr_valid in kasan_addr_to_page/slab
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Instead of open-coding the validity checks for addr in kasan_addr_to_page/slab(), use the virt_addr_valid() helper.
---
Changes v1->v2:
- This is a new patch.
--- mm/kasan/report.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
mm/kasan/report.c +2 -2
@@ -208,14 +208,14 @@ static void print_track(struct kasan_track *track, const char *prefix)struct page *kasan_addr_to_page (const void *addr) { -if ((addr >= (void *)PAGE_OFFSET ) && (addr < high_memory)) +if (virt_addr_valid (addr))return virt_to_head_page (addr);return NULL ; }struct slab *kasan_addr_to_slab (const void *addr) { -if ((addr >= (void *)PAGE_OFFSET ) && (addr < high_memory)) +if (virt_addr_valid (addr))return virt_to_slab (addr);return NULL ; }
Signature · 1 line
2.25.1
PATCH v3 23/34 kasan: use kasan_addr_to_slab in print_address_description
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Use the kasan_addr_to_slab() helper in print_address_description() instead of separately invoking PageSlab() and page_slab().
--- mm/kasan/common.c | 7 +++++++ mm/kasan/report.c | 11 ++--------- 2 files changed, 9 insertions(+), 9 deletions(-)
mm/kasan/common.c +7 -0
@@ -30,6 +30,13 @@#include "kasan.h" #include "../slab.h" +struct slab *kasan_addr_to_slab (const void *addr) +{ +if (virt_addr_valid (addr)) +return virt_to_slab (addr); +return NULL ; +} +depot_stack_handle_t kasan_save_stack (gfp_t flags,bool can_alloc) {unsigned long entries[KASAN_STACK_DEPTH ];
mm/kasan/report.c +2 -9
@@ -213,13 +213,6 @@ struct page *kasan_addr_to_page(const void *addr)return NULL ; } -struct slab *kasan_addr_to_slab (const void *addr) -{ -if (virt_addr_valid (addr)) -return virt_to_slab (addr); -return NULL ; -} -static void describe_object_addr (struct kmem_cache *cache,void *object,const void *addr) {
@@ -297,12 +290,12 @@ static inline bool init_task_stack_addr(const void *addr)static void print_address_description (void *addr,u8 tag) {struct page *page =kasan_addr_to_page (addr); +struct slab *slab =kasan_addr_to_slab (addr);dump_stack_lvl (KERN_ERR );pr_err ("\n" ); -if (page &&PageSlab (page)) { -struct slab *slab =page_slab (page); +if (slab) {struct kmem_cache *cache = slab->slab_cache;void *object =nearest_obj (cache, slab, addr);
Signature · 1 line
2.25.1
PATCH v3 24/34 kasan: make kasan_addr_to_page static
In reply to [email protected]From: Andrey Konovalov <[email protected]>
As kasan_addr_to_page() is only used in report.c, rename it to addr_to_page() and make it static.
--- mm/kasan/kasan.h | 1 - mm/kasan/report.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-)
mm/kasan/kasan.h +0 -1
@@ -291,7 +291,6 @@ bool kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip);void kasan_report_invalid_free (void *object,unsigned long ip,enum kasan_report_type type); -struct page *kasan_addr_to_page (const void *addr);struct slab *kasan_addr_to_slab (const void *addr);#ifdef CONFIG_KASAN_GENERIC
mm/kasan/report.c +2 -2
@@ -206,7 +206,7 @@ static void print_track(struct kasan_track *track, const char *prefix)pr_err ("(stack is not available)\n" ); } -struct page *kasan_addr_to_page (const void *addr) +static inline struct page *addr_to_page (const void *addr) {if (virt_addr_valid (addr))return virt_to_head_page (addr);
@@ -289,7 +289,7 @@ static inline bool init_task_stack_addr(const void *addr)static void print_address_description (void *addr,u8 tag) { -struct page *page =kasan_addr_to_page (addr); +struct page *page =addr_to_page (addr);struct slab *slab =kasan_addr_to_slab (addr);dump_stack_lvl (KERN_ERR );
Signature · 1 line
2.25.1
PATCH v3 25/34 kasan: simplify print_report
In reply to [email protected]From: Andrey Konovalov <[email protected]>
To simplify reading the implementation of print_report(), remove the tagged_addr variable and rename untagged_addr to addr.
--- mm/kasan/report.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
mm/kasan/report.c +5 -6
@@ -397,17 +397,16 @@ static void print_memory_metadata(const void *addr)static void print_report (struct kasan_report_info *info) { -void *tagged_addr = info->access_addr; -void *untagged_addr =kasan_reset_tag (tagged_addr); -u8 tag =get_tag (tagged_addr); +void *addr =kasan_reset_tag (info->access_addr); +u8 tag =get_tag (info->access_addr);print_error_description (info); -if (addr_has_metadata (untagged_addr)) +if (addr_has_metadata (addr))kasan_print_tags (tag, info->first_bad_addr);pr_err ("\n" ); -if (addr_has_metadata (untagged_addr)) { -print_address_description (untagged_addr, tag); +if (addr_has_metadata (addr)) { +print_address_description (addr, tag);print_memory_metadata (info->first_bad_addr); }else {dump_stack_lvl (KERN_ERR );
Signature · 1 line
2.25.1
PATCH v3 26/34 kasan: introduce complete_report_info
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Introduce a complete_report_info() function that fills in the first_bad_addr field of kasan_report_info instead of doing it in kasan_report_*().
This function will be extended in the next patch.
--- mm/kasan/kasan.h | 5 ++++- mm/kasan/report.c | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-)
mm/kasan/kasan.h +4 -1
@@ -153,12 +153,15 @@ enum kasan_report_type { };struct kasan_report_info { +/* Filled in by kasan_report_*(). */ enum kasan_report_type type;void *access_addr; -void *first_bad_addr;size_t access_size;bool is_write;unsigned long ip; + +/* Filled in by the common reporting code. */ +void *first_bad_addr; };/* Do not change the struct layout: compiler ABI. */
mm/kasan/report.c +15 -2
@@ -413,6 +413,17 @@ static void print_report(struct kasan_report_info *info) } } +static void complete_report_info (struct kasan_report_info *info) +{ +void *addr =kasan_reset_tag (info->access_addr); + +if (info->type ==KASAN_REPORT_ACCESS ) + info->first_bad_addr =kasan_find_first_bad_addr ( + info->access_addr, info->access_size); +else + info->first_bad_addr = addr; +} +void kasan_report_invalid_free (void *ptr,unsigned long ip,enum kasan_report_type type) {unsigned long flags;
@@ -430,11 +441,12 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_ty info.type = type; info.access_addr = ptr; - info.first_bad_addr =kasan_reset_tag (ptr); info.access_size =0 ; info.is_write = false; info.ip = ip; +complete_report_info (&info); +print_report (&info);end_report (&flags, ptr);
@@ -463,11 +475,12 @@ bool kasan_report(unsigned long addr, size_t size, bool is_write, info.type =KASAN_REPORT_ACCESS ; info.access_addr = ptr; - info.first_bad_addr =kasan_find_first_bad_addr (ptr, size); info.access_size = size; info.is_write = is_write; info.ip = ip; +complete_report_info (&info); +print_report (&info);end_report (&irq_flags, ptr);
Signature · 1 line
2.25.1
PATCH v3 27/34 kasan: fill in cache and object in complete_report_info
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add cache and object fields to kasan_report_info and fill them in in complete_report_info() instead of fetching them in the middle of the report printing code.
This allows the reporting code to get access to the object information before starting printing the report. One of the following patches uses this information to determine the bug type with the tag-based modes.
--- mm/kasan/kasan.h | 2 ++ mm/kasan/report.c | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-)
mm/kasan/kasan.h +2 -0
@@ -162,6 +162,8 @@ struct kasan_report_info {/* Filled in by the common reporting code. */ void *first_bad_addr; +struct kmem_cache *cache; +void *object; };/* Do not change the struct layout: compiler ABI. */
mm/kasan/report.c +13 -8
@@ -287,19 +287,16 @@ static inline bool init_task_stack_addr(const void *addr) sizeof(init_thread_union.stack)); } -static void print_address_description (void *addr,u8 tag) +static void print_address_description (void *addr,u8 tag, +struct kasan_report_info *info) {struct page *page =addr_to_page (addr); -struct slab *slab =kasan_addr_to_slab (addr);dump_stack_lvl (KERN_ERR );pr_err ("\n" ); -if (slab) { -struct kmem_cache *cache = slab->slab_cache; -void *object =nearest_obj (cache, slab, addr); - -describe_object (cache, object, addr, tag); +if (info->cache && info->object) { +describe_object (info->cache, info->object, addr, tag);pr_err ("\n" ); }
@@ -406,7 +403,7 @@ static void print_report(struct kasan_report_info *info)pr_err ("\n" );if (addr_has_metadata (addr)) { -print_address_description (addr, tag); +print_address_description (addr, tag, info);print_memory_metadata (info->first_bad_addr); }else {dump_stack_lvl (KERN_ERR );
@@ -416,12 +413,20 @@ static void print_report(struct kasan_report_info *info)static void complete_report_info (struct kasan_report_info *info) {void *addr =kasan_reset_tag (info->access_addr); +struct slab *slab;if (info->type ==KASAN_REPORT_ACCESS ) info->first_bad_addr =kasan_find_first_bad_addr ( info->access_addr, info->access_size);else info->first_bad_addr = addr; + + slab =kasan_addr_to_slab (addr); +if (slab) { + info->cache = slab->slab_cache; + info->object =nearest_obj (info->cache, slab, addr); + }else + info->cache = info->object =NULL ; }void kasan_report_invalid_free (void *ptr,unsigned long ip,enum kasan_report_type type)
Signature · 1 line
2.25.1
PATCH v3 28/34 kasan: rework function arguments in report.c
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Pass a pointer to kasan_report_info to describe_object() and describe_object_stacks(), instead of passing the structure's fields.
The untagged pointer and the tag are still passed as separate arguments to some of the functions to avoid duplicating the untagging logic.
This is preparatory change for the next patch.
--- mm/kasan/report.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-)
mm/kasan/report.c +11 -12
@@ -213,8 +213,8 @@ static inline struct page *addr_to_page(const void *addr)return NULL ; } -static void describe_object_addr (struct kmem_cache *cache,void *object, -const void *addr) +static void describe_object_addr (const void *addr,struct kmem_cache *cache, +void *object) {unsigned long access_addr = (unsigned long )addr;unsigned long object_addr = (unsigned long )object;
@@ -242,33 +242,32 @@ static void describe_object_addr(struct kmem_cache *cache, void *object, (void *)(object_addr + cache->object_size)); } -static void describe_object_stacks (struct kmem_cache *cache,void *object, -const void *addr,u8 tag) +static void describe_object_stacks (u8 tag,struct kasan_report_info *info) {struct kasan_track *alloc_track;struct kasan_track *free_track; - alloc_track =kasan_get_alloc_track (cache, object); + alloc_track =kasan_get_alloc_track (info->cache, info->object);if (alloc_track) {print_track (alloc_track,"Allocated" );pr_err ("\n" ); } - free_track =kasan_get_free_track (cache, object, tag); + free_track =kasan_get_free_track (info->cache, info->object, tag);if (free_track) {print_track (free_track,"Freed" );pr_err ("\n" ); } -kasan_print_aux_stacks (cache, object); +kasan_print_aux_stacks (info->cache, info->object); } -static void describe_object (struct kmem_cache *cache,void *object, -const void *addr,u8 tag) +static void describe_object (const void *addr,u8 tag, +struct kasan_report_info *info) {if (kasan_stack_collection_enabled ()) -describe_object_stacks (cache, object, addr, tag); -describe_object_addr (cache, object, addr); +describe_object_stacks (tag, info); +describe_object_addr (addr, info->cache, info->object); }static inline bool kernel_or_module_addr (const void *addr)
@@ -296,7 +295,7 @@ static void print_address_description(void *addr, u8 tag,pr_err ("\n" );if (info->cache && info->object) { -describe_object (info->cache, info->object, addr, tag); +describe_object (addr, tag, info);pr_err ("\n" ); }
Signature · 1 line
2.25.1
PATCH v3 29/34 kasan: introduce kasan_complete_mode_report_info
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add bug_type and alloc/free_track fields to kasan_report_info and add a kasan_complete_mode_report_info() function that fills in these fields. This function is implemented differently for different KASAN mode.
Change the reporting code to use the filled in fields instead of invoking kasan_get_bug_type() and kasan_get_alloc/free_track().
For the Generic mode, kasan_complete_mode_report_info() invokes these functions instead. For the tag-based modes, only the bug_type field is filled in; alloc/free_track are handled in the next patch.
Using a single function that fills in these fields is required for the tag-based modes, as the values for all three fields are determined in a single procedure implemented in the following patch.
--- mm/kasan/kasan.h | 33 +++++++++++++++++---------------- mm/kasan/report.c | 30 ++++++++++++++---------------- mm/kasan/report_generic.c | 32 +++++++++++++++++--------------- mm/kasan/report_tags.c | 13 +++---------- 4 files changed, 51 insertions(+), 57 deletions(-)
mm/kasan/kasan.h +17 -16
@@ -146,6 +146,13 @@ static inline bool kasan_requires_meta(void)#define META_MEM_BYTES_PER_ROW (META_BYTES_PER_ROW * KASAN_GRANULE_SIZE)#define META_ROWS_AROUND_ADDR 2 +#define KASAN_STACK_DEPTH 64 + +struct kasan_track { +u32 pid; +depot_stack_handle_t stack; +}; +enum kasan_report_type {KASAN_REPORT_ACCESS ,KASAN_REPORT_INVALID_FREE ,
@@ -164,6 +171,11 @@ struct kasan_report_info {void *first_bad_addr;struct kmem_cache *cache;void *object; + +/* Filled in by the mode-specific reporting code. */ +const char *bug_type; +struct kasan_track alloc_track; +struct kasan_track free_track; };/* Do not change the struct layout: compiler ABI. */
@@ -189,14 +201,7 @@ struct kasan_global {#endif }; -/* Structures for keeping alloc and free tracks. */ - -#define KASAN_STACK_DEPTH 64 - -struct kasan_track { -u32 pid; -depot_stack_handle_t stack; -}; +/* Structures for keeping alloc and free meta. */ #ifdef CONFIG_KASAN_GENERIC
@@ -270,16 +275,16 @@ static inline bool addr_has_metadata(const void *addr)#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */ +void *kasan_find_first_bad_addr (void *addr,size_t size); +void kasan_complete_mode_report_info (struct kasan_report_info *info); +void kasan_metadata_fetch_row (char *buffer,void *row); +#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS )void kasan_print_tags (u8 addr_tag,const void *addr);#else static inline void kasan_print_tags (u8 addr_tag,const void *addr) { }#endif -void *kasan_find_first_bad_addr (void *addr,size_t size); -const char *kasan_get_bug_type (struct kasan_report_info *info); -void kasan_metadata_fetch_row (char *buffer,void *row); -#if defined(CONFIG_KASAN_STACK )void kasan_print_address_stack_frame (const void *addr);#else
@@ -314,10 +319,6 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags, bool can_alloc);void kasan_set_track (struct kasan_track *track,gfp_t flags);void kasan_save_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags);void kasan_save_free_info (struct kmem_cache *cache,void *object); -struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, -void *object); -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 ))
mm/kasan/report.c +14 -16
@@ -185,8 +185,7 @@ static void print_error_description(struct kasan_report_info *info)return ; } -pr_err ("BUG: KASAN: %s in %pS\n" , -kasan_get_bug_type (info), (void *)info->ip); +pr_err ("BUG: KASAN: %s in %pS\n" , info->bug_type, (void *)info->ip);if (info->access_size) pr_err("%s of size %zu at addr %px by task %s/%d\n", info->is_write ? "Write" : "Read", info->access_size,
@@ -242,31 +241,25 @@ static void describe_object_addr(const void *addr, struct kmem_cache *cache, (void *)(object_addr + cache->object_size)); } -static void describe_object_stacks (u8 tag,struct kasan_report_info *info) +static void describe_object_stacks (struct kasan_report_info *info) { -struct kasan_track *alloc_track; -struct kasan_track *free_track; - - alloc_track =kasan_get_alloc_track (info->cache, info->object); -if (alloc_track) { -print_track (alloc_track,"Allocated" ); +if (info->alloc_track.stack) { +print_track (&info->alloc_track,"Allocated" );pr_err ("\n" ); } - free_track =kasan_get_free_track (info->cache, info->object, tag); -if (free_track) { -print_track (free_track,"Freed" ); +if (info->free_track.stack) { +print_track (&info->free_track,"Freed" );pr_err ("\n" ); }kasan_print_aux_stacks (info->cache, info->object); } -static void describe_object (const void *addr,u8 tag, -struct kasan_report_info *info) +static void describe_object (const void *addr,struct kasan_report_info *info) {if (kasan_stack_collection_enabled ()) -describe_object_stacks (tag, info); +describe_object_stacks (info);describe_object_addr (addr, info->cache, info->object); }
@@ -295,7 +288,7 @@ static void print_address_description(void *addr, u8 tag,pr_err ("\n" );if (info->cache && info->object) { -describe_object (addr, tag, info); +describe_object (addr, info);pr_err ("\n" ); }
@@ -426,6 +419,9 @@ static void complete_report_info(struct kasan_report_info *info) info->object =nearest_obj (info->cache, slab, addr); }else info->cache = info->object =NULL ; + +/* Fill in mode-specific report info fields. */ +kasan_complete_mode_report_info (info); }void kasan_report_invalid_free (void *ptr,unsigned long ip,enum kasan_report_type type)
@@ -443,6 +439,7 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_tystart_report (&flags, true); +memset (&info,0 ,sizeof (info)); info.type = type; info.access_addr = ptr; info.access_size =0 ;
@@ -477,6 +474,7 @@ bool kasan_report(unsigned long addr, size_t size, bool is_write,start_report (&irq_flags, true); +memset (&info,0 ,sizeof (info)); info.type =KASAN_REPORT_ACCESS ; info.access_addr = ptr; info.access_size = size;
mm/kasan/report_generic.c +17 -15
@@ -109,7 +109,7 @@ static const char *get_wild_bug_type(struct kasan_report_info *info)return bug_type; } -const char *kasan_get_bug_type (struct kasan_report_info *info) +static const char *get_bug_type (struct kasan_report_info *info) {/* * If access_size is a negative number, then it has reason to be
@@ -127,25 +127,27 @@ const char *kasan_get_bug_type(struct kasan_report_info *info)return get_wild_bug_type (info); } -struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, -void *object) +void kasan_complete_mode_report_info (struct kasan_report_info *info) {struct kasan_alloc_meta *alloc_meta; +struct kasan_free_meta *free_meta; - alloc_meta =kasan_get_alloc_meta (cache, object); -if (!alloc_meta) -return NULL ; + info->bug_type =get_bug_type (info); -return &alloc_meta->alloc_track; -} +if (!info->cache || !info->object) +return ; -struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag) -{ -if (*(u8 *)kasan_mem_to_shadow (object) !=KASAN_SLAB_FREETRACK ) -return NULL ; -/* Free meta must be present with KASAN_SLAB_FREETRACK. */ -return &kasan_get_free_meta (cache, object)->free_track; + alloc_meta =kasan_get_alloc_meta (info->cache, info->object); +if (alloc_meta) +memcpy (&info->alloc_track, &alloc_meta->alloc_track, +sizeof (info->alloc_track)); + +if (*(u8 *)kasan_mem_to_shadow (info->object) ==KASAN_SLAB_FREETRACK ) { +/* Free meta must be present with KASAN_SLAB_FREETRACK. */ + free_meta =kasan_get_free_meta (info->cache, info->object); +memcpy (&info->free_track, &free_meta->free_track, +sizeof (info->free_track)); + } }void kasan_metadata_fetch_row (char *buffer,void *row)
mm/kasan/report_tags.c +3 -10
@@ -6,7 +6,7 @@#include "kasan.h" -const char *kasan_get_bug_type (struct kasan_report_info *info) +static const char *get_bug_type (struct kasan_report_info *info) {/* * If access_size is a negative number, then it has reason to be
@@ -22,14 +22,7 @@ const char *kasan_get_bug_type(struct kasan_report_info *info)return "invalid-access" ; } -struct kasan_track *kasan_get_alloc_track (struct kmem_cache *cache, -void *object) +void kasan_complete_mode_report_info (struct kasan_report_info *info) { -return NULL ; -} - -struct kasan_track *kasan_get_free_track (struct kmem_cache *cache, -void *object,u8 tag) -{ -return NULL ; + info->bug_type =get_bug_type (info); }
Signature · 1 line
2.25.1
PATCH v3 30/34 kasan: implement stack ring for tag-based modes
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Implement storing stack depot handles for alloc/free stack traces for slab objects for the tag-based KASAN modes in a ring buffer.
This ring buffer is referred to as the stack ring.
On each alloc/free of a slab object, the tagged address of the object and the current stack trace are recorded in the stack ring.
On each bug report, if the accessed address belongs to a slab object, the stack ring is scanned for matching entries. The newest entries are used to print the alloc/free stack traces in the report: one entry for alloc and one for free.
The number of entries in the stack ring is fixed in this patch, but one of the following patches adds a command-line argument to control it.
---
Changes v2->v3:
- Drop redundant check for concurrent overwrites of stack ring entries.
Changes v1->v2:
- Only use the atomic type for pos, use READ/WRITE_ONCE() for the rest.
- Rename KASAN_STACK_RING_ENTRIES to KASAN_STACK_RING_SIZE.
- Rename object local variable in kasan_complete_mode_report_info() to ptr to match the name in kasan_stack_ring_entry.
- Detect stack ring entry slots that are being written to.
- Use read-write lock to disallow reading half-written stack ring entries.
- Add a comment about the stack ring being best-effort.
--- mm/kasan/kasan.h | 21 +++++++++++++ mm/kasan/report_tags.c | 71 ++++++++++++++++++++++++++++++++++++++++++ mm/kasan/tags.c | 50 +++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+)
mm/kasan/kasan.h +21 -0
@@ -2,6 +2,7 @@#ifndef __MM_KASAN_KASAN_H#define __MM_KASAN_KASAN_H +#include <linux/atomic.h> #include <linux/kasan.h> #include <linux/kasan-tags.h> #include <linux/kfence.h>
@@ -233,6 +234,26 @@ struct kasan_free_meta {#endif /* CONFIG_KASAN_GENERIC */ +#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS ) + +struct kasan_stack_ring_entry { +void *ptr; +size_t size; +u32 pid; +depot_stack_handle_t stack; +bool is_free; +}; + +#define KASAN_STACK_RING_SIZE (32 << 10) + +struct kasan_stack_ring { +rwlock_t lock; +atomic64_t pos; +struct kasan_stack_ring_entry entries[KASAN_STACK_RING_SIZE ]; +}; + +#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ +#if IS_ENABLED (CONFIG_KASAN_KUNIT_TEST )/* Used in KUnit-compatible KASAN tests. */ struct kunit_kasan_status {
mm/kasan/report_tags.c +71 -0
84 lines not on this page · raw message
mm/kasan/tags.c +50 -0
67 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v3 31/34 kasan: support kasan.stacktrace for SW_TAGS
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add support for the kasan.stacktrace command-line argument for Software Tag-Based KASAN.
The following patch adds a command-line argument for selecting the stack ring size, and, as the stack ring is supported by both the Software and the Hardware Tag-Based KASAN modes, it is natural that both of them have support for kasan.stacktrace too.
---
Changes v1->v2:
- This is a new patch.
--- Documentation/dev-tools/kasan.rst | 15 ++++++----- mm/kasan/hw_tags.c | 39 +--------------------------- mm/kasan/kasan.h | 36 +++++++++++++++++--------- mm/kasan/sw_tags.c | 5 +++- mm/kasan/tags.c | 43 +++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 57 deletions(-)
Documentation/dev-tools/kasan.rst +9 -6
@@ -111,9 +111,15 @@ parameter can be used to control panic and reporting behaviour: report or also panic the kernel (default: ``report``). The panic happens even if ``kasan_multi_shot`` is enabled. -Hardware Tag-Based KASAN mode (see the section about various modes below) is -intended for use in production as a security mitigation. Therefore, it supports -additional boot parameters that allow disabling KASAN or controlling features: +Software and Hardware Tag-Based KASAN modes (see the section about various +modes below) support disabling stack trace collection: + +- ``kasan.stacktrace=off`` or ``=on`` disables or enables alloc and free stack + traces collection (default: ``on``). + +Hardware Tag-Based KASAN mode is intended for use in production as a security +mitigation. Therefore, it supports additional boot parameters that allow +disabling KASAN altogether or controlling its features: - ``kasan=off`` or ``=on`` controls whether KASAN is enabled (default: ``on``).
@@ -132,9 +138,6 @@ additional boot parameters that allow disabling KASAN or controlling features: - ``kasan.vmalloc=off`` or ``=on`` disables or enables tagging of vmalloc allocations (default: ``on``). -- ``kasan.stacktrace=off`` or ``=on`` disables or enables alloc and free stack - traces collection (default: ``on``). - Error reports ~~~~~~~~~~~~~
mm/kasan/hw_tags.c +1 -38
@@ -38,16 +38,9 @@ enum kasan_arg_vmalloc { KASAN_ARG_VMALLOC_ON, }; -enum kasan_arg_stacktrace { -KASAN_ARG_STACKTRACE_DEFAULT , -KASAN_ARG_STACKTRACE_OFF , -KASAN_ARG_STACKTRACE_ON , -}; -static enum kasan_arg kasan_arg__ro_after_init ;static enum kasan_arg_mode kasan_arg_mode__ro_after_init ;static enum kasan_arg_vmalloc kasan_arg_vmalloc__initdata ; -static enum kasan_arg_stacktrace kasan_arg_stacktrace__initdata ;/* * Whether KASAN is enabled at all.
@@ -66,9 +59,6 @@ EXPORT_SYMBOL_GPL(kasan_mode);/* Whether to enable vmalloc tagging. */ DEFINE_STATIC_KEY_TRUE (kasan_flag_vmalloc); -/* Whether to collect alloc/free stack traces. */ -DEFINE_STATIC_KEY_TRUE (kasan_flag_stacktrace); -/* kasan=off/on */ static int __init early_kasan_flag (char *arg) {
@@ -122,23 +112,6 @@ static int __init early_kasan_flag_vmalloc(char *arg) }early_param ("kasan.vmalloc" , early_kasan_flag_vmalloc); -/* kasan.stacktrace=off/on */ -static int __init early_kasan_flag_stacktrace (char *arg) -{ -if (!arg) -return -EINVAL ; - -if (!strcmp (arg,"off" )) - kasan_arg_stacktrace =KASAN_ARG_STACKTRACE_OFF ; -else if (!strcmp (arg,"on" )) - kasan_arg_stacktrace =KASAN_ARG_STACKTRACE_ON ; -else -return -EINVAL ; - -return 0 ; -} -early_param ("kasan.stacktrace" , early_kasan_flag_stacktrace); -static inline const char *kasan_mode_info (void ) {if (kasan_mode ==KASAN_MODE_ASYNC )
@@ -213,17 +186,7 @@ void __init kasan_init_hw_tags(void)break ; } -switch (kasan_arg_stacktrace) { -case KASAN_ARG_STACKTRACE_DEFAULT : -/* Default is specified by kasan_flag_stacktrace definition. */ -break ; -case KASAN_ARG_STACKTRACE_OFF : -static_branch_disable (&kasan_flag_stacktrace); -break ; -case KASAN_ARG_STACKTRACE_ON : -static_branch_enable (&kasan_flag_stacktrace); -break ; - } +kasan_init_tags ();/* KASAN is now initialized, enable it. */ static_branch_enable (&kasan_flag_enabled);
mm/kasan/kasan.h +24 -12
@@ -8,13 +8,31 @@#include <linux/kfence.h> #include <linux/stackdepot.h> -#ifdef CONFIG_KASAN_HW_TAGS +#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS )#include <linux/static_key.h> + +DECLARE_STATIC_KEY_TRUE (kasan_flag_stacktrace); + +static inline bool kasan_stack_collection_enabled (void ) +{ +return static_branch_unlikely (&kasan_flag_stacktrace); +} + +#else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ + +static inline bool kasan_stack_collection_enabled (void ) +{ +return true; +} + +#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ + +#ifdef CONFIG_KASAN_HW_TAGS +#include "../slab.h" DECLARE_STATIC_KEY_TRUE (kasan_flag_vmalloc); -DECLARE_STATIC_KEY_TRUE (kasan_flag_stacktrace);enum kasan_mode { KASAN_MODE_SYNC,
@@ -29,11 +47,6 @@ static inline bool kasan_vmalloc_enabled(void)return static_branch_likely (&kasan_flag_vmalloc); } -static inline bool kasan_stack_collection_enabled (void ) -{ -return static_branch_unlikely (&kasan_flag_stacktrace); -} -static inline bool kasan_async_fault_possible (void ) {return kasan_mode ==KASAN_MODE_ASYNC || kasan_mode ==KASAN_MODE_ASYMM ;
@@ -46,11 +59,6 @@ static inline bool kasan_sync_fault_possible(void)#else /* CONFIG_KASAN_HW_TAGS */ -static inline bool kasan_stack_collection_enabled (void ) -{ -return true; -} -static inline bool kasan_async_fault_possible (void ) {return false;
@@ -410,6 +418,10 @@ static inline void kasan_enable_tagging(void) { }#endif /* CONFIG_KASAN_HW_TAGS */ +#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS ) +void __init kasan_init_tags (void ); +#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ +#if defined(CONFIG_KASAN_HW_TAGS ) &&IS_ENABLED (CONFIG_KASAN_KUNIT_TEST )void kasan_force_async_fault (void );
mm/kasan/sw_tags.c +4 -1
@@ -42,7 +42,10 @@ void __init kasan_init_sw_tags(void)for_each_possible_cpu (cpu)per_cpu (prng_state, cpu) = (u32 )get_cycles (); -pr_info ("KernelAddressSanitizer initialized (sw-tags)\n" ); +kasan_init_tags (); + +pr_info ("KernelAddressSanitizer initialized (sw-tags, stacktrace=%s)\n" , +kasan_stack_collection_enabled () ?"on" :"off" ); }/*
mm/kasan/tags.c +43 -0
54 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v3 32/34 kasan: dynamically allocate stack ring entries
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Instead of using a large static array, allocate the stack ring dynamically via memblock_alloc().
The size of the stack ring is controlled by a new kasan.stack_ring_size command-line parameter. When kasan.stack_ring_size is not provided, the default value of 32 << 10 is used.
When the stack trace collection is disabled via kasan.stacktrace=off, the stack ring is not allocated.
---
Changes v2->v3:
- Move KASAN_STACK_RING_SIZE_DEFAULT definition to tags.c
- Improve comment for early_kasan_flag_stack_ring_size().
- WARN_ON and disable stack traces on failed memblock_alloc.
- Add kasan.stack_ring_size to documentation.
Changes v1->v2:
- This is a new patch.
--- Documentation/dev-tools/kasan.rst | 4 +++- mm/kasan/kasan.h | 5 ++--- mm/kasan/report_tags.c | 4 ++-- mm/kasan/tags.c | 25 ++++++++++++++++++++++++- 4 files changed, 31 insertions(+), 7 deletions(-)
Documentation/dev-tools/kasan.rst +3 -1
@@ -112,10 +112,12 @@ parameter can be used to control panic and reporting behaviour: if ``kasan_multi_shot`` is enabled. Software and Hardware Tag-Based KASAN modes (see the section about various -modes below) support disabling stack trace collection: +modes below) support altering stack trace collection behavior: - ``kasan.stacktrace=off`` or ``=on`` disables or enables alloc and free stack traces collection (default: ``on``). +- ``kasan.stack_ring_size=<number of entries>`` specifies the number of entries + in the stack ring (default: ``32768``). Hardware Tag-Based KASAN mode is intended for use in production as a security mitigation. Therefore, it supports additional boot parameters that allow
mm/kasan/kasan.h +2 -3
@@ -252,12 +252,11 @@ struct kasan_stack_ring_entry { bool is_free; }; -#define KASAN_STACK_RING_SIZE (32 << 10) -struct kasan_stack_ring {rwlock_t lock; +size_t size;atomic64_t pos; -struct kasan_stack_ring_entry entries[KASAN_STACK_RING_SIZE ]; +struct kasan_stack_ring_entry *entries; };#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
mm/kasan/report_tags.c +2 -2
@@ -56,11 +56,11 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info)* entries relevant to the buggy object can be overwritten. */ -for (u64 i = pos -1 ; i != pos -1 -KASAN_STACK_RING_SIZE ; i--) { +for (u64 i = pos -1 ; i != pos -1 - stack_ring.size; i--) {if (alloc_found && free_found)break ; - entry = &stack_ring.entries[i %KASAN_STACK_RING_SIZE ]; + entry = &stack_ring.entries[i % stack_ring.size];/* Paired with smp_store_release() in save_stack_info(). */ ptr = (void *)smp_load_acquire (&entry->ptr);
mm/kasan/tags.c +24 -1
@@ -10,6 +10,7 @@#include <linux/init.h> #include <linux/kasan.h> #include <linux/kernel.h> +#include <linux/memblock.h> #include <linux/memory.h> #include <linux/mm.h> #include <linux/static_key.h>
@@ -19,6 +20,8 @@#include "kasan.h" #include "../slab.h" +#define KASAN_STACK_RING_SIZE_DEFAULT (32 << 10) +enum kasan_arg_stacktrace {KASAN_ARG_STACKTRACE_DEFAULT ,KASAN_ARG_STACKTRACE_OFF ,
@@ -52,6 +55,16 @@ static int __init early_kasan_flag_stacktrace(char *arg) }early_param ("kasan.stacktrace" , early_kasan_flag_stacktrace); +/* kasan.stack_ring_size=<number of entries> */ +static int __init early_kasan_flag_stack_ring_size (char *arg) +{ +if (!arg) +return -EINVAL ; + +return kstrtoul (arg,0 , &stack_ring.size); +} +early_param ("kasan.stack_ring_size" , early_kasan_flag_stack_ring_size); +void __init kasan_init_tags (void ) {switch (kasan_arg_stacktrace) {
@@ -65,6 +78,16 @@ void __init kasan_init_tags(void)static_branch_enable (&kasan_flag_stacktrace);break ; } + +if (kasan_stack_collection_enabled ()) { +if (!stack_ring.size) + stack_ring.size =KASAN_STACK_RING_SIZE_DEFAULT ; + stack_ring.entries =memblock_alloc ( +sizeof (stack_ring.entries[0 ]) * stack_ring.size, +SMP_CACHE_BYTES ); +if (WARN_ON (!stack_ring.entries)) +static_branch_disable (&kasan_flag_stacktrace); + } } static void save_stack_info(struct kmem_cache *cache, void *object,
@@ -86,7 +109,7 @@ static void save_stack_info(struct kmem_cache *cache, void *object, next: pos =atomic64_fetch_add (1 , &stack_ring.pos); - entry = &stack_ring.entries[pos %KASAN_STACK_RING_SIZE ]; + entry = &stack_ring.entries[pos % stack_ring.size];/* Detect stack ring entry slots that are being written to. */ old_ptr =READ_ONCE (entry->ptr);
Signature · 1 line
2.25.1
PATCH v3 33/34 kasan: better identify bug types for tag-based modes
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Identify the bug type for the tag-based modes based on the stack trace entries found in the stack ring.
If a free entry is found first (meaning that it was added last), mark the bug as use-after-free. If an alloc entry is found first, mark the bug as slab-out-of-bounds. Otherwise, assign the common bug type.
This change returns the functionalify of the previously dropped CONFIG_KASAN_TAGS_IDENTIFY.
--- mm/kasan/report_tags.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-)
mm/kasan/report_tags.c +21 -4
@@ -10,7 +10,7 @@extern struct kasan_stack_ring stack_ring; -static const char *get_bug_type (struct kasan_report_info *info) +static const char *get_common_bug_type (struct kasan_report_info *info) {/* * If access_size is a negative number, then it has reason to be
@@ -37,9 +37,8 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info)bool is_free;bool alloc_found = false, free_found = false; - info->bug_type =get_bug_type (info); - -if (!info->cache || !info->object) +if (!info->cache || !info->object) { + info->bug_type =get_common_bug_type (info);return ; }
@@ -84,6 +83,13 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info) info->free_track.pid = pid; info->free_track.stack = stack; free_found = true; + +/* +* If a free entry is found first, the bug is likely +* a use-after-free. +*/ +if (!info->bug_type) + info->bug_type ="use-after-free" ; }else {/* Second alloc of the same object. Give up. */ if (alloc_found)
@@ -92,8 +98,19 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info) info->alloc_track.pid = pid; info->alloc_track.stack = stack; alloc_found = true; + +/* +* If an alloc entry is found first, the bug is likely +* an out-of-bounds. +*/ +if (!info->bug_type) + info->bug_type ="slab-out-of-bounds" ; } }write_unlock_irqrestore (&stack_ring.lock, flags); + +/* Assign the common bug type if no entries were found. */ +if (!info->bug_type) + info->bug_type =get_common_bug_type (info); }
Signature · 1 line
2.25.1
PATCH v3 34/34 kasan: add another use-after-free test
In reply to [email protected]From: Andrey Konovalov <[email protected]>
Add a new use-after-free test that checks that KASAN detects use-after-free when another object was allocated in the same slot.
This test is mainly relevant for the tag-based modes, which do not use quarantine.
Once [1] is resolved, this test can be extended to check that the stack traces in the report point to the proper kmalloc/kfree calls.
[1] https://bugzilla.kernel.org/show_bug.cgi?id=212203
---
Changes v2->v3:
- This is a new patch.
--- lib/test_kasan.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
lib/test_kasan.c +24 -0
@@ -612,6 +612,29 @@ static void kmalloc_uaf2(struct kunit *test)kfree (ptr2); } +/* +* Check that KASAN detects use-after-free when another object was allocated in +* the same slot. Relevant for the tag-based modes, which do not use quarantine. +*/ +static void kmalloc_uaf3 (struct kunit *test) +{ +char *ptr1, *ptr2; +size_t size =100 ; + +/* This test is specifically crafted for tag-based modes. */ +KASAN_TEST_NEEDS_CONFIG_OFF (test,CONFIG_KASAN_GENERIC ); + + ptr1 =kmalloc (size,GFP_KERNEL ); +KUNIT_ASSERT_NOT_ERR_OR_NULL (test, ptr1); +kfree (ptr1); + + ptr2 =kmalloc (size,GFP_KERNEL ); +KUNIT_ASSERT_NOT_ERR_OR_NULL (test, ptr2); +kfree (ptr2); + +KUNIT_EXPECT_KASAN_FAIL (test, ((volatile char *)ptr1)[8 ]); +} +static void kfree_via_page (struct kunit *test) {char *ptr;
@@ -1382,6 +1405,7 @@ static struct kunit_case kasan_kunit_test_cases[] = {KUNIT_CASE (kmalloc_uaf),KUNIT_CASE (kmalloc_uaf_memset),KUNIT_CASE (kmalloc_uaf2), +KUNIT_CASE (kmalloc_uaf3),KUNIT_CASE (kfree_via_page),KUNIT_CASE (kfree_via_phys), KUNIT_CASE(kmem_cache_oob),
Signature · 1 line
2.25.1
Andrey Konovalov
reviewerQuoted 69 lines · On Mon, Sep 5, 2022 at 11:05 PM <[email protected]> wrote:
From: Andrey Konovalov <[email protected]>
This series makes the tag-based KASAN modes use a ring buffer for storing stack depot handles for alloc/free stack traces for slab objects instead of per-object metadata. This ring buffer is referred to as the stack ring.
On each alloc/free of a slab object, the tagged address of the object and the current stack trace are recorded in the stack ring.
On each bug report, if the accessed address belongs to a slab object, the stack ring is scanned for matching entries. The newest entries are used to print the alloc/free stack traces in the report: one entry for alloc and one for free.
The advantages of this approach over storing stack trace handles in per-object metadata with the tag-based KASAN modes:
- Allows to find relevant stack traces for use-after-free bugs without using quarantine for freed memory. (Currently, if the object was reallocated multiple times, the report contains the latest alloc/free stack traces, not necessarily the ones relevant to the buggy allocation.)
- Allows to better identify and mark use-after-free bugs, effectively making theCONFIG_KASAN_TAGS_IDENTIFYfunctionality always-on.
- Has fixed memory overhead.The disadvantage:
- If the affected object was allocated/freed long before the bug happened and the stack trace events were purged from the stack ring, the report will have no stack traces.
Discussion
==========The proposed implementation of the stack ring uses a single ring buffer for the whole kernel. This might lead to contention due to atomic accesses to the ring buffer index on multicore systems.
At this point, it is unknown whether the performance impact from this contention would be significant compared to the slowdown introduced by collecting stack traces due to the planned changes to the latter part, see the section below.
For now, the proposed implementation is deemed to be good enough, but this might need to be revisited once the stack collection becomes faster.
A considered alternative is to keep a separate ring buffer for each CPU and then iterate over all of them when printing a bug report. This approach requires somehow figuring out which of the stack rings has the freshest stack traces for an object if multiple stack rings have them.
Further plans
=============This series is a part of an effort to make KASAN stack trace collection suitable for production. This requires stack trace collection to be fast and memory-bounded.
The planned steps are:
1. Speed up stack trace collection (potentially, by using SCS; patches on-hold until steps #2 and #3 are completed).
2. Keep stack trace handles in the stack ring (this series).
3. Add a memory-bounded mode to stack depot or provide an alternative memory-bounded stack storage.
4. Potentially, implement stack trace collection sampling to minimize the performance impact.
Quoted 1 line
Thanks!
Hi Andrew,
Could you consider picking up this series into mm?
Most of the patches have a Reviewed-by tag from Marco, and I've addressed the last few comments he had in v3.
Thanks!
Marco Elver
Acked-byQuoted 73 lines · Andrey Konovalov
Quoted 69 lines · On Mon, Sep 5, 2022 at 11:05 PM <[email protected]> wrote:
From: Andrey Konovalov <[email protected]>
This series makes the tag-based KASAN modes use a ring buffer for storing stack depot handles for alloc/free stack traces for slab objects instead of per-object metadata. This ring buffer is referred to as the stack ring.
On each alloc/free of a slab object, the tagged address of the object and the current stack trace are recorded in the stack ring.
On each bug report, if the accessed address belongs to a slab object, the stack ring is scanned for matching entries. The newest entries are used to print the alloc/free stack traces in the report: one entry for alloc and one for free.
The advantages of this approach over storing stack trace handles in per-object metadata with the tag-based KASAN modes:
- Allows to find relevant stack traces for use-after-free bugs without using quarantine for freed memory. (Currently, if the object was reallocated multiple times, the report contains the latest alloc/free stack traces, not necessarily the ones relevant to the buggy allocation.)
- Allows to better identify and mark use-after-free bugs, effectively making theCONFIG_KASAN_TAGS_IDENTIFYfunctionality always-on.
- Has fixed memory overhead.The disadvantage:
- If the affected object was allocated/freed long before the bug happened and the stack trace events were purged from the stack ring, the report will have no stack traces.
Discussion
==========The proposed implementation of the stack ring uses a single ring buffer for the whole kernel. This might lead to contention due to atomic accesses to the ring buffer index on multicore systems.
At this point, it is unknown whether the performance impact from this contention would be significant compared to the slowdown introduced by collecting stack traces due to the planned changes to the latter part, see the section below.
For now, the proposed implementation is deemed to be good enough, but this might need to be revisited once the stack collection becomes faster.
A considered alternative is to keep a separate ring buffer for each CPU and then iterate over all of them when printing a bug report. This approach requires somehow figuring out which of the stack rings has the freshest stack traces for an object if multiple stack rings have them.
Further plans
=============This series is a part of an effort to make KASAN stack trace collection suitable for production. This requires stack trace collection to be fast and memory-bounded.
The planned steps are:
1. Speed up stack trace collection (potentially, by using SCS; patches on-hold until steps #2 and #3 are completed).
2. Keep stack trace handles in the stack ring (this series).
3. Add a memory-bounded mode to stack depot or provide an alternative memory-bounded stack storage.
4. Potentially, implement stack trace collection sampling to minimize the performance impact.Quoted 1 line
Thanks!
Quoted 8 lines
Hi Andrew,
Could you consider picking up this series into mm?
Most of the patches have a Reviewed-by tag from Marco, and I've addressed the last few comments he had in v3.
Thanks!
I see them in -next, so they've been picked up?
FWIW, my concerns have been addressed, so for patches that don't yet have my Reviewed:
Andrew Morton
maintainerQuoted 5 lines · On Mon, 12 Sep 2022 11:39:07 +0200 Marco Elver <[email protected]> wrote:
...
Quoted 2 lines
Hi Andrew,
Quoted 8 lines
Quoted 6 lines
Could you consider picking up this series into mm?
Most of the patches have a Reviewed-by tag from Marco, and I've addressed the last few comments he had in v3.
Thanks!
I see them in -next, so they've been picked up?
yup.
Quoted 5 lines
FWIW, my concerns have been addressed, so for patches that don't yet have my Reviewed:
Acked-by Marco Elver <[email protected]>
Updated, thanks.
Quoted 19 lines · Andrew Morton
Quoted 5 lines · On Mon, 12 Sep 2022 11:39:07 +0200 Marco Elver <[email protected]> wrote:
...
Quoted 2 lines
Hi Andrew,
Quoted 8 lines
Quoted 6 lines
Could you consider picking up this series into mm?
Most of the patches have a Reviewed-by tag from Marco, and I've addressed the last few comments he had in v3.
Thanks!
I see them in -next, so they've been picked up?
yup.
Quoted 7 lines
Quoted 5 lines
FWIW, my concerns have been addressed, so for patches that don't yet have my Reviewed:
Acked-by Marco Elver <[email protected]>Updated, thanks.
Hit the following with the latest mm-unstable. Please take a look. Thanks.
BUG: rwlock bad magic on CPU#0, swapper/0, ffffffdc589d8218 CPU: 0 PID: 0 Comm: swapper Not tainted 6.0.0-rc3-lockdep+ #36 Call trace: dump_backtrace+0xfc/0x14c show_stack+0x24/0x58 dump_stack_lvl+0x7c/0xa0 dump_stack+0x18/0x44 rwlock_bug+0x88/0x8c do_raw_read_unlock+0x7c/0x90 _raw_read_unlock_irqrestore+0x54/0xa0 save_stack_info+0x100/0x118 kasan_save_alloc_info+0x20/0x2c __kasan_slab_alloc+0x90/0x94 early_kmem_cache_node_alloc+0x8c/0x1a8 __kmem_cache_create+0x1ac/0x338 create_boot_cache+0xac/0xec kmem_cache_init+0x8c/0x174 mm_init+0x3c/0x78 start_kernel+0x188/0x49c
Andrey Konovalov
reviewerQuoted 14 lines · Yu Zhao
Hit the following with the latest mm-unstable. Please take a look. Thanks.
BUG: rwlock bad magic on CPU#0, swapper/0, ffffffdc589d8218 CPU: 0 PID: 0 Comm: swapper Not tainted 6.0.0-rc3-lockdep+ #36 Call trace: dump_backtrace+0xfc/0x14c show_stack+0x24/0x58 dump_stack_lvl+0x7c/0xa0 dump_stack+0x18/0x44 rwlock_bug+0x88/0x8c do_raw_read_unlock+0x7c/0x90 _raw_read_unlock_irqrestore+0x54/0xa0 save_stack_info+0x100/0x118
Quoted 8 lines
kasan_save_alloc_info+0x20/0x2c __kasan_slab_alloc+0x90/0x94 early_kmem_cache_node_alloc+0x8c/0x1a8 __kmem_cache_create+0x1ac/0x338 create_boot_cache+0xac/0xec kmem_cache_init+0x8c/0x174 mm_init+0x3c/0x78 start_kernel+0x188/0x49c
Hi Yu,
Just mailed a fix.
Thank you for the report!