kasan: boot parameters for hardware tag-based mode
Subsystems (10)
v4 cover kasan: boot parameters for hardware tag-based mode
=== Overview
Hardware tag-based KASAN mode [1] is intended to eventually be used in production as a security mitigation. Therefore there's a need for finer control over KASAN features and for an existence of a kill switch.
This patchset adds a few boot parameters for hardware tag-based KASAN that allow to disable or otherwise control particular KASAN features, as well as provides some initial optimizations for running KASAN in production.
There's another planned patchset what will further optimize hardware tag-based KASAN, provide proper benchmarking and tests, and will fully enable tag-based KASAN for production use.
Hardware tag-based KASAN relies on arm64 Memory Tagging Extension (MTE)
[2] to perform memory and pointer tagging. Please see [3] and [4] for detailed analysis of how MTE helps to fight memory safety problems.
The features that can be controlled are:
1. Whether KASAN is enabled at all.
2. Whether KASAN collects and saves alloc/free stacks.
3. Whether KASAN panics on a detected bug or not.
The patch titled "kasan: add and integrate kasan boot parameters" of this series adds a few new boot parameters.
kasan.mode allows to choose one of three main modes:
- kasan.mode=off - KASAN is disabled, no tag checks are performed
- kasan.mode=prod - only essential production features are enabled
- kasan.mode=full - all KASAN features are enabled
The chosen mode provides default control values for the features mentioned above. However it's also possible to override the default values by providing:
- kasan.stacktrace=off/on - enable stacks collection (default: on for mode=full, otherwise off)
- kasan.fault=report/panic - only report tag fault or also panic (default: report)
If kasan.mode parameter is not provided, it defaults to full when CONFIG_DEBUG_KERNEL is enabled, and to prod otherwise.
It is essential that switching between these modes doesn't require rebuilding the kernel with different configs, as this is required by the Android GKI (Generic Kernel Image) initiative.
=== Benchmarks
For now I've only performed a few simple benchmarks such as measuring kernel boot time and slab memory usage after boot. There's an upcoming patchset which will optimize KASAN further and include more detailed benchmarking results.
The benchmarks were performed in QEMU and the results below exclude the slowdown caused by QEMU memory tagging emulation (as it's different from the slowdown that will be introduced by hardware and is therefore irrelevant).
KASAN_HW_TAGS=y + kasan.mode=off introduces no performance or memory impact compared to KASAN_HW_TAGS=n.
kasan.mode=prod (manually excluding tagging) introduces 3% of performance and no memory impact (except memory used by hardware to store tags) compared to kasan.mode=off.
kasan.mode=full has about 40% performance and 30% memory impact over kasan.mode=prod. Both come from alloc/free stack collection.
=== Notes
This patchset is available here:
https://github.com/xairy/linux/tree/up-boot-mte-v4
This patchset is based on v11 of "kasan: add hardware tag-based mode for arm64" patchset [1].
For testing in QEMU hardware tag-based KASAN requires:
1. QEMU built from master [6] (use "-machine virt,mte=on -cpu max" arguments to run).
2. GCC version 10.
[1] https://lore.kernel.org/linux-arm-kernel/[email protected]/T/#t
[2] https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety
[3] https://arxiv.org/pdf/1802.09517.pdf
[4] https://github.com/microsoft/MSRC-Security-Research/blob/master/papers/2020/Security%20analysis%20of%20memory%20tagging.pdf
[5] https://source.android.com/devices/architecture/kernel/generic-kernel-image
[6] https://github.com/qemu/qemu
=== Tags
=== History
Changes v3 -> v4:
- Rebase onto v11 of the HW_TAGS series.
- Don't leak objects for generic KASAN when metadata doesn't fit.
- Clarify metadata rework patch title.
- Drop unnecessary returns kasan_unpoison_object_data() and kasan_poison_slab().
Changes v2 -> v3:
- Rebase onto v10 of the HW_TAGS series.
- Add missing return type for kasan_enabled().
- Always define random_tag() as a function.
- Mark kasan wrappers as __always_inline.
- Don't "kasan: simplify kasan_poison_kfree" as it's based on a false assumption, add a comment instead.
- Address documentation comments.
- Use <linux/static_key.h> instead of <linux/jump_label.h>.
- Rework switches in mm/kasan/hw_tags.c.
- Don't init tag in ____kasan_kmalloc().
- Correctly check SLAB_TYPESAFE_BY_RCU flag in mm/kasan/common.c.
- Readability fixes for "kasan: clean up metadata allocation and usage".
- Change kasan_never_merge() to return SLAB_KASAN instead of excluding it from flags.
- (Vincenzo) Address concerns from checkpatch.pl (courtesy of Marco Elver).
Changes v1 -> v2:
- Rebased onto v9 of the HW_TAGS patchset.
- Don't initialize static branches in kasan_init_hw_tags_cpu(), as cpu_enable_mte() can't sleep; do in in kasan_init_hw_tags() instead.
- Rename kasan.stacks to kasan.stacktrace.
Changes RFC v2 -> v1:
- Rebrand the patchset from fully enabling production use to partially addressing that; another optimization and testing patchset will be required.
- Rebase onto v8 of KASAN_HW_TAGS series.
- Fix "ASYNC" -> "async" typo.
- Rework depends condition for VMAP_STACK and update config text.
- Remove unneeded reset_tag() macro, use kasan_reset_tag() instead.
- Rename kasan.stack to kasan.stacks to avoid confusion with stack instrumentation.
- Introduce kasan_stack_collection_enabled() and kasan_is_enabled() helpers.
- Simplify kasan_stack_collection_enabled() usage.
- Rework SLAB_KASAN flag and metadata allocation (see the corresponding patch for details).
- Allow cache merging with KASAN_HW_TAGS when kasan.stacks is off.
- Use sync mode dy default for both prod and full KASAN modes.
- Drop kasan.trap=sync/async boot parameter, as async mode isn't supported yet.
- Choose prod or full mode depending on CONFIG_DEBUG_KERNEL when no kasan.mode boot parameter is provided.
- Drop krealloc optimization changes, those will be included in a separate patchset.
- Update KASAN documentation to mention boot parameters.
Changes RFC v1 -> RFC v2:
- Rework boot parameters.
- Drop __init from empty kasan_init_tags() definition.
- Add cpu_supports_mte() helper that can be used during early boot and use it in kasan_init_tags()
- Lots of new KASAN optimization commits.
Andrey Konovalov (19):
kasan: simplify quarantine_put call site
kasan: rename get_alloc/free_info
kasan: introduce set_alloc_info
kasan, arm64: unpoison stack only with CONFIG_KASAN_STACK
kasan: allow VMAP_STACK for HW_TAGS mode
kasan: remove __kasan_unpoison_stack
kasan: inline kasan_reset_tag for tag-based modes
kasan: inline random_tag for HW_TAGS
kasan: open-code kasan_unpoison_slab
kasan: inline (un)poison_range and check_invalid_free
kasan: add and integrate kasan boot parameters kasan, mm: check kasan_enabled in annotations kasan, mm: rename kasan_poison_kfree
kasan: don't round_up too much
kasan: simplify assign_tag and set_tag calls
kasan: clarify comment in __kasan_kfree_large
kasan: sanitize objects when metadata doesn't fit kasan, mm: allow cache merging with no metadata
kasan: update documentation
Documentation/dev-tools/kasan.rst | 186 ++++++++++++-------- arch/Kconfig | 8 +- arch/arm64/kernel/sleep.S | 2 +- arch/x86/kernel/acpi/wakeup_64.S | 2 +- include/linux/kasan.h | 245 ++++++++++++++++++++------ include/linux/mm.h | 22 ++- mm/kasan/common.c | 283 ++++++++++++++++++------------ mm/kasan/generic.c | 27 +-- mm/kasan/hw_tags.c | 185 +++++++++++++++---- mm/kasan/kasan.h | 120 +++++++++---- mm/kasan/quarantine.c | 19 +- mm/kasan/report.c | 61 ++++--- mm/kasan/report_hw_tags.c | 2 +- mm/kasan/report_sw_tags.c | 15 +- mm/kasan/shadow.c | 5 +- mm/kasan/sw_tags.c | 17 +- mm/mempool.c | 4 +- mm/slab_common.c | 3 +- 18 files changed, 829 insertions(+), 377 deletions(-)
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 1/19 kasan: simplify quarantine_put call site
Move get_free_info() call into quarantine_put() to simplify the call site.
No functional changes.
--- mm/kasan/common.c | 2 +- mm/kasan/kasan.h | 5 ++--- mm/kasan/quarantine.c | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-)
mm/kasan/common.c +1 -1
@@ -317,7 +317,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,kasan_set_free_info (cache, object, tag); -quarantine_put (get_free_info (cache, object), cache); +quarantine_put (cache, object);return IS_ENABLED (CONFIG_KASAN_GENERIC ); }
mm/kasan/kasan.h +2 -3
@@ -216,12 +216,11 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,#if defined(CONFIG_KASAN_GENERIC ) && \ (defined(CONFIG_SLAB ) || defined(CONFIG_SLUB )) -void quarantine_put (struct kasan_free_meta *info,struct kmem_cache *cache); +void quarantine_put (struct kmem_cache *cache,void *object);void quarantine_reduce (void );void quarantine_remove_cache (struct kmem_cache *cache);#else -static inline void quarantine_put (struct kasan_free_meta *info, -struct kmem_cache *cache) { } +static inline void quarantine_put (struct kmem_cache *cache,void *object) { }static inline void quarantine_reduce (void ) { }static inline void quarantine_remove_cache (struct kmem_cache *cache) { }#endif
mm/kasan/quarantine.c +2 -1
@@ -161,11 +161,12 @@ static void qlist_free_all(struct qlist_head *q, struct kmem_cache *cache)qlist_init (q); } -void quarantine_put (struct kasan_free_meta *info,struct kmem_cache *cache) +void quarantine_put (struct kmem_cache *cache,void *object) {unsigned long flags;struct qlist_head *q;struct qlist_head temp =QLIST_INIT ; +struct kasan_free_meta *info =get_free_info (cache, object);/* * Note: irq must be disabled until after we move the batch to the
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 2/19 kasan: rename get_alloc/free_info
In reply to Andrey KonovalovRename get_alloc_info() and get_free_info() to kasan_get_alloc_meta() and kasan_get_free_meta() to better reflect what those do and avoid confusion with kasan_set_free_info().
No functional changes.
--- mm/kasan/common.c | 16 ++++++++-------- mm/kasan/generic.c | 12 ++++++------ mm/kasan/hw_tags.c | 4 ++-- mm/kasan/kasan.h | 8 ++++---- mm/kasan/quarantine.c | 4 ++-- mm/kasan/report.c | 12 ++++++------ mm/kasan/report_sw_tags.c | 2 +- mm/kasan/sw_tags.c | 4 ++-- 8 files changed, 31 insertions(+), 31 deletions(-)
mm/kasan/common.c +8 -8
@@ -181,14 +181,14 @@ size_t kasan_metadata_size(struct kmem_cache *cache) sizeof(struct kasan_free_meta) : 0); } -struct kasan_alloc_meta *get_alloc_info (struct kmem_cache *cache, -const void *object) +struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache, +const void *object) {return (void *)reset_tag (object) + cache->kasan_info.alloc_meta_offset; } -struct kasan_free_meta *get_free_info (struct kmem_cache *cache, -const void *object) +struct kasan_free_meta *kasan_get_free_meta (struct kmem_cache *cache, +const void *object) {BUILD_BUG_ON (sizeof (struct kasan_free_meta ) >32 );return (void *)reset_tag (object) + cache->kasan_info.free_meta_offset;
@@ -265,13 +265,13 @@ static u8 assign_tag(struct kmem_cache *cache, const void *object,void *__must_check kasan_init_slab_obj (struct kmem_cache *cache,const void *object) { -struct kasan_alloc_meta *alloc_info; +struct kasan_alloc_meta *alloc_meta;if (!(cache->flags &SLAB_KASAN ))return (void *)object; - alloc_info =get_alloc_info (cache, object); -__memset (alloc_info,0 ,sizeof (*alloc_info)); + alloc_meta =kasan_get_alloc_meta (cache, object); +__memset (alloc_meta,0 ,sizeof (*alloc_meta));if (IS_ENABLED (CONFIG_KASAN_SW_TAGS ) ||IS_ENABLED (CONFIG_KASAN_HW_TAGS )) object =set_tag (object,assign_tag (cache, object, true, false));
@@ -357,7 +357,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object, KASAN_KMALLOC_REDZONE);if (cache->flags &SLAB_KASAN ) -kasan_set_track (&get_alloc_info (cache, object)->alloc_track, flags); +kasan_set_track (&kasan_get_alloc_meta (cache, object)->alloc_track, flags);return set_tag (object, tag); }
mm/kasan/generic.c +6 -6
@@ -329,7 +329,7 @@ void kasan_record_aux_stack(void *addr) {struct page *page =kasan_addr_to_page (addr);struct kmem_cache *cache; -struct kasan_alloc_meta *alloc_info; +struct kasan_alloc_meta *alloc_meta;void *object;if (is_kfence_address (addr) || !(page &&PageSlab (page)))
@@ -337,13 +337,13 @@ void kasan_record_aux_stack(void *addr) cache = page->slab_cache; object =nearest_obj (cache, page, addr); - alloc_info =get_alloc_info (cache, object); + alloc_meta =kasan_get_alloc_meta (cache, object);/* * record the last two call_rcu() call stacks. */ - alloc_info->aux_stack[1 ] = alloc_info->aux_stack[0 ]; - alloc_info->aux_stack[0 ] =kasan_save_stack (GFP_NOWAIT ); + alloc_meta->aux_stack[1 ] = alloc_meta->aux_stack[0 ]; + alloc_meta->aux_stack[0 ] =kasan_save_stack (GFP_NOWAIT ); } void kasan_set_free_info(struct kmem_cache *cache,
@@ -351,7 +351,7 @@ void kasan_set_free_info(struct kmem_cache *cache, {struct kasan_free_meta *free_meta; - free_meta =get_free_info (cache, object); + free_meta =kasan_get_free_meta (cache, object);kasan_set_track (&free_meta->free_track,GFP_NOWAIT );/*
@@ -365,5 +365,5 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, {if (*(u8 *)kasan_mem_to_shadow (object) !=KASAN_KMALLOC_FREETRACK )return NULL ; -return &get_free_info (cache, object)->free_track; +return &kasan_get_free_meta (cache, object)->free_track; }
mm/kasan/hw_tags.c +2 -2
@@ -75,7 +75,7 @@ void kasan_set_free_info(struct kmem_cache *cache, {struct kasan_alloc_meta *alloc_meta; - alloc_meta =get_alloc_info (cache, object); + alloc_meta =kasan_get_alloc_meta (cache, object);kasan_set_track (&alloc_meta->free_track[0 ],GFP_NOWAIT ); }
@@ -84,6 +84,6 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, {struct kasan_alloc_meta *alloc_meta; - alloc_meta =get_alloc_info (cache, object); + alloc_meta =kasan_get_alloc_meta (cache, object);return &alloc_meta->free_track[0 ]; }
mm/kasan/kasan.h +4 -4
@@ -149,10 +149,10 @@ struct kasan_free_meta {#endif }; -struct kasan_alloc_meta *get_alloc_info (struct kmem_cache *cache, -const void *object); -struct kasan_free_meta *get_free_info (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);void poison_range (const void *address,size_t size,u8 value);void unpoison_range (const void *address,size_t size);
mm/kasan/quarantine.c +2 -2
@@ -166,7 +166,7 @@ void quarantine_put(struct kmem_cache *cache, void *object)unsigned long flags;struct qlist_head *q;struct qlist_head temp =QLIST_INIT ; -struct kasan_free_meta *info =get_free_info (cache, object); +struct kasan_free_meta *meta =kasan_get_free_meta (cache, object);/* * Note: irq must be disabled until after we move the batch to the
@@ -179,7 +179,7 @@ void quarantine_put(struct kmem_cache *cache, void *object)local_irq_save (flags); q =this_cpu_ptr (&cpu_quarantine); -qlist_put (q, &info->quarantine_link, cache->size); +qlist_put (q, &meta->quarantine_link, cache->size);if (unlikely (q->bytes >QUARANTINE_PERCPU_SIZE )) {qlist_move_all (q, &temp);
mm/kasan/report.c +6 -6
@@ -164,12 +164,12 @@ static void describe_object_addr(struct kmem_cache *cache, void *object,static void describe_object (struct kmem_cache *cache,void *object,const void *addr,u8 tag) { -struct kasan_alloc_meta *alloc_info =get_alloc_info (cache, object); +struct kasan_alloc_meta *alloc_meta =kasan_get_alloc_meta (cache, object);if (cache->flags &SLAB_KASAN ) {struct kasan_track *free_track; -print_track (&alloc_info->alloc_track,"Allocated" ); +print_track (&alloc_meta->alloc_track,"Allocated" );pr_err ("\n" ); free_track =kasan_get_free_track (cache, object, tag);if (free_track) {
@@ -178,14 +178,14 @@ static void describe_object(struct kmem_cache *cache, void *object, }#ifdef CONFIG_KASAN_GENERIC -if (alloc_info->aux_stack[0 ]) { +if (alloc_meta->aux_stack[0 ]) {pr_err ("Last call_rcu():\n" ); -print_stack (alloc_info->aux_stack[0 ]); +print_stack (alloc_meta->aux_stack[0 ]);pr_err ("\n" ); } -if (alloc_info->aux_stack[1 ]) { +if (alloc_meta->aux_stack[1 ]) {pr_err ("Second to last call_rcu():\n" ); -print_stack (alloc_info->aux_stack[1 ]); +print_stack (alloc_meta->aux_stack[1 ]);pr_err ("\n" ); }#endif
mm/kasan/report_sw_tags.c +1 -1
@@ -46,7 +46,7 @@ const char *get_bug_type(struct kasan_access_info *info)if (page &&PageSlab (page)) { cache = page->slab_cache; object =nearest_obj (cache, page, (void *)addr); - alloc_meta =get_alloc_info (cache, object); + alloc_meta =kasan_get_alloc_meta (cache, object);for (i =0 ; i <KASAN_NR_FREE_STACKS ; i++)if (alloc_meta->free_pointer_tag[i] == tag)
mm/kasan/sw_tags.c +2 -2
@@ -174,7 +174,7 @@ void kasan_set_free_info(struct kmem_cache *cache,struct kasan_alloc_meta *alloc_meta;u8 idx =0 ; - alloc_meta =get_alloc_info (cache, object); + alloc_meta =kasan_get_alloc_meta (cache, object);#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY idx = alloc_meta->free_track_idx;
@@ -191,7 +191,7 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,struct kasan_alloc_meta *alloc_meta;int i =0 ; - alloc_meta =get_alloc_info (cache, object); + alloc_meta =kasan_get_alloc_meta (cache, object);#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY for (i =0 ; i <KASAN_NR_FREE_STACKS ; i++) {
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 3/19 kasan: introduce set_alloc_info
In reply to Andrey KonovalovAdd set_alloc_info() helper and move kasan_set_track() into it. This will simplify the code for one of the upcoming changes.
No functional changes.
--- mm/kasan/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
mm/kasan/common.c +6 -1
@@ -327,6 +327,11 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object, unsigned long ip)return __kasan_slab_free (cache, object, ip, true); } +static void set_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) +{ +kasan_set_track (&kasan_get_alloc_meta (cache, object)->alloc_track, flags); +} +static void *__kasan_kmalloc (struct kmem_cache *cache,const void *object,size_t size,gfp_t flags,bool keep_tag) {
@@ -357,7 +362,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object, KASAN_KMALLOC_REDZONE);if (cache->flags &SLAB_KASAN ) -kasan_set_track (&kasan_get_alloc_meta (cache, object)->alloc_track, flags); +set_alloc_info (cache, (void *)object, flags);return set_tag (object, tag); }
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 4/19 kasan, arm64: unpoison stack only with CONFIG_KASAN_STACK
In reply to Andrey KonovalovThere's a config option CONFIG_KASAN_STACK that has to be enabled for KASAN to use stack instrumentation and perform validity checks for stack variables.
There's no need to unpoison stack when CONFIG_KASAN_STACK is not enabled. Only call kasan_unpoison_task_stack[_below]() when CONFIG_KASAN_STACK is enabled.
Note, that CONFIG_KASAN_STACK is an option that is currently always defined when CONFIG_KASAN is enabled, and therefore has to be tested with #if instead of #ifdef.
--- arch/arm64/kernel/sleep.S | 2 +- arch/x86/kernel/acpi/wakeup_64.S | 2 +- include/linux/kasan.h | 10 ++++++---- mm/kasan/common.c | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-)
arch/arm64/kernel/sleep.S +1 -1
@@ -133,7 +133,7 @@ SYM_FUNC_START(_cpu_resume) */ bl cpu_do_resume -#ifdef CONFIG_KASAN +#if defined(CONFIG_KASAN) && CONFIG_KASAN_STACK mov x0, sp bl kasan_unpoison_task_stack_below #endif
arch/x86/kernel/acpi/wakeup_64.S +1 -1
@@ -112,7 +112,7 @@ SYM_FUNC_START(do_suspend_lowlevel) movq pt_regs_r14(%rax), %r14 movq pt_regs_r15(%rax), %r15 -#ifdef CONFIG_KASAN +#if defined(CONFIG_KASAN) && CONFIG_KASAN_STACK /* * The suspend path may have poisoned some areas deeper in the stack, * which we now need to unpoison.
include/linux/kasan.h +6 -4
@@ -76,8 +76,6 @@ static inline void kasan_disable_current(void) {}void kasan_unpoison_range (const void *address,size_t size); -void kasan_unpoison_task_stack (struct task_struct *task); -void kasan_alloc_pages (struct page *page,unsigned int order);void kasan_free_pages (struct page *page,unsigned int order);
@@ -122,8 +120,6 @@ void kasan_restore_multi_shot(bool enabled);static inline void kasan_unpoison_range (const void *address,size_t size) {} -static inline void kasan_unpoison_task_stack (struct task_struct *task) {} -static inline void kasan_alloc_pages (struct page *page,unsigned int order) {}static inline void kasan_free_pages (struct page *page,unsigned int order) {}
@@ -175,6 +171,12 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }#endif /* CONFIG_KASAN */ +#if defined(CONFIG_KASAN ) &&CONFIG_KASAN_STACK +void kasan_unpoison_task_stack (struct task_struct *task); +#else +static inline void kasan_unpoison_task_stack (struct task_struct *task) {} +#endif +#ifdef CONFIG_KASAN_GENERIC void kasan_cache_shrink (struct kmem_cache *cache);
mm/kasan/common.c +2 -0
@@ -64,6 +64,7 @@ void kasan_unpoison_range(const void *address, size_t size)unpoison_range (address, size); } +#if CONFIG_KASAN_STACK static void __kasan_unpoison_stack (struct task_struct *task,const void *sp) {void *base =task_stack_page (task);
@@ -90,6 +91,7 @@ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark) unpoison_range(base, watermark - base); } +#endif /* CONFIG_KASAN_STACK */ void kasan_alloc_pages (struct page *page,unsigned int order) {
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 5/19 kasan: allow VMAP_STACK for HW_TAGS mode
In reply to Andrey KonovalovEven though hardware tag-based mode currently doesn't support checking vmalloc allocations, it doesn't use shadow memory and works with VMAP_STACK as is. Change VMAP_STACK definition accordingly.
--- arch/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/Kconfig +4 -4
@@ -955,16 +955,16 @@ config VMAP_STACK default y bool "Use a virtually-mapped stack" depends on HAVE_ARCH_VMAP_STACK - depends on !KASAN || KASAN_VMALLOC + depends on !KASAN || KASAN_HW_TAGS || KASAN_VMALLOC help Enable this if you want the use virtually-mapped kernel stacks with guard pages. This causes kernel stack overflows to be caught immediately rather than causing difficult-to-diagnose corruption. - To use this with KASAN, the architecture must support backing - virtual mappings with real shadow memory, and KASAN_VMALLOC must - be enabled. + To use this with software KASAN modes, the architecture must support + backing virtual mappings with real shadow memory, and KASAN_VMALLOC + must be enabled. config ARCH_OPTIONAL_KERNEL_RWX def_bool n
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 6/19 kasan: remove __kasan_unpoison_stack
In reply to Andrey KonovalovThere's no need for __kasan_unpoison_stack() helper, as it's only currently used in a single place. Removing it also removes unneeded arithmetic.
No functional changes.
--- mm/kasan/common.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)
mm/kasan/common.c +3 -9
@@ -65,18 +65,12 @@ void kasan_unpoison_range(const void *address, size_t size) }#if CONFIG_KASAN_STACK -static void __kasan_unpoison_stack (struct task_struct *task,const void *sp) -{ -void *base =task_stack_page (task); -size_t size = sp - base; - -unpoison_range (base, size); -} -/* Unpoison the entire stack for a task. */ void kasan_unpoison_task_stack (struct task_struct *task) { -__kasan_unpoison_stack (task,task_stack_page (task) +THREAD_SIZE ); +void *base =task_stack_page (task); + +unpoison_range (base,THREAD_SIZE ); }/* Unpoison the stack for the current task beyond a watermark sp value. */
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 7/19 kasan: inline kasan_reset_tag for tag-based modes
In reply to Andrey KonovalovUsing kasan_reset_tag() currently results in a function call. As it's called quite often from the allocator code, this leads to a noticeable slowdown. Move it to include/linux/kasan.h and turn it into a static inline function. Also remove the now unneeded reset_tag() internal KASAN macro and use kasan_reset_tag() instead.
--- include/linux/kasan.h | 5 ++++- mm/kasan/common.c | 6 +++--- mm/kasan/hw_tags.c | 9 ++------- mm/kasan/kasan.h | 4 ---- mm/kasan/report.c | 4 ++-- mm/kasan/report_hw_tags.c | 2 +- mm/kasan/report_sw_tags.c | 4 ++-- mm/kasan/shadow.c | 4 ++-- mm/kasan/sw_tags.c | 9 ++------- 9 files changed, 18 insertions(+), 29 deletions(-)
include/linux/kasan.h +4 -1
@@ -193,7 +193,10 @@ static inline void kasan_record_aux_stack(void *ptr) {}#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS ) -void *kasan_reset_tag (const void *addr); +static inline void *kasan_reset_tag (const void *addr) +{ +return (void *)arch_kasan_reset_tag (addr); +}bool kasan_report (unsigned long addr,size_t size,bool is_write,unsigned long ip);
mm/kasan/common.c +3 -3
@@ -180,14 +180,14 @@ size_t kasan_metadata_size(struct kmem_cache *cache)struct kasan_alloc_meta *kasan_get_alloc_meta (struct kmem_cache *cache,const void *object) { -return (void *)reset_tag (object) + cache->kasan_info.alloc_meta_offset; +return kasan_reset_tag (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 ); -return (void *)reset_tag (object) + cache->kasan_info.free_meta_offset; +return kasan_reset_tag (object) + cache->kasan_info.free_meta_offset; }void kasan_poison_slab (struct page *page)
@@ -284,7 +284,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object, tag =get_tag (object); tagged_object = object; - object =reset_tag (object); + object =kasan_reset_tag (object);if (is_kfence_address (object))return false;
mm/kasan/hw_tags.c +2 -7
@@ -31,18 +31,13 @@ void __init kasan_init_hw_tags(void)pr_info ("KernelAddressSanitizer initialized\n" ); } -void *kasan_reset_tag (const void *addr) -{ -return reset_tag (addr); -} -void poison_range (const void *address,size_t size,u8 value) {/* Skip KFENCE memory if called explicitly outside of sl*b. */ if (is_kfence_address (address))return ; -hw_set_mem_tag_range (reset_tag (address), +hw_set_mem_tag_range (kasan_reset_tag (address),round_up (size,KASAN_GRANULE_SIZE ), value); }
@@ -52,7 +47,7 @@ void unpoison_range(const void *address, size_t size)if (is_kfence_address (address))return ; -hw_set_mem_tag_range (reset_tag (address), +hw_set_mem_tag_range (kasan_reset_tag (address),round_up (size,KASAN_GRANULE_SIZE ),get_tag (address)); }
mm/kasan/kasan.h +0 -4
@@ -248,15 +248,11 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag) return addr; }#endif -#ifndef arch_kasan_reset_tag -#define arch_kasan_reset_tag (addr) ((void *)(addr)) -#endif #ifndef arch_kasan_get_tag#define arch_kasan_get_tag (addr) 0#endif #define set_tag (addr, tag) ((void *)arch_kasan_set_tag((addr), (tag))) -#define reset_tag (addr) ((void *)arch_kasan_reset_tag(addr))#define get_tag (addr) arch_kasan_get_tag(addr)#ifdef CONFIG_KASAN_HW_TAGS
mm/kasan/report.c +2 -2
@@ -328,7 +328,7 @@ void kasan_report_invalid_free(void *object, unsigned long ip)unsigned long flags;u8 tag =get_tag (object); - object =reset_tag (object); + object =kasan_reset_tag (object);#if IS_ENABLED (CONFIG_KUNIT )if (current->kunit_test)
@@ -361,7 +361,7 @@ static void __kasan_report(unsigned long addr, size_t size, bool is_write,disable_trace_on_warning (); tagged_addr = (void *)addr; - untagged_addr =reset_tag (tagged_addr); + untagged_addr =kasan_reset_tag (tagged_addr); info.access_addr = tagged_addr;if (addr_has_metadata (untagged_addr))
mm/kasan/report_hw_tags.c +1 -1
@@ -22,7 +22,7 @@ const char *get_bug_type(struct kasan_access_info *info)void *find_first_bad_addr (void *addr,size_t size) { -return reset_tag (addr); +return kasan_reset_tag (addr); }void metadata_fetch_row (char *buffer,void *row)
mm/kasan/report_sw_tags.c +2 -2
@@ -41,7 +41,7 @@ const char *get_bug_type(struct kasan_access_info *info)int i; tag =get_tag (info->access_addr); - addr =reset_tag (info->access_addr); + addr =kasan_reset_tag (info->access_addr); page =kasan_addr_to_page (addr);if (page &&PageSlab (page)) { cache = page->slab_cache;
@@ -72,7 +72,7 @@ const char *get_bug_type(struct kasan_access_info *info)void *find_first_bad_addr (void *addr,size_t size) {u8 tag =get_tag (addr); -void *p =reset_tag (addr); +void *p =kasan_reset_tag (addr);void *end = p + size;while (p < end && tag == *(u8 *)kasan_mem_to_shadow (p))
mm/kasan/shadow.c +2 -2
@@ -82,7 +82,7 @@ void poison_range(const void *address, size_t size, u8 value)* some of the callers (e.g. kasan_poison_object_data) pass tagged * addresses to this function. */ - address =reset_tag (address); + address =kasan_reset_tag (address);/* Skip KFENCE memory if called explicitly outside of sl*b. */ if (is_kfence_address (address))
@@ -103,7 +103,7 @@ void unpoison_range(const void *address, size_t size)* some of the callers (e.g. kasan_unpoison_object_data) pass tagged * addresses to this function. */ - address =reset_tag (address); + address =kasan_reset_tag (address);/* * Skip KFENCE memory if called explicitly outside of sl*b. Also note
mm/kasan/sw_tags.c +2 -7
@@ -67,11 +67,6 @@ u8 random_tag(void)return (u8)(state % (KASAN_TAG_MAX +1 )); } -void *kasan_reset_tag (const void *addr) -{ -return reset_tag (addr); -} -bool check_memory_region (unsigned long addr,size_t size,bool write,unsigned long ret_ip) {
@@ -107,7 +102,7 @@ bool check_memory_region(unsigned long addr, size_t size, bool write,if (tag ==KASAN_TAG_KERNEL )return true; - untagged_addr =reset_tag ((const void *)addr); + untagged_addr =kasan_reset_tag ((const void *)addr);if (unlikely (untagged_addr <kasan_shadow_to_mem ((void *)KASAN_SHADOW_START ))) {return !kasan_report (addr, size, write, ret_ip);
@@ -126,7 +121,7 @@ bool check_memory_region(unsigned long addr, size_t size, bool write,bool check_invalid_free (void *addr) {u8 tag =get_tag (addr); -u8 shadow_byte =READ_ONCE (*(u8 *)kasan_mem_to_shadow (reset_tag (addr))); +u8 shadow_byte =READ_ONCE (*(u8 *)kasan_mem_to_shadow (kasan_reset_tag (addr)));return (shadow_byte ==KASAN_TAG_INVALID ) || (tag !=KASAN_TAG_KERNEL && tag != shadow_byte);
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 8/19 kasan: inline random_tag for HW_TAGS
In reply to Andrey KonovalovUsing random_tag() currently results in a function call. Move its definition to mm/kasan/kasan.h and turn it into a static inline function for hardware tag-based mode to avoid uneeded function calls.
--- mm/kasan/hw_tags.c | 5 ----- mm/kasan/kasan.h | 31 ++++++++++++++----------------- 2 files changed, 14 insertions(+), 22 deletions(-)
mm/kasan/hw_tags.c +0 -5
@@ -51,11 +51,6 @@ void unpoison_range(const void *address, size_t size) round_up(size, KASAN_GRANULE_SIZE), get_tag(address)); } -u8 random_tag (void ) -{ -return hw_get_random_tag (); -} -bool check_invalid_free (void *addr) {u8 ptr_tag =get_tag (addr);
mm/kasan/kasan.h +14 -17
@@ -190,6 +190,12 @@ static inline bool addr_has_metadata(const void *addr)#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */ +#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS ) +void print_tags (u8 addr_tag,const void *addr); +#else +static inline void print_tags (u8 addr_tag,const void *addr) { } +#endif +bool check_invalid_free (void *addr);void *find_first_bad_addr (void *addr,size_t size);
@@ -225,23 +231,6 @@ static inline void quarantine_reduce(void) { }static inline void quarantine_remove_cache (struct kmem_cache *cache) { }#endif -#if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS ) - -void print_tags (u8 addr_tag,const void *addr); - -u8 random_tag (void ); - -#else - -static inline void print_tags (u8 addr_tag,const void *addr) { } - -static inline u8 random_tag (void ) -{ -return 0 ; -} - -#endif -#ifndef arch_kasan_set_tagstatic inline const void *arch_kasan_set_tag (const void *addr,u8 tag) {
@@ -281,6 +270,14 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)#endif /* CONFIG_KASAN_HW_TAGS */ +#ifdef CONFIG_KASAN_SW_TAGS +u8 random_tag (void ); +#elif defined(CONFIG_KASAN_HW_TAGS ) +static inline u8 random_tag (void ) {return hw_get_random_tag (); } +#else +static inline u8 random_tag (void ) {return 0 ; } +#endif +/* * Exported functions for interfaces called from assembly or from generated * code. Declarations here to avoid warning about missing declarations.
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 9/19 kasan: open-code kasan_unpoison_slab
In reply to Andrey KonovalovThere's the external annotation kasan_unpoison_slab() that is currently defined as static inline and uses kasan_unpoison_range(). Open-code this function in mempool.c. Otherwise with an upcoming change this function will result in an unnecessary function call.
--- include/linux/kasan.h | 6 ------ mm/mempool.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-)
include/linux/kasan.h +0 -6
@@ -106,11 +106,6 @@ struct kasan_cache {int free_meta_offset; }; -size_t __ksize (const void *); -static inline void kasan_unpoison_slab (const void *ptr) -{ -kasan_unpoison_range (ptr,__ksize (ptr)); -}size_t kasan_metadata_size (struct kmem_cache *cache);bool kasan_save_enable_multi_shot (void );
@@ -166,7 +161,6 @@ static inline bool kasan_slab_free(struct kmem_cache *s, void *object, return false; } -static inline void kasan_unpoison_slab (const void *ptr) { }static inline size_t kasan_metadata_size (struct kmem_cache *cache) {return 0 ; }#endif /* CONFIG_KASAN */
mm/mempool.c +1 -1
@@ -112,7 +112,7 @@ static __always_inline void kasan_poison_element(mempool_t *pool, void *element)static void kasan_unpoison_element (mempool_t *pool,void *element) {if (pool->alloc == mempool_alloc_slab || pool->alloc == mempool_kmalloc) -kasan_unpoison_slab (element); +kasan_unpoison_range (element,__ksize (element));else if (pool->alloc == mempool_alloc_pages)kasan_alloc_pages (element, (unsigned long )pool->pool_data); }
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 10/19 kasan: inline (un)poison_range and check_invalid_free
In reply to Andrey KonovalovUsing (un)poison_range() or check_invalid_free() currently results in function calls. Move their definitions to mm/kasan/kasan.h and turn them into static inline functions for hardware tag-based mode to avoid unneeded function calls.
--- mm/kasan/hw_tags.c | 30 ------------------------------ mm/kasan/kasan.h | 45 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 35 deletions(-)
mm/kasan/hw_tags.c +0 -30
@@ -10,7 +10,6 @@#include <linux/kasan.h> #include <linux/kernel.h> -#include <linux/kfence.h> #include <linux/memory.h> #include <linux/mm.h> #include <linux/string.h>
@@ -31,35 +30,6 @@ void __init kasan_init_hw_tags(void)pr_info ("KernelAddressSanitizer initialized\n" ); } -void poison_range (const void *address,size_t size,u8 value) -{ -/* Skip KFENCE memory if called explicitly outside of sl*b. */ -if (is_kfence_address (address)) -return ; - -hw_set_mem_tag_range (kasan_reset_tag (address), -round_up (size,KASAN_GRANULE_SIZE ), value); -} - -void unpoison_range (const void *address,size_t size) -{ -/* Skip KFENCE memory if called explicitly outside of sl*b. */ -if (is_kfence_address (address)) -return ; - -hw_set_mem_tag_range (kasan_reset_tag (address), -round_up (size,KASAN_GRANULE_SIZE ),get_tag (address)); -} - -bool check_invalid_free (void *addr) -{ -u8 ptr_tag =get_tag (addr); -u8 mem_tag =hw_get_mem_tag (addr); - -return (mem_tag ==KASAN_TAG_INVALID ) || - (ptr_tag !=KASAN_TAG_KERNEL && ptr_tag != mem_tag); -} -void kasan_set_free_info (struct kmem_cache *cache,void *object,u8 tag) {
mm/kasan/kasan.h +40 -5
69 lines not on this page · raw message
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 11/19 kasan: add and integrate kasan boot parameters
In reply to Andrey KonovalovHardware tag-based KASAN mode is intended to eventually be used in production as a security mitigation. Therefore there's a need for finer control over KASAN features and for an existence of a kill switch.
This change adds a few boot parameters for hardware tag-based KASAN that allow to disable or otherwise control particular KASAN features.
The features that can be controlled are:
1. Whether KASAN is enabled at all.
2. Whether KASAN collects and saves alloc/free stacks.
3. Whether KASAN panics on a detected bug or not.
With this change a new boot parameter kasan.mode allows to choose one of three main modes:
- kasan.mode=off - KASAN is disabled, no tag checks are performed
- kasan.mode=prod - only essential production features are enabled
- kasan.mode=full - all KASAN features are enabled
The chosen mode provides default control values for the features mentioned above. However it's also possible to override the default values by providing:
- kasan.stacktrace=off/on - enable alloc/free stack collection (default: on for mode=full, otherwise off)
- kasan.fault=report/panic - only report tag fault or also panic (default: report)
If kasan.mode parameter is not provided, it defaults to full when CONFIG_DEBUG_KERNEL is enabled, and to prod otherwise.
It is essential that switching between these modes doesn't require rebuilding the kernel with different configs, as this is required by the Android GKI (Generic Kernel Image) initiative [1].
[1] https://source.android.com/devices/architecture/kernel/generic-kernel-image
--- mm/kasan/common.c | 22 +++++-- mm/kasan/hw_tags.c | 151 +++++++++++++++++++++++++++++++++++++++++++++ mm/kasan/kasan.h | 16 +++++ mm/kasan/report.c | 14 ++++- 4 files changed, 196 insertions(+), 7 deletions(-)
mm/kasan/common.c +17 -5
@@ -135,6 +135,11 @@ void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,unsigned int redzone_size;int redzone_adjust; +if (!kasan_stack_collection_enabled ()) { + *flags |=SLAB_KASAN ; +return ; + } +/* Add alloc meta. */ cache->kasan_info.alloc_meta_offset = *size; *size +=sizeof (struct kasan_alloc_meta );
@@ -171,6 +176,8 @@ void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,size_t kasan_metadata_size (struct kmem_cache *cache) { +if (!kasan_stack_collection_enabled ()) +return 0 ;return (cache->kasan_info.alloc_meta_offset ? sizeof(struct kasan_alloc_meta) : 0) + (cache->kasan_info.free_meta_offset ?
@@ -263,11 +270,13 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache, {struct kasan_alloc_meta *alloc_meta; -if (!(cache->flags &SLAB_KASAN )) -return (void *)object; +if (kasan_stack_collection_enabled ()) { +if (!(cache->flags &SLAB_KASAN )) +return (void *)object; - alloc_meta =kasan_get_alloc_meta (cache, object); -__memset (alloc_meta,0 ,sizeof (*alloc_meta)); + alloc_meta =kasan_get_alloc_meta (cache, object); +__memset (alloc_meta,0 ,sizeof (*alloc_meta)); + }if (IS_ENABLED (CONFIG_KASAN_SW_TAGS ) ||IS_ENABLED (CONFIG_KASAN_HW_TAGS )) object =set_tag (object,assign_tag (cache, object, true, false));
@@ -307,6 +316,9 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object, rounded_up_size =round_up (cache->object_size,KASAN_GRANULE_SIZE );poison_range (object, rounded_up_size,KASAN_KMALLOC_FREE ); +if (!kasan_stack_collection_enabled ()) +return false; +if ((IS_ENABLED (CONFIG_KASAN_GENERIC ) && !quarantine) ||unlikely (!(cache->flags &SLAB_KASAN )))return false;
@@ -357,7 +369,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,poison_range ((void *)redzone_start, redzone_end - redzone_start,KASAN_KMALLOC_REDZONE ); -if (cache->flags &SLAB_KASAN ) +if (kasan_stack_collection_enabled () && (cache->flags &SLAB_KASAN ))set_alloc_info (cache, (void *)object, flags);return set_tag (object, tag);
mm/kasan/hw_tags.c +151 -0
175 lines not on this page · raw message
mm/kasan/kasan.h +16 -0
@@ -6,6 +6,22 @@#include <linux/kfence.h> #include <linux/stackdepot.h> +#ifdef CONFIG_KASAN_HW_TAGS +#include <linux/static_key.h> +DECLARE_STATIC_KEY_FALSE (kasan_flag_stacktrace); +static inline bool kasan_stack_collection_enabled (void ) +{ +return static_branch_unlikely (&kasan_flag_stacktrace); +} +#else +static inline bool kasan_stack_collection_enabled (void ) +{ +return true; +} +#endif + +extern bool kasan_flag_panic__ro_after_init ; +#if defined(CONFIG_KASAN_GENERIC ) || defined(CONFIG_KASAN_SW_TAGS )#define KASAN_GRANULE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)#else
mm/kasan/report.c +12 -2
@@ -99,6 +99,10 @@ static void end_report(unsigned long *flags) panic_on_warn =0 ;panic ("panic_on_warn set ...\n" ); } +#ifdef CONFIG_KASAN_HW_TAGS +if (kasan_flag_panic) +panic ("kasan.fault=panic set ...\n" ); +#endif kasan_enable_current (); }
@@ -161,8 +165,8 @@ static void describe_object_addr(struct kmem_cache *cache, void *object, (void *)(object_addr + cache->object_size)); } -static void describe_object (struct kmem_cache *cache,void *object, -const void *addr,u8 tag) +static void describe_object_stacks (struct kmem_cache *cache,void *object, +const void *addr,u8 tag) {struct kasan_alloc_meta *alloc_meta =kasan_get_alloc_meta (cache, object);
@@ -190,7 +194,13 @@ static void describe_object(struct kmem_cache *cache, void *object, }#endif } +} +static void describe_object (struct kmem_cache *cache,void *object, +const void *addr,u8 tag) +{ +if (kasan_stack_collection_enabled ()) +describe_object_stacks (cache, object, addr, tag);describe_object_addr (cache, object, addr); }
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 12/19 kasan, mm: check kasan_enabled in annotations
In reply to Andrey KonovalovDeclare the kasan_enabled static key in include/linux/kasan.h and in include/linux/mm.h and check it in all kasan annotations. This allows to avoid any slowdown caused by function calls when kasan_enabled is disabled.
--- include/linux/kasan.h | 213 ++++++++++++++++++++++++++++++++---------- include/linux/mm.h | 22 +++-- mm/kasan/common.c | 56 +++++------ 3 files changed, 210 insertions(+), 81 deletions(-)
include/linux/kasan.h +166 -47
@@ -2,6 +2,7 @@#ifndef _LINUX_KASAN_H#define _LINUX_KASAN_H +#include <linux/static_key.h> #include <linux/types.h> struct kmem_cache ;
@@ -74,54 +75,176 @@ static inline void kasan_disable_current(void) {}#ifdef CONFIG_KASAN -void kasan_unpoison_range (const void *address,size_t size); +struct kasan_cache { +int alloc_meta_offset; +int free_meta_offset; +}; -void kasan_alloc_pages (struct page *page,unsigned int order); -void kasan_free_pages (struct page *page,unsigned int order); +#ifdef CONFIG_KASAN_HW_TAGS +DECLARE_STATIC_KEY_FALSE (kasan_flag_enabled); +static __always_inline bool kasan_enabled(void) +{ +return static_branch_likely (&kasan_flag_enabled); +} +#else +static inline bool kasan_enabled (void ) +{ +return true; +} +#endif -void kasan_cache_create (struct kmem_cache *cache,unsigned int *size, -slab_flags_t *flags); +void __kasan_unpoison_range (const void *addr,size_t size); +static __always_inline void kasan_unpoison_range(const void *addr, size_t size) +{ +if (kasan_enabled ()) +__kasan_unpoison_range (addr, size); +} -void kasan_poison_slab (struct page *page); -void kasan_unpoison_object_data (struct kmem_cache *cache,void *object); -void kasan_poison_object_data (struct kmem_cache *cache,void *object); -void *__must_check kasan_init_slab_obj (struct kmem_cache *cache, -const void *object); +void __kasan_alloc_pages (struct page *page,unsigned int order); +static __always_inline void kasan_alloc_pages(struct page *page, +unsigned int order) +{ +if (kasan_enabled ()) +__kasan_alloc_pages (page, order); +} -void *__must_check kasan_kmalloc_large (const void *ptr,size_t size, -gfp_t flags); -void kasan_kfree_large (void *ptr,unsigned long ip); -void kasan_poison_kfree (void *ptr,unsigned long ip); -void *__must_check kasan_kmalloc (struct kmem_cache *s,const void *object, -size_t size,gfp_t flags); -void *__must_check kasan_krealloc (const void *object,size_t new_size, -gfp_t flags); +void __kasan_free_pages (struct page *page,unsigned int order); +static __always_inline void kasan_free_pages(struct page *page, +unsigned int order) +{ +if (kasan_enabled ()) +__kasan_free_pages (page, order); +} -void *__must_check kasan_slab_alloc (struct kmem_cache *s,void *object, -gfp_t flags); -bool kasan_slab_free (struct kmem_cache *s,void *object,unsigned long ip); +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); +} -struct kasan_cache { -int alloc_meta_offset; -int free_meta_offset; -}; +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 page *page); +static __always_inline void kasan_poison_slab(struct page *page) +{ +if (kasan_enabled ()) +__kasan_poison_slab (page); +} + +void __kasan_unpoison_object_data (struct kmem_cache *cache,void *object); +static __always_inline void kasan_unpoison_object_data(struct kmem_cache *cache, +void *object) +{ +if (kasan_enabled ()) +__kasan_unpoison_object_data (cache, object); +} + +void __kasan_poison_object_data (struct kmem_cache *cache,void *object); +static __always_inline void kasan_poison_object_data(struct kmem_cache *cache, +void *object) +{ +if (kasan_enabled ()) +__kasan_poison_object_data (cache, object); +} + +void *__must_check __kasan_init_slab_obj (struct kmem_cache *cache, +const void *object); +static __always_inline void * __must_check kasan_init_slab_obj( +struct kmem_cache *cache,const void *object) +{ +if (kasan_enabled ()) +return __kasan_init_slab_obj (cache, object); +return (void *)object; +} + +bool __kasan_slab_free (struct kmem_cache *s,void *object,unsigned long ip); +static __always_inline bool kasan_slab_free(struct kmem_cache *s, void *object, +unsigned long ip) +{ +if (kasan_enabled ()) +return __kasan_slab_free (s, object, ip); +return false; +} + +void *__must_check __kasan_slab_alloc (struct kmem_cache *s, +void *object,gfp_t flags); +static __always_inline void * __must_check kasan_slab_alloc( +struct kmem_cache *s,void *object,gfp_t flags) +{ +if (kasan_enabled ()) +return __kasan_slab_alloc (s, object, flags); +return object; +} + +void *__must_check __kasan_kmalloc (struct kmem_cache *s,const void *object, +size_t size,gfp_t flags); +static __always_inline void * __must_check kasan_kmalloc(struct kmem_cache *s, +const void *object,size_t size,gfp_t flags) +{ +if (kasan_enabled ()) +return __kasan_kmalloc (s, object, size, flags); +return (void *)object; +} -size_t kasan_metadata_size (struct kmem_cache *cache); +void *__must_check __kasan_kmalloc_large (const void *ptr, +size_t size,gfp_t flags); +static __always_inline void * __must_check kasan_kmalloc_large(const void *ptr, +size_t size,gfp_t flags) +{ +if (kasan_enabled ()) +return __kasan_kmalloc_large (ptr, size, flags); +return (void *)ptr; +} + +void *__must_check __kasan_krealloc (const void *object, +size_t new_size,gfp_t flags); +static __always_inline void * __must_check kasan_krealloc(const void *object, +size_t new_size,gfp_t flags) +{ +if (kasan_enabled ()) +return __kasan_krealloc (object, new_size, flags); +return (void *)object; +} + +void __kasan_poison_kfree (void *ptr,unsigned long ip); +static __always_inline void kasan_poison_kfree(void *ptr, unsigned long ip) +{ +if (kasan_enabled ()) +__kasan_poison_kfree (ptr, ip); +} + +void __kasan_kfree_large (void *ptr,unsigned long ip); +static __always_inline void kasan_kfree_large(void *ptr, unsigned long ip) +{ +if (kasan_enabled ()) +__kasan_kfree_large (ptr, ip); +}bool kasan_save_enable_multi_shot (void );void kasan_restore_multi_shot (bool enabled);#else /* CONFIG_KASAN */ +static inline bool kasan_enabled (void ) +{ +return false; +}static inline void kasan_unpoison_range (const void *address,size_t size) {} -static inline void kasan_alloc_pages (struct page *page,unsigned int order) {}static inline void kasan_free_pages (struct page *page,unsigned int order) {} -static inline void kasan_cache_create (struct kmem_cache *cache,unsigned int *size,slab_flags_t *flags) {} - +static inline size_t kasan_metadata_size (struct kmem_cache *cache) {return 0 ; }static inline void kasan_poison_slab (struct page *page) {}static inline void kasan_unpoison_object_data (struct kmem_cache *cache,void *object) {}
@@ -132,36 +255,32 @@ static inline void *kasan_init_slab_obj(struct kmem_cache *cache, {return (void *)object; } - -static inline void *kasan_kmalloc_large (void *ptr,size_t size,gfp_t flags) +static inline bool kasan_slab_free (struct kmem_cache *s,void *object, +unsigned long ip) { -return ptr; +return false; +} +static inline void *kasan_slab_alloc (struct kmem_cache *s,void *object, +gfp_t flags) +{ +return object; } -static inline void kasan_kfree_large (void *ptr,unsigned long ip) {} -static inline void kasan_poison_kfree (void *ptr,unsigned long ip) {}static inline void *kasan_kmalloc (struct kmem_cache *s,const void *object,size_t size,gfp_t flags) {return (void *)object; } +static inline void *kasan_kmalloc_large (const void *ptr,size_t size,gfp_t flags) +{ +return (void *)ptr; +}static inline void *kasan_krealloc (const void *object,size_t new_size,gfp_t flags) {return (void *)object; } - -static inline void *kasan_slab_alloc (struct kmem_cache *s,void *object, -gfp_t flags) -{ -return object; -} -static inline bool kasan_slab_free (struct kmem_cache *s,void *object, -unsigned long ip) -{ -return false; -} - -static inline size_t kasan_metadata_size (struct kmem_cache *cache) {return 0 ; } +static inline void kasan_poison_kfree (void *ptr,unsigned long ip) {} +static inline void kasan_kfree_large (void *ptr,unsigned long ip) {}#endif /* CONFIG_KASAN */
include/linux/mm.h +16 -6
@@ -31,6 +31,7 @@#include <linux/sizes.h> #include <linux/sched.h> #include <linux/pgtable.h> +#include <linux/kasan.h> struct mempolicy ;struct anon_vma ;
@@ -1422,22 +1423,30 @@ static inline bool cpupid_match_pid(struct task_struct *task, int cpupid)#endif /* CONFIG_NUMA_BALANCING */ #if defined(CONFIG_KASAN_SW_TAGS ) || defined(CONFIG_KASAN_HW_TAGS ) +static inline u8 page_kasan_tag (const struct page *page) { -return (page->flags >>KASAN_TAG_PGSHIFT ) &KASAN_TAG_MASK ; +if (kasan_enabled ()) +return (page->flags >>KASAN_TAG_PGSHIFT ) &KASAN_TAG_MASK ; +return 0xff ; }static inline void page_kasan_tag_set (struct page *page,u8 tag) { - page->flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); - page->flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; +if (kasan_enabled ()) { + page->flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); + page->flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; + } }static inline void page_kasan_tag_reset (struct page *page) { -page_kasan_tag_set (page,0xff ); +if (kasan_enabled ()) +page_kasan_tag_set (page,0xff ); } -#else + +#else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ +static inline u8 page_kasan_tag (const struct page *page) {return 0xff ;
@@ -1445,7 +1454,8 @@ static inline u8 page_kasan_tag(const struct page *page)static inline void page_kasan_tag_set (struct page *page,u8 tag) { }static inline void page_kasan_tag_reset (struct page *page) { } -#endif + +#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ static inline struct zone *page_zone (const struct page *page) {
mm/kasan/common.c +28 -28
164 lines not on this page · raw message
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 13/19 kasan, mm: rename kasan_poison_kfree
In reply to Andrey KonovalovRename kasan_poison_kfree() to kasan_slab_free_mempool() as it better reflects what this annotation does. Also add a comment that explains the PageSlab() check.
No functional changes.
--- include/linux/kasan.h | 16 ++++++++-------- mm/kasan/common.c | 40 +++++++++++++++++++++++----------------- mm/mempool.c | 2 +- 3 files changed, 32 insertions(+), 26 deletions(-)
include/linux/kasan.h +8 -8
@@ -175,6 +175,13 @@ static __always_inline bool kasan_slab_free(struct kmem_cache *s, void *object,return false; } +void __kasan_slab_free_mempool (void *ptr,unsigned long ip); +static __always_inline void kasan_slab_free_mempool(void *ptr, unsigned long ip) +{ +if (kasan_enabled ()) +__kasan_slab_free_mempool (ptr, ip); +} +void *__must_check __kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags); static __always_inline void * __must_check kasan_slab_alloc(
@@ -215,13 +222,6 @@ static __always_inline void * __must_check kasan_krealloc(const void *object,return (void *)object; } -void __kasan_poison_kfree (void *ptr,unsigned long ip); -static __always_inline void kasan_poison_kfree(void *ptr, unsigned long ip) -{ -if (kasan_enabled ()) -__kasan_poison_kfree (ptr, ip); -} -void __kasan_kfree_large (void *ptr,unsigned long ip); static __always_inline void kasan_kfree_large(void *ptr, unsigned long ip) {
@@ -260,6 +260,7 @@ static inline bool kasan_slab_free(struct kmem_cache *s, void *object, {return false; } +static inline void kasan_slab_free_mempool (void *ptr,unsigned long ip) {}static inline void *kasan_slab_alloc (struct kmem_cache *s,void *object,gfp_t flags) {
@@ -279,7 +280,6 @@ static inline void *kasan_krealloc(const void *object, size_t new_size, {return (void *)object; } -static inline void kasan_poison_kfree (void *ptr,unsigned long ip) {}static inline void kasan_kfree_large (void *ptr,unsigned long ip) {}#endif /* CONFIG_KASAN */
mm/kasan/common.c +23 -17
@@ -335,6 +335,29 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object, unsigned long ip)return ____kasan_slab_free (cache, object, ip, true); } +void __kasan_slab_free_mempool (void *ptr,unsigned long ip) +{ +struct page *page; + + page =virt_to_head_page (ptr); + +/* +* Even though this function is only called for kmem_cache_alloc and +* kmalloc backed mempool allocations, those allocations can still be +* !PageSlab() when the size provided to kmalloc is larger than +* KMALLOC_MAX_SIZE, and kmalloc falls back onto page_alloc. +*/ +if (unlikely (!PageSlab (page))) { +if (ptr !=page_address (page)) { +kasan_report_invalid_free (ptr, ip); +return ; + } +poison_range (ptr,page_size (page),KASAN_FREE_PAGE ); + }else { +____kasan_slab_free (page->slab_cache, ptr, ip, false); + } +} +static void set_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) {kasan_set_track (&kasan_get_alloc_meta (cache, object)->alloc_track, flags);
@@ -429,23 +452,6 @@ void * __must_check __kasan_krealloc(const void *object, size_t size, gfp_t flag flags, true); } -void __kasan_poison_kfree (void *ptr,unsigned long ip) -{ -struct page *page; - - page =virt_to_head_page (ptr); - -if (unlikely (!PageSlab (page))) { -if (ptr !=page_address (page)) { -kasan_report_invalid_free (ptr, ip); -return ; - } -poison_range (ptr,page_size (page),KASAN_FREE_PAGE ); - }else { -____kasan_slab_free (page->slab_cache, ptr, ip, false); - } -} -void __kasan_kfree_large (void *ptr,unsigned long ip) {if (ptr !=page_address (virt_to_head_page (ptr)))
mm/mempool.c +1 -1
@@ -104,7 +104,7 @@ static inline void poison_element(mempool_t *pool, void *element) static __always_inline void kasan_poison_element(mempool_t *pool, void *element) {if (pool->alloc == mempool_alloc_slab || pool->alloc == mempool_kmalloc) -kasan_poison_kfree (element, _RET_IP_); +kasan_slab_free_mempool (element, _RET_IP_);else if (pool->alloc == mempool_alloc_pages)kasan_free_pages (element, (unsigned long )pool->pool_data); }
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 14/19 kasan: don't round_up too much
In reply to Andrey KonovalovFor hardware tag-based mode kasan_poison_memory() already rounds up the size. Do the same for software modes and remove round_up() from the common code.
--- mm/kasan/common.c | 8 ++------ mm/kasan/shadow.c | 1 + 2 files changed, 3 insertions(+), 6 deletions(-)
mm/kasan/common.c +2 -6
@@ -214,9 +214,7 @@ void __kasan_unpoison_object_data(struct kmem_cache *cache, void *object)void __kasan_poison_object_data (struct kmem_cache *cache,void *object) { -poison_range (object, -round_up (cache->object_size,KASAN_GRANULE_SIZE ), -KASAN_KMALLOC_REDZONE ); +poison_range (object, cache->object_size,KASAN_KMALLOC_REDZONE ); }/*
@@ -289,7 +287,6 @@ static bool ____kasan_slab_free(struct kmem_cache *cache, void *object, {u8 tag;void *tagged_object; -unsigned long rounded_up_size; tag =get_tag (object); tagged_object = object;
@@ -313,8 +310,7 @@ static bool ____kasan_slab_free(struct kmem_cache *cache, void *object,return true; } - rounded_up_size =round_up (cache->object_size,KASAN_GRANULE_SIZE ); -poison_range (object, rounded_up_size,KASAN_KMALLOC_FREE ); +poison_range (object, cache->object_size,KASAN_KMALLOC_FREE );if (!kasan_stack_collection_enabled ())return false;
mm/kasan/shadow.c +1 -0
@@ -83,6 +83,7 @@ void poison_range(const void *address, size_t size, u8 value)* addresses to this function. */ address =kasan_reset_tag (address); + size =round_up (size,KASAN_GRANULE_SIZE );/* Skip KFENCE memory if called explicitly outside of sl*b. */ if (is_kfence_address (address))
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 15/19 kasan: simplify assign_tag and set_tag calls
In reply to Andrey Konovalovset_tag() already ignores the tag for the generic mode, so just call it as is. Add a check for the generic mode to assign_tag(), and simplify its call in ____kasan_kmalloc().
--- mm/kasan/common.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
mm/kasan/common.c +7 -6
@@ -234,6 +234,9 @@ void __kasan_poison_object_data(struct kmem_cache *cache, void *object)static u8 assign_tag (struct kmem_cache *cache,const void *object,bool init,bool keep_tag) { +if (IS_ENABLED (CONFIG_KASAN_GENERIC )) +return 0xff ; +/* * 1. When an object is kmalloc()'ed, two hooks are called: * kasan_slab_alloc() and kasan_kmalloc(). We assign the
@@ -276,8 +279,8 @@ void * __must_check __kasan_init_slab_obj(struct kmem_cache *cache,__memset (alloc_meta,0 ,sizeof (*alloc_meta)); } -if (IS_ENABLED (CONFIG_KASAN_SW_TAGS ) ||IS_ENABLED (CONFIG_KASAN_HW_TAGS )) - object =set_tag (object,assign_tag (cache, object, true, false)); +/* Tag is ignored in set_tag() without CONFIG_KASAN_SW/HW_TAGS */ + object =set_tag (object,assign_tag (cache, object, true, false));return (void *)object; }
@@ -364,7 +367,7 @@ static void *____kasan_kmalloc(struct kmem_cache *cache, const void *object, {unsigned long redzone_start;unsigned long redzone_end; -u8 tag =0xff ; +u8 tag;if (gfpflags_allow_blocking (flags))quarantine_reduce ();
@@ -379,9 +382,7 @@ static void *____kasan_kmalloc(struct kmem_cache *cache, const void *object, KASAN_GRANULE_SIZE); redzone_end =round_up ((unsigned long )object + cache->object_size,KASAN_GRANULE_SIZE ); - -if (IS_ENABLED (CONFIG_KASAN_SW_TAGS ) ||IS_ENABLED (CONFIG_KASAN_HW_TAGS )) - tag =assign_tag (cache, object, false, keep_tag); + tag =assign_tag (cache, object, false, keep_tag);/* Tag is ignored in set_tag without CONFIG_KASAN_SW/HW_TAGS */ unpoison_range (set_tag (object, tag), size);
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 16/19 kasan: clarify comment in __kasan_kfree_large
In reply to Andrey KonovalovCurrently it says that the memory gets poisoned by page_alloc code. Clarify this by mentioning the specific callback that poisons the memory.
--- mm/kasan/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/kasan/common.c +1 -1
@@ -453,5 +453,5 @@ void __kasan_kfree_large(void *ptr, unsigned long ip) {if (ptr !=page_address (virt_to_head_page (ptr)))kasan_report_invalid_free (ptr, ip); -/* The object will be poisoned by page_alloc. */ +/* The object will be poisoned by kasan_free_pages(). */ }
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 17/19 kasan: sanitize objects when metadata doesn't fit
In reply to Andrey KonovalovKASAN marks caches that are sanitized with the SLAB_KASAN cache flag. Currently if the metadata that is appended after the object (stores e.g. stack trace ids) doesn't fit into KMALLOC_MAX_SIZE (can only happen with SLAB, see the comment in the patch), KASAN turns off sanitization completely.
With this change sanitization of the object data is always enabled. However the metadata is only stored when it fits. Instead of checking for SLAB_KASAN flag accross the code to find out whether the metadata is there, use cache->kasan_info.alloc/free_meta_offset. As 0 can be a valid value for free_meta_offset, introduce KASAN_NO_FREE_META as an indicator that the free metadata is missing.
Without this change all sanitized KASAN objects would be put into quarantine with generic KASAN. With this change, only the objects that have metadata (i.e. when it fits) are put into quarantine, the rest is freed right away.
Along the way rework __kasan_cache_create() and add claryfying comments.
--- mm/kasan/common.c | 116 ++++++++++++++++++++++++-------------- mm/kasan/generic.c | 15 ++--- mm/kasan/hw_tags.c | 6 +- mm/kasan/kasan.h | 17 +++++- mm/kasan/quarantine.c | 16 +++++- mm/kasan/report.c | 43 +++++++------- mm/kasan/report_sw_tags.c | 9 ++- mm/kasan/sw_tags.c | 4 ++ 8 files changed, 149 insertions(+), 77 deletions(-)
mm/kasan/common.c +74 -42
@@ -115,9 +115,6 @@ void __kasan_free_pages(struct page *page, unsigned int order)*/ static inline unsigned int optimal_redzone (unsigned int object_size) { -if (!IS_ENABLED (CONFIG_KASAN_GENERIC )) -return 0 ; -return object_size <=64 -16 ?16 : object_size <= 128 - 32 ? 32 :
@@ -131,47 +128,77 @@ static inline unsigned int optimal_redzone(unsigned int object_size)void __kasan_cache_create (struct kmem_cache *cache,unsigned int *size,slab_flags_t *flags) { -unsigned int orig_size = *size; -unsigned int redzone_size; -int redzone_adjust; +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: +* 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_stack_collection_enabled ()) { - *flags |=SLAB_KASAN ; +if (!kasan_stack_collection_enabled ())return ; - } -/* Add alloc meta. */ + ok_size = *size; + +/* Add alloc meta into redzone. */ cache->kasan_info.alloc_meta_offset = *size; *size +=sizeof (struct kasan_alloc_meta ); -/* Add free meta. */ -if (IS_ENABLED (CONFIG_KASAN_GENERIC ) && - (cache->flags &SLAB_TYPESAFE_BY_RCU || cache->ctor || - cache->object_size <sizeof (struct kasan_free_meta ))) { - cache->kasan_info.free_meta_offset = *size; - *size +=sizeof (struct kasan_free_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. */ } - redzone_size =optimal_redzone (cache->object_size); - redzone_adjust = redzone_size - (*size - cache->object_size); -if (redzone_adjust >0 ) - *size += redzone_adjust; - - *size = min_t(unsigned int, KMALLOC_MAX_SIZE, -max (*size, cache->object_size + redzone_size)); +/* 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 ; + }/* -* If the metadata doesn't fit, don't enable KASAN at all. +* 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 (*size <= cache->kasan_info.alloc_meta_offset || - *size <= cache->kasan_info.free_meta_offset) { - cache->kasan_info.alloc_meta_offset =0 ; - cache->kasan_info.free_meta_offset =0 ; - *size = orig_size; -return ; +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; + } } - *flags |=SLAB_KASAN ; +/* 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; }size_t __kasan_metadata_size (struct kmem_cache *cache)
@@ -187,15 +214,21 @@ size_t __kasan_metadata_size(struct kmem_cache *cache)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 page *page) {
@@ -272,11 +305,9 @@ void * __must_check __kasan_init_slab_obj(struct kmem_cache *cache,struct kasan_alloc_meta *alloc_meta;if (kasan_stack_collection_enabled ()) { -if (!(cache->flags &SLAB_KASAN )) -return (void *)object; - alloc_meta =kasan_get_alloc_meta (cache, object); -__memset (alloc_meta,0 ,sizeof (*alloc_meta)); +if (alloc_meta) +__memset (alloc_meta,0 ,sizeof (*alloc_meta)); }/* Tag is ignored in set_tag() without CONFIG_KASAN_SW/HW_TAGS */
@@ -318,15 +349,12 @@ static bool ____kasan_slab_free(struct kmem_cache *cache, void *object,if (!kasan_stack_collection_enabled ())return false; -if ((IS_ENABLED (CONFIG_KASAN_GENERIC ) && !quarantine) || -unlikely (!(cache->flags &SLAB_KASAN ))) +if ((IS_ENABLED (CONFIG_KASAN_GENERIC ) && !quarantine))return false;kasan_set_free_info (cache, object, tag); -quarantine_put (cache, object); - -return IS_ENABLED (CONFIG_KASAN_GENERIC ); +return quarantine_put (cache, object); }bool __kasan_slab_free (struct kmem_cache *cache,void *object,unsigned long ip)
@@ -359,7 +387,11 @@ void __kasan_slab_free_mempool(void *ptr, unsigned long ip)static void set_alloc_info (struct kmem_cache *cache,void *object,gfp_t flags) { -kasan_set_track (&kasan_get_alloc_meta (cache, object)->alloc_track, 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); } static void *____kasan_kmalloc(struct kmem_cache *cache, const void *object,
@@ -389,7 +421,7 @@ static void *____kasan_kmalloc(struct kmem_cache *cache, const void *object,poison_range ((void *)redzone_start, redzone_end - redzone_start,KASAN_KMALLOC_REDZONE ); -if (kasan_stack_collection_enabled () && (cache->flags &SLAB_KASAN )) +if (kasan_stack_collection_enabled ())set_alloc_info (cache, (void *)object, flags);return set_tag (object, tag);
mm/kasan/generic.c +8 -7
@@ -338,10 +338,10 @@ void kasan_record_aux_stack(void *addr) cache = page->slab_cache; object =nearest_obj (cache, page, addr); alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return ; -/* -* record the last two call_rcu() call stacks. -*/ +/* Record the last two call_rcu() call stacks. */ alloc_meta->aux_stack[1 ] = alloc_meta->aux_stack[0 ]; alloc_meta->aux_stack[0 ] =kasan_save_stack (GFP_NOWAIT ); }
@@ -352,11 +352,11 @@ void kasan_set_free_info(struct kmem_cache *cache,struct kasan_free_meta *free_meta; free_meta =kasan_get_free_meta (cache, object); -kasan_set_track (&free_meta->free_track,GFP_NOWAIT ); +if (!free_meta) +return ; -/* -* the object was freed and has free track set -*/ +kasan_set_track (&free_meta->free_track,GFP_NOWAIT ); +/* The object was freed and has free track set. */ *(u8 *)kasan_mem_to_shadow (object) =KASAN_KMALLOC_FREETRACK ; }
@@ -365,5 +365,6 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, {if (*(u8 *)kasan_mem_to_shadow (object) !=KASAN_KMALLOC_FREETRACK )return NULL ; +/* Free meta must be present with KASAN_KMALLOC_FREETRACK. */ return &kasan_get_free_meta (cache, object)->free_track; }
mm/kasan/hw_tags.c +5 -1
@@ -187,7 +187,8 @@ void kasan_set_free_info(struct kmem_cache *cache,struct kasan_alloc_meta *alloc_meta; alloc_meta =kasan_get_alloc_meta (cache, object); -kasan_set_track (&alloc_meta->free_track[0 ],GFP_NOWAIT ); +if (alloc_meta) +kasan_set_track (&alloc_meta->free_track[0 ],GFP_NOWAIT ); } struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
@@ -196,5 +197,8 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,struct kasan_alloc_meta *alloc_meta; alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return NULL ; +return &alloc_meta->free_track[0 ]; }
mm/kasan/kasan.h +14 -3
@@ -156,20 +156,31 @@ struct kasan_alloc_meta {struct qlist_node {struct qlist_node *next; }; + +/* +* Generic mode either stores free meta in the object itself or in the redzone +* after the object. In the former case free meta offset is 0, in the latter +* case it has some sane value smaller than INT_MAX. Use INT_MAX as free meta +* offset when free meta isn't present. +*/ +#define KASAN_NO_FREE_META INT_MAX +struct kasan_free_meta { +#ifdef CONFIG_KASAN_GENERIC /* This field is used while the object is in the quarantine. * Otherwise it might be used for the allocator freelist. */ struct qlist_node quarantine_link; -#ifdef CONFIG_KASAN_GENERIC struct kasan_track free_track;#endif };struct kasan_alloc_meta *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 )
@@ -234,11 +245,11 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,#if defined(CONFIG_KASAN_GENERIC ) && \ (defined(CONFIG_SLAB ) || defined(CONFIG_SLUB )) -void quarantine_put (struct kmem_cache *cache,void *object); +bool quarantine_put (struct kmem_cache *cache,void *object);void quarantine_reduce (void );void quarantine_remove_cache (struct kmem_cache *cache);#else -static inline void quarantine_put (struct kmem_cache *cache,void *object) { } +static inline bool quarantine_put (struct kmem_cache *cache,void *object) {return false; }static inline void quarantine_reduce (void ) { }static inline void quarantine_remove_cache (struct kmem_cache *cache) { }#endif
mm/kasan/quarantine.c +15 -1
@@ -135,7 +135,12 @@ static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)if (IS_ENABLED (CONFIG_SLAB ))local_irq_save (flags); +/* +* As the object now gets freed from the quaratine, assume that its +* free track is no longer valid. +*/ *(u8 *)kasan_mem_to_shadow (object) =KASAN_KMALLOC_FREE ; +___cache_free (cache, object, _THIS_IP_);if (IS_ENABLED (CONFIG_SLAB ))
@@ -161,13 +166,20 @@ static void qlist_free_all(struct qlist_head *q, struct kmem_cache *cache)qlist_init (q); } -void quarantine_put (struct kmem_cache *cache,void *object) +bool quarantine_put (struct kmem_cache *cache,void *object) {unsigned long flags;struct qlist_head *q;struct qlist_head temp =QLIST_INIT ;struct kasan_free_meta *meta =kasan_get_free_meta (cache, object); +/* +* If there's no metadata for this object, don't put it into +* quarantine. +*/ +if (!meta) +return false; +/* * Note: irq must be disabled until after we move the batch to the * global quarantine. Otherwise quarantine_remove_cache() can miss
@@ -200,6 +212,8 @@ void quarantine_put(struct kmem_cache *cache, void *object) }local_irq_restore (flags); + +return true; }void quarantine_reduce (void )
mm/kasan/report.c +23 -20
55 lines not on this page · raw message
mm/kasan/report_sw_tags.c +6 -3
@@ -48,9 +48,12 @@ const char *get_bug_type(struct kasan_access_info *info) object =nearest_obj (cache, page, (void *)addr); alloc_meta =kasan_get_alloc_meta (cache, object); -for (i =0 ; i <KASAN_NR_FREE_STACKS ; i++) -if (alloc_meta->free_pointer_tag[i] == tag) -return "use-after-free" ; +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" ; }
mm/kasan/sw_tags.c +4 -0
@@ -170,6 +170,8 @@ void kasan_set_free_info(struct kmem_cache *cache,u8 idx =0 ; alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return ;#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY idx = alloc_meta->free_track_idx;
@@ -187,6 +189,8 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,int i =0 ; alloc_meta =kasan_get_alloc_meta (cache, object); +if (!alloc_meta) +return NULL ;#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY for (i =0 ; i <KASAN_NR_FREE_STACKS ; i++) {
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 18/19 kasan, mm: allow cache merging with no metadata
In reply to Andrey KonovalovThe reason cache merging is disabled with KASAN is because KASAN puts its metadata right after the allocated object. When the merged caches have slightly different sizes, the metadata ends up in different places, which KASAN doesn't support.
It might be possible to adjust the metadata allocation algorithm and make it friendly to the cache merging code. Instead this change takes a simpler approach and allows merging caches when no metadata is present. Which is the case for hardware tag-based KASAN with kasan.mode=prod.
--- include/linux/kasan.h | 21 +++++++++++++++++++-- mm/kasan/common.c | 11 +++++++++++ mm/slab_common.c | 3 ++- 3 files changed, 32 insertions(+), 3 deletions(-)
include/linux/kasan.h +19 -2
@@ -81,17 +81,30 @@ struct kasan_cache { };#ifdef CONFIG_KASAN_HW_TAGS +DECLARE_STATIC_KEY_FALSE (kasan_flag_enabled); + static __always_inline bool kasan_enabled(void) {return static_branch_likely (&kasan_flag_enabled); } -#else + +#else /* CONFIG_KASAN_HW_TAGS */ +static inline bool kasan_enabled (void ) {return true; } -#endif + +#endif /* CONFIG_KASAN_HW_TAGS */ + +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)
@@ -238,6 +251,10 @@ static inline bool kasan_enabled(void) {return false; } +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_alloc_pages (struct page *page,unsigned int order) {}static inline void kasan_free_pages (struct page *page,unsigned int order) {}
mm/kasan/common.c +11 -0
@@ -87,6 +87,17 @@ 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. +*/ +slab_flags_t __kasan_never_merge (void ) +{ +if (kasan_stack_collection_enabled ()) +return SLAB_KASAN ; +return 0 ; +} +void __kasan_alloc_pages (struct page *page,unsigned int order) {u8 tag;
mm/slab_common.c +2 -1
@@ -19,6 +19,7 @@#include <linux/seq_file.h> #include <linux/proc_fs.h> #include <linux/debugfs.h> +#include <linux/kasan.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> #include <asm/page.h>
@@ -54,7 +55,7 @@ static DECLARE_WORK(slab_caches_to_rcu_destroy_work,*/ #define SLAB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \ SLAB_TRACE | SLAB_TYPESAFE_BY_RCU | SLAB_NOLEAKTRACE | \ - SLAB_FAILSLAB | SLAB_KASAN) + SLAB_FAILSLAB | kasan_never_merge())#define SLAB_MERGE_SAME (SLAB_RECLAIM_ACCOUNT | SLAB_CACHE_DMA | \ SLAB_CACHE_DMA32 | SLAB_ACCOUNT)
Signature · 1 line
2.29.2.454.gaff20da3a2-goog
PATCH v4 19/19 kasan: update documentation
In reply to Andrey KonovalovThis change updates KASAN documentation to reflect the addition of boot parameters and also reworks and clarifies some of the existing sections, in particular: defines what a memory granule is, mentions quarantine, makes Kunit section more readable.
--- Documentation/dev-tools/kasan.rst | 186 +++++++++++++++++++----------- 1 file changed, 116 insertions(+), 70 deletions(-)
Documentation/dev-tools/kasan.rst +116 -70
@@ -4,8 +4,9 @@ The Kernel Address Sanitizer (KASAN) Overview -------- -KernelAddressSANitizer (KASAN) is a dynamic memory error detector designed to -find out-of-bound and use-after-free bugs. KASAN has three modes: +KernelAddressSANitizer (KASAN) is a dynamic memory safety error detector +designed to find out-of-bound and use-after-free bugs. KASAN has three modes: + 1. generic KASAN (similar to userspace ASan), 2. software tag-based KASAN (similar to userspace HWASan), 3. hardware tag-based KASAN (based on hardware memory tagging).
@@ -39,23 +40,13 @@ CONFIG_KASAN_INLINE. Outline and inline are compiler instrumentation types. The former produces smaller binary while the latter is 1.1 - 2 times faster. Both software KASAN modes work with both SLUB and SLAB memory allocators, -hardware tag-based KASAN currently only support SLUB. -For better bug detection and nicer reporting, enable CONFIG_STACKTRACE. +while the hardware tag-based KASAN currently only support SLUB. + +For better error reports that include stack traces, enable CONFIG_STACKTRACE. To augment reports with last allocation and freeing stack of the physical page, it is recommended to enable also CONFIG_PAGE_OWNER and boot with page_owner=on. -To disable instrumentation for specific files or directories, add a line -similar to the following to the respective kernel Makefile: - -- For a single file (e.g. main.o):: - - KASAN_SANITIZE_main.o := n - -- For all files in one directory:: - - KASAN_SANITIZE := n - Error reports ~~~~~~~~~~~~~
@@ -140,22 +131,75 @@ freed (in case of a use-after-free bug report). Next comes a description of the accessed slab object and information about the accessed memory page. In the last section the report shows memory state around the accessed address. -Reading this part requires some understanding of how KASAN works. - -The state of each 8 aligned bytes of memory is encoded in one shadow byte. -Those 8 bytes can be accessible, partially accessible, freed or be a redzone. -We use the following encoding for each shadow byte: 0 means that all 8 bytes -of the corresponding memory region are accessible; number N (1 <= N <= 7) means -that the first N bytes are accessible, and other (8 - N) bytes are not; -any negative value indicates that the entire 8-byte word is inaccessible. -We use different negative values to distinguish between different kinds of -inaccessible memory like redzones or freed memory (see mm/kasan/kasan.h). +Internally KASAN tracks memory state separately for each memory granule, which +is either 8 or 16 aligned bytes depending on KASAN mode. Each number in the +memory state section of the report shows the state of one of the memory +granules that surround the accessed address. + +For generic KASAN the size of each memory granule is 8. The state of each +granule is encoded in one shadow byte. Those 8 bytes can be accessible, +partially accessible, freed or be a part of a redzone. KASAN uses the following +encoding for each shadow byte: 0 means that all 8 bytes of the corresponding +memory region are accessible; number N (1 <= N <= 7) means that the first N +bytes are accessible, and other (8 - N) bytes are not; any negative value +indicates that the entire 8-byte word is inaccessible. KASAN uses different +negative values to distinguish between different kinds of inaccessible memory +like redzones or freed memory (see mm/kasan/kasan.h). In the report above the arrows point to the shadow byte 03, which means that the accessed address is partially accessible. For tag-based KASAN this last report section shows the memory tags around the -accessed address (see Implementation details section). +accessed address (see `Implementation details`_ section). + +Boot parameters +~~~~~~~~~~~~~~~ + +Hardware tag-based KASAN mode (see the section about different mode below) is +intended for use in production as a security mitigation. Therefore it supports +boot parameters that allow to disable KASAN competely or otherwise control +particular KASAN features. + +The things that can be controlled are: + +1. Whether KASAN is enabled at all. +2. Whether KASAN collects and saves alloc/free stacks. +3. Whether KASAN panics on a detected bug or not. + +The ``kasan.mode`` boot parameter allows to choose one of three main modes: + +- ``kasan.mode=off`` - KASAN is disabled, no tag checks are performed +- ``kasan.mode=prod`` - only essential production features are enabled +- ``kasan.mode=full`` - all KASAN features are enabled + +The chosen mode provides default control values for the features mentioned +above. However it's also possible to override the default values by providing: + +- ``kasan.stacktrace=off`` or ``=on`` - enable alloc/free stack collection + (default: ``on`` for ``mode=full``, + otherwise ``off``) +- ``kasan.fault=report`` or ``=panic`` - only print KASAN report or also panic + (default: ``report``) + +If ``kasan.mode`` parameter is not provided, it defaults to ``full`` when +``CONFIG_DEBUG_KERNEL`` is enabled, and to ``prod`` otherwise. + +For developers +~~~~~~~~~~~~~~ + +Software KASAN modes use compiler instrumentation to insert validity checks. +Such instrumentation might be incompatible with some part of the kernel, and +therefore needs to be disabled. To disable instrumentation for specific files +or directories, add a line similar to the following to the respective kernel +Makefile: + +- For a single file (e.g. main.o):: + + KASAN_SANITIZE_main.o := n + +- For all files in one directory:: + + KASAN_SANITIZE := n Implementation details
@@ -164,10 +208,10 @@ Implementation details Generic KASAN ~~~~~~~~~~~~~ -From a high level, our approach to memory error detection is similar to that -of kmemcheck: use shadow memory to record whether each byte of memory is safe -to access, and use compile-time instrumentation to insert checks of shadow -memory on each memory access. +From a high level perspective, KASAN's approach to memory error detection is +similar to that of kmemcheck: use shadow memory to record whether each byte of +memory is safe to access, and use compile-time instrumentation to insert checks +of shadow memory on each memory access. Generic KASAN dedicates 1/8th of kernel memory to its shadow memory (e.g. 16TB to cover 128TB on x86_64) and uses direct mapping with a scale and offset to
@@ -194,7 +238,10 @@ function calls GCC directly inserts the code to check the shadow memory. This option significantly enlarges kernel but it gives x1.1-x2 performance boost over outline instrumented kernel. -Generic KASAN prints up to 2 call_rcu() call stacks in reports, the last one +Generic KASAN is the only mode that delays the reuse of freed object via +quarantine (see mm/kasan/quarantine.c for implementation). + +Generic KASAN prints up to two call_rcu() call stacks in reports, the last one and the second to last. Software tag-based KASAN
@@ -304,15 +351,15 @@ therefore be wasteful. Furthermore, to ensure that different mappings use different shadow pages, mappings would have to be aligned to ``KASAN_GRANULE_SIZE * PAGE_SIZE``. -Instead, we share backing space across multiple mappings. We allocate +Instead, KASAN shares backing space across multiple mappings. It allocates a backing page when a mapping in vmalloc space uses a particular page of the shadow region. This page can be shared by other vmalloc mappings later on. -We hook in to the vmap infrastructure to lazily clean up unused shadow +KASAN hooks into the vmap infrastructure to lazily clean up unused shadow memory. -To avoid the difficulties around swapping mappings around, we expect +To avoid the difficulties around swapping mappings around, KASAN expects that the part of the shadow region that covers the vmalloc space will not be covered by the early shadow page, but will be left unmapped. This will require changes in arch-specific code.
@@ -323,24 +370,31 @@ architectures that do not have a fixed module region. CONFIG_KASAN_KUNIT_TEST & CONFIG_TEST_KASAN_MODULE -------------------------------------------------- -``CONFIG_KASAN_KUNIT_TEST`` utilizes the KUnit Test Framework for testing. -This means each test focuses on a small unit of functionality and -there are a few ways these tests can be run. +KASAN tests consist on two parts: + +1. Tests that are integrated with the KUnit Test Framework. Enabled with +``CONFIG_KASAN_KUNIT_TEST``. These tests can be run and partially verified +automatically in a few different ways, see the instructions below. -Each test will print the KASAN report if an error is detected and then -print the number of the test and the status of the test: +2. Tests that are currently incompatible with KUnit. Enabled with +``CONFIG_TEST_KASAN_MODULE`` and can only be run as a module. These tests can +only be verified manually, by loading the kernel module and inspecting the +kernel log for KASAN reports. -pass:: +Each KUnit-compatible KASAN test prints a KASAN report if an error is detected. +Then the test prints its number and status. + +When a test passes:: ok 28 - kmalloc_double_kzfree -or, if kmalloc failed:: +When a test fails due to a failed ``kmalloc``:: # kmalloc_large_oob_right: ASSERTION FAILED at lib/test_kasan.c:163 Expected ptr is not null, but is not ok 4 - kmalloc_large_oob_right -or, if a KASAN report was expected, but not found:: +When a test fails due to a missing KASAN report:: # kmalloc_double_kzfree: EXPECTATION FAILED at lib/test_kasan.c:629 Expected kasan_data->report_expected == kasan_data->report_found, but
@@ -348,46 +402,38 @@ or, if a KASAN report was expected, but not found:: kasan_data->report_found == 0 not ok 28 - kmalloc_double_kzfree -All test statuses are tracked as they run and an overall status will -be printed at the end:: +At the end the cumulative status of all KASAN tests is printed. On success:: ok 1 - kasan -or:: +Or, if one of the tests failed:: not ok 1 - kasan -(1) Loadable Module -~~~~~~~~~~~~~~~~~~~~ + +There are a few ways to run KUnit-compatible KASAN tests. + +1. Loadable module +~~~~~~~~~~~~~~~~~~ With ``CONFIG_KUNIT`` enabled, ``CONFIG_KASAN_KUNIT_TEST`` can be built as -a loadable module and run on any architecture that supports KASAN -using something like insmod or modprobe. The module is called ``test_kasan``. +a loadable module and run on any architecture that supports KASAN by loading +the module with insmod or modprobe. The module is called ``test_kasan``. -(2) Built-In -~~~~~~~~~~~~~ +2. Built-In +~~~~~~~~~~~ With ``CONFIG_KUNIT`` built-in, ``CONFIG_KASAN_KUNIT_TEST`` can be built-in -on any architecure that supports KASAN. These and any other KUnit -tests enabled will run and print the results at boot as a late-init -call. +on any architecure that supports KASAN. These and any other KUnit tests enabled +will run and print the results at boot as a late-init call. -(3) Using kunit_tool -~~~~~~~~~~~~~~~~~~~~~ +3. Using kunit_tool +~~~~~~~~~~~~~~~~~~~ -With ``CONFIG_KUNIT`` and ``CONFIG_KASAN_KUNIT_TEST`` built-in, we can also -use kunit_tool to see the results of these along with other KUnit -tests in a more readable way. This will not print the KASAN reports -of tests that passed. Use `KUnit documentation <https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>`_ for more up-to-date -information on kunit_tool. +With ``CONFIG_KUNIT`` and ``CONFIG_KASAN_KUNIT_TEST`` built-in, it's also +possible use ``kunit_tool`` to see the results of these and other KUnit tests +in a more readable way. This will not print the KASAN reports of the tests that +passed. Use `KUnit documentation <https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>`_ +for more up-to-date information on ``kunit_tool``. .. _KUnit: https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html - -``CONFIG_TEST_KASAN_MODULE`` is a set of KASAN tests that could not be -converted to KUnit. These tests can be run only as a module with -``CONFIG_TEST_KASAN_MODULE`` built as a loadable module and -``CONFIG_KASAN`` built-in. The type of error expected and the -function being run is printed before the expression expected to give -an error. Then the error is printed, if found, and that test -should be interpretted to pass only if the error was the one expected -by the test.
Signature · 1 line
2.29.2.454.gaff20da3a2-goog