mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 05:55:44 +02:00
BACKPORT: kasan, arm64: allow using KUnit tests with HW_TAGS mode
[ Conflicts due to conflicts when reverting the FROMGIT version. ]
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.com
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Vincenzo 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
(cherry picked from commit f05842cfb9)
Bug: 172318110
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Change-Id: I56c2fb669b1440b6bb3cd5e41e514a6179ea3b81
This commit is contained in:
parent
9f925b293d
commit
c7d68c4502
|
|
@ -233,6 +233,7 @@ static inline const void *__tag_set(const void *addr, u8 tag)
|
|||
|
||||
#ifdef CONFIG_KASAN_HW_TAGS
|
||||
#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_get_random_tag() mte_get_random_tag()
|
||||
#define arch_get_mem_tag(addr) mte_get_mem_tag(addr)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@ static inline void mte_set_mem_tag_range(void *addr, size_t size, u8 tag)
|
|||
void mte_enable_kernel(void);
|
||||
void mte_init_tags(u64 max_tag);
|
||||
|
||||
void mte_set_report_once(bool state);
|
||||
bool mte_report_once(void);
|
||||
|
||||
#else /* CONFIG_ARM64_MTE */
|
||||
|
||||
static inline u8 mte_get_ptr_tag(void *ptr)
|
||||
|
|
@ -109,6 +112,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 /* __ASSEMBLY__ */
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
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)
|
||||
{
|
||||
pte_t old_pte = READ_ONCE(*ptep);
|
||||
|
|
@ -112,6 +114,16 @@ void mte_enable_kernel(void)
|
|||
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)
|
||||
{
|
||||
/* ISB required for the kernel uaccess routines */
|
||||
|
|
|
|||
|
|
@ -306,12 +306,24 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
|
|||
static void report_tag_fault(unsigned long addr, unsigned int esr,
|
||||
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
|
||||
* find out access size.
|
||||
*/
|
||||
is_write = !!(esr & ESR_ELx_WNR);
|
||||
kasan_report(addr, 0, is_write, regs->pc);
|
||||
}
|
||||
#else
|
||||
|
|
@ -323,12 +335,8 @@ static inline void report_tag_fault(unsigned long addr, unsigned int esr,
|
|||
static void do_tag_recovery(unsigned long addr, unsigned int esr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
static bool reported;
|
||||
|
||||
if (!READ_ONCE(reported)) {
|
||||
report_tag_fault(addr, esr, regs);
|
||||
WRITE_ONCE(reported, true);
|
||||
}
|
||||
report_tag_fault(addr, esr, regs);
|
||||
|
||||
/*
|
||||
* Disable MTE Tag Checking on the local CPU for the current EL.
|
||||
|
|
|
|||
|
|
@ -199,11 +199,11 @@ config KASAN_KUNIT_TEST
|
|||
kernel debugging features like KASAN.
|
||||
|
||||
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 KASAN_MODULE_TEST
|
||||
tristate "KUnit-incompatible tests of KASAN bug detection capabilities"
|
||||
depends on m && KASAN
|
||||
depends on m && KASAN && !KASAN_HW_TAGS
|
||||
help
|
||||
This is a part of the KASAN test suite that is incompatible with
|
||||
KUnit. Currently includes tests that do bad copy_from/to_user
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ static bool multishot;
|
|||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
|
|
@ -51,11 +53,13 @@ static int kasan_test_init(struct kunit *test)
|
|||
}
|
||||
|
||||
multishot = kasan_save_enable_multi_shot();
|
||||
kasan_set_tagging_report_once(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kasan_test_exit(struct kunit *test)
|
||||
{
|
||||
kasan_set_tagging_report_once(true);
|
||||
kasan_restore_multi_shot(multishot);
|
||||
}
|
||||
|
||||
|
|
@ -65,12 +69,19 @@ static void kasan_test_exit(struct kunit *test)
|
|||
* resource named "kasan_data". Do not use this name for KUnit resources
|
||||
* 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).
|
||||
*
|
||||
* Since the compiler doesn't see that the expression can change the fail_data
|
||||
* fields, it can reorder or optimize away the accesses to those fields.
|
||||
* Use READ/WRITE_ONCE() for the accesses and compiler barriers around the
|
||||
* expression to prevent that.
|
||||
*/
|
||||
#define KUNIT_EXPECT_KASAN_FAIL(test, expression) do { \
|
||||
if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) \
|
||||
migrate_disable(); \
|
||||
WRITE_ONCE(fail_data.report_expected, true); \
|
||||
WRITE_ONCE(fail_data.report_found, false); \
|
||||
kunit_add_named_resource(test, \
|
||||
|
|
@ -84,6 +95,11 @@ static void kasan_test_exit(struct kunit *test)
|
|||
KUNIT_EXPECT_EQ(test, \
|
||||
READ_ONCE(fail_data.report_expected), \
|
||||
READ_ONCE(fail_data.report_found)); \
|
||||
if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) { \
|
||||
if (READ_ONCE(fail_data.report_found)) \
|
||||
kasan_enable_tagging(); \
|
||||
migrate_enable(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define KASAN_TEST_NEEDS_CONFIG_ON(test, config) do { \
|
||||
|
|
|
|||
|
|
@ -185,3 +185,19 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
|
|||
#ifndef arch_init_tags
|
||||
#define arch_init_tags(max_tag)
|
||||
#endif
|
||||
#ifndef arch_set_tagging_report_once
|
||||
#define arch_set_tagging_report_once(state)
|
||||
#endif
|
||||
#ifndef arch_get_random_tag
|
||||
#define arch_get_random_tag() (0xFF)
|
||||
#endif
|
||||
|
|
@ -293,12 +296,30 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
|
|||
|
||||
#define hw_enable_tagging() arch_enable_tagging()
|
||||
#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_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))
|
||||
|
||||
#else /* CONFIG_KASAN_HW_TAGS */
|
||||
|
||||
#define hw_enable_tagging()
|
||||
#define hw_set_tagging_report_once(state)
|
||||
|
||||
#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
|
||||
u8 kasan_random_tag(void);
|
||||
#elif defined(CONFIG_KASAN_HW_TAGS)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user