Commit f05842cf authored by Andrey Konovalov's avatar Andrey Konovalov Committed by Linus Torvalds

kasan, arm64: allow using KUnit tests with HW_TAGS mode

On a high level, this patch allows running KUnit KASAN tests with the
hardware tag-based KASAN mode.

Internally, this change reenables tag checking at the end of each KASAN
test that triggers a tag fault and leads to tag checking being disabled.

Also simplify is_write calculation in report_tag_fault.

With this patch KASAN tests are still failing for the hardware tag-based
mode; fixes come in the next few patches.

[andreyknvl@google.com: export HW_TAGS symbols for KUnit tests]
  Link: https://lkml.kernel.org/r/e7eeb252da408b08f0c81b950a55fb852f92000b.1613155970.git.andreyknvl@google.com

Link: https://linux-review.googlesource.com/id/Id94dc9eccd33b23cda4950be408c27f879e474c8
Link: https://lkml.kernel.org/r/51b23112cf3fd62b8f8e9df81026fa2b15870501.1610733117.git.andreyknvl@google.comSigned-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Reviewed-by: default avatarVincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Branislav Rankov <Branislav.Rankov@arm.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Evgenii Stepanov <eugenis@google.com>
Cc: Kevin Brodsky <kevin.brodsky@arm.com>
Cc: Marco Elver <elver@google.com>
Cc: Peter Collingbourne <pcc@google.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 573a4809
...@@ -244,6 +244,7 @@ static inline const void *__tag_set(const void *addr, u8 tag) ...@@ -244,6 +244,7 @@ static inline const void *__tag_set(const void *addr, u8 tag)
#ifdef CONFIG_KASAN_HW_TAGS #ifdef CONFIG_KASAN_HW_TAGS
#define arch_enable_tagging() mte_enable_kernel() #define arch_enable_tagging() mte_enable_kernel()
#define arch_set_tagging_report_once(state) mte_set_report_once(state)
#define arch_init_tags(max_tag) mte_init_tags(max_tag) #define arch_init_tags(max_tag) mte_init_tags(max_tag)
#define arch_get_random_tag() mte_get_random_tag() #define arch_get_random_tag() mte_get_random_tag()
#define arch_get_mem_tag(addr) mte_get_mem_tag(addr) #define arch_get_mem_tag(addr) mte_get_mem_tag(addr)
......
...@@ -32,6 +32,9 @@ void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag); ...@@ -32,6 +32,9 @@ void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag);
void mte_enable_kernel(void); void mte_enable_kernel(void);
void mte_init_tags(u64 max_tag); void mte_init_tags(u64 max_tag);
void mte_set_report_once(bool state);
bool mte_report_once(void);
#else /* CONFIG_ARM64_MTE */ #else /* CONFIG_ARM64_MTE */
static inline u8 mte_get_ptr_tag(void *ptr) static inline u8 mte_get_ptr_tag(void *ptr)
...@@ -60,6 +63,15 @@ static inline void mte_init_tags(u64 max_tag) ...@@ -60,6 +63,15 @@ static inline void mte_init_tags(u64 max_tag)
{ {
} }
static inline void mte_set_report_once(bool state)
{
}
static inline bool mte_report_once(void)
{
return false;
}
#endif /* CONFIG_ARM64_MTE */ #endif /* CONFIG_ARM64_MTE */
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
u64 gcr_kernel_excl __ro_after_init; u64 gcr_kernel_excl __ro_after_init;
static bool report_fault_once = true;
static void mte_sync_page_tags(struct page *page, pte_t *ptep, bool check_swap) static void mte_sync_page_tags(struct page *page, pte_t *ptep, bool check_swap)
{ {
pte_t old_pte = READ_ONCE(*ptep); pte_t old_pte = READ_ONCE(*ptep);
...@@ -158,6 +160,16 @@ void mte_enable_kernel(void) ...@@ -158,6 +160,16 @@ void mte_enable_kernel(void)
isb(); isb();
} }
void mte_set_report_once(bool state)
{
WRITE_ONCE(report_fault_once, state);
}
bool mte_report_once(void)
{
return READ_ONCE(report_fault_once);
}
static void update_sctlr_el1_tcf0(u64 tcf0) static void update_sctlr_el1_tcf0(u64 tcf0)
{ {
/* ISB required for the kernel uaccess routines */ /* ISB required for the kernel uaccess routines */
......
...@@ -302,12 +302,24 @@ static void die_kernel_fault(const char *msg, unsigned long addr, ...@@ -302,12 +302,24 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
static void report_tag_fault(unsigned long addr, unsigned int esr, static void report_tag_fault(unsigned long addr, unsigned int esr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0; static bool reported;
bool is_write;
if (READ_ONCE(reported))
return;
/*
* This is used for KASAN tests and assumes that no MTE faults
* happened before running the tests.
*/
if (mte_report_once())
WRITE_ONCE(reported, true);
/* /*
* SAS bits aren't set for all faults reported in EL1, so we can't * SAS bits aren't set for all faults reported in EL1, so we can't
* find out access size. * find out access size.
*/ */
is_write = !!(esr & ESR_ELx_WNR);
kasan_report(addr, 0, is_write, regs->pc); kasan_report(addr, 0, is_write, regs->pc);
} }
#else #else
...@@ -319,12 +331,8 @@ static inline void report_tag_fault(unsigned long addr, unsigned int esr, ...@@ -319,12 +331,8 @@ static inline void report_tag_fault(unsigned long addr, unsigned int esr,
static void do_tag_recovery(unsigned long addr, unsigned int esr, static void do_tag_recovery(unsigned long addr, unsigned int esr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
static bool reported;
if (!READ_ONCE(reported)) { report_tag_fault(addr, esr, regs);
report_tag_fault(addr, esr, regs);
WRITE_ONCE(reported, true);
}
/* /*
* Disable MTE Tag Checking on the local CPU for the current EL. * Disable MTE Tag Checking on the local CPU for the current EL.
......
...@@ -190,11 +190,11 @@ config KASAN_KUNIT_TEST ...@@ -190,11 +190,11 @@ config KASAN_KUNIT_TEST
kernel debugging features like KASAN. kernel debugging features like KASAN.
For more information on KUnit and unit tests in general, please refer For more information on KUnit and unit tests in general, please refer
to the KUnit documentation in Documentation/dev-tools/kunit to the KUnit documentation in Documentation/dev-tools/kunit.
config TEST_KASAN_MODULE config TEST_KASAN_MODULE
tristate "KUnit-incompatible tests of KASAN bug detection capabilities" tristate "KUnit-incompatible tests of KASAN bug detection capabilities"
depends on m && KASAN depends on m && KASAN && !KASAN_HW_TAGS
help help
This is a part of the KASAN test suite that is incompatible with This is a part of the KASAN test suite that is incompatible with
KUnit. Currently includes tests that do bad copy_from/to_user KUnit. Currently includes tests that do bad copy_from/to_user
......
...@@ -41,16 +41,20 @@ static bool multishot; ...@@ -41,16 +41,20 @@ static bool multishot;
/* /*
* Temporarily enable multi-shot mode. Otherwise, KASAN would only report the * Temporarily enable multi-shot mode. Otherwise, KASAN would only report the
* first detected bug and panic the kernel if panic_on_warn is enabled. * first detected bug and panic the kernel if panic_on_warn is enabled. For
* hardware tag-based KASAN also allow tag checking to be reenabled for each
* test, see the comment for KUNIT_EXPECT_KASAN_FAIL().
*/ */
static int kasan_test_init(struct kunit *test) static int kasan_test_init(struct kunit *test)
{ {
multishot = kasan_save_enable_multi_shot(); multishot = kasan_save_enable_multi_shot();
kasan_set_tagging_report_once(false);
return 0; return 0;
} }
static void kasan_test_exit(struct kunit *test) static void kasan_test_exit(struct kunit *test)
{ {
kasan_set_tagging_report_once(true);
kasan_restore_multi_shot(multishot); kasan_restore_multi_shot(multishot);
} }
...@@ -59,19 +63,31 @@ static void kasan_test_exit(struct kunit *test) ...@@ -59,19 +63,31 @@ static void kasan_test_exit(struct kunit *test)
* KASAN report; causes a test failure otherwise. This relies on a KUnit * KASAN report; causes a test failure otherwise. This relies on a KUnit
* resource named "kasan_data". Do not use this name for KUnit resources * resource named "kasan_data". Do not use this name for KUnit resources
* outside of KASAN tests. * outside of KASAN tests.
*
* For hardware tag-based KASAN, when a tag fault happens, tag checking is
* normally auto-disabled. When this happens, this test handler reenables
* tag checking. As tag checking can be only disabled or enabled per CPU, this
* handler disables migration (preemption).
*/ */
#define KUNIT_EXPECT_KASAN_FAIL(test, expression) do { \ #define KUNIT_EXPECT_KASAN_FAIL(test, expression) do { \
fail_data.report_expected = true; \ if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) \
fail_data.report_found = false; \ migrate_disable(); \
kunit_add_named_resource(test, \ fail_data.report_expected = true; \
NULL, \ fail_data.report_found = false; \
NULL, \ kunit_add_named_resource(test, \
&resource, \ NULL, \
"kasan_data", &fail_data); \ NULL, \
expression; \ &resource, \
KUNIT_EXPECT_EQ(test, \ "kasan_data", &fail_data); \
fail_data.report_expected, \ expression; \
fail_data.report_found); \ KUNIT_EXPECT_EQ(test, \
fail_data.report_expected, \
fail_data.report_found); \
if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) { \
if (fail_data.report_found) \
kasan_enable_tagging(); \
migrate_enable(); \
} \
} while (0) } while (0)
#define KASAN_TEST_NEEDS_CONFIG_ON(test, config) do { \ #define KASAN_TEST_NEEDS_CONFIG_ON(test, config) do { \
......
...@@ -185,3 +185,19 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, ...@@ -185,3 +185,19 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
return &alloc_meta->free_track[0]; return &alloc_meta->free_track[0];
} }
#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
void kasan_set_tagging_report_once(bool state)
{
hw_set_tagging_report_once(state);
}
EXPORT_SYMBOL_GPL(kasan_set_tagging_report_once);
void kasan_enable_tagging(void)
{
hw_enable_tagging();
}
EXPORT_SYMBOL_GPL(kasan_enable_tagging);
#endif
...@@ -280,6 +280,9 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag) ...@@ -280,6 +280,9 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
#ifndef arch_init_tags #ifndef arch_init_tags
#define arch_init_tags(max_tag) #define arch_init_tags(max_tag)
#endif #endif
#ifndef arch_set_tagging_report_once
#define arch_set_tagging_report_once(state)
#endif
#ifndef arch_get_random_tag #ifndef arch_get_random_tag
#define arch_get_random_tag() (0xFF) #define arch_get_random_tag() (0xFF)
#endif #endif
...@@ -292,12 +295,30 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag) ...@@ -292,12 +295,30 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
#define hw_enable_tagging() arch_enable_tagging() #define hw_enable_tagging() arch_enable_tagging()
#define hw_init_tags(max_tag) arch_init_tags(max_tag) #define hw_init_tags(max_tag) arch_init_tags(max_tag)
#define hw_set_tagging_report_once(state) arch_set_tagging_report_once(state)
#define hw_get_random_tag() arch_get_random_tag() #define hw_get_random_tag() arch_get_random_tag()
#define hw_get_mem_tag(addr) arch_get_mem_tag(addr) #define hw_get_mem_tag(addr) arch_get_mem_tag(addr)
#define hw_set_mem_tag_range(addr, size, tag) arch_set_mem_tag_range((addr), (size), (tag)) #define hw_set_mem_tag_range(addr, size, tag) arch_set_mem_tag_range((addr), (size), (tag))
#else /* CONFIG_KASAN_HW_TAGS */
#define hw_enable_tagging()
#define hw_set_tagging_report_once(state)
#endif /* CONFIG_KASAN_HW_TAGS */ #endif /* CONFIG_KASAN_HW_TAGS */
#if defined(CONFIG_KASAN_HW_TAGS) && IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
void kasan_set_tagging_report_once(bool state);
void kasan_enable_tagging(void);
#else /* CONFIG_KASAN_HW_TAGS || CONFIG_KASAN_KUNIT_TEST */
static inline void kasan_set_tagging_report_once(bool state) { }
static inline void kasan_enable_tagging(void) { }
#endif /* CONFIG_KASAN_HW_TAGS || CONFIG_KASAN_KUNIT_TEST */
#ifdef CONFIG_KASAN_SW_TAGS #ifdef CONFIG_KASAN_SW_TAGS
u8 kasan_random_tag(void); u8 kasan_random_tag(void);
#elif defined(CONFIG_KASAN_HW_TAGS) #elif defined(CONFIG_KASAN_HW_TAGS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment