From: Yeoreum Yun <yeoreum.yun@arm.com>
To: Andrey Konovalov <andreyknvl@gmail.com>
Cc: glider@google.com, Marco Elver <elver@google.com>,
ryabinin.a.a@gmail.com, dvyukov@google.com,
vincenzo.frascino@arm.com, corbet@lwn.net,
catalin.marinas@arm.com, will@kernel.org,
akpm@linux-foundation.org, scott@os.amperecomputing.com,
jhubbard@nvidia.com, pankaj.gupta@amd.com, leitao@debian.org,
kaleshsingh@google.com, maz@kernel.org, broonie@kernel.org,
oliver.upton@linux.dev, james.morse@arm.com, ardb@kernel.org,
hardevsinh.palaniya@siliconsignals.io, david@redhat.com,
yang@os.amperecomputing.com, kasan-dev@googlegroups.com,
workflows@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-mm@kvack.org
Subject: Re: [PATCH v2 1/2] kasan/hw-tags: introduce kasan.store_only option
Date: Thu, 14 Aug 2025 09:51:14 +0100 [thread overview]
Message-ID: <aJ2jgqKYL2C6bUjC@e129823.arm.com> (raw)
In-Reply-To: <CA+fCnZd=EQ+5b=rBQ66LkJ3Bz2GrKHvnYk0DQLbs=o9=k0C69g@mail.gmail.com>
Hi Andrey,
> On Wed, Aug 13, 2025 at 7:53 PM Yeoreum Yun <yeoreum.yun@arm.com> wrote:
> >
> > Since Armv8.9, FEATURE_MTE_STORE_ONLY feature is introduced to restrict
> > raise of tag check fault on store operation only.
> > Introcude KASAN store only mode based on this feature.
> >
> > KASAN store only mode restricts KASAN checks operation for store only and
> > omits the checks for fetch/read operation when accessing memory.
> > So it might be used not only debugging enviroment but also normal
> > enviroment to check memory safty.
> >
> > This features can be controlled with "kasan.store_only" arguments.
> > When "kasan.store_only=on", KASAN checks store only mode otherwise
> > KASAN checks all operations.
>
> I'm thinking if we should name this "kasan.write_only" instead of
> "kasan.store_only". This would align the terms with the
> "kasan.fault=panic_on_write" parameter we already have. But then it
> would be different from "FEATURE_MTE_STORE_ONLY", which is what Arm
> documentation uses (right?).
Yes. it uses "MTE_STORE_ONLY". but, write seems fine for me too.
>
> Marco, Alexander, any opinion?
>
> >
> > Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> > ---
> > Documentation/dev-tools/kasan.rst | 3 ++
> > arch/arm64/include/asm/memory.h | 1 +
> > arch/arm64/include/asm/mte-kasan.h | 6 +++
> > arch/arm64/kernel/cpufeature.c | 6 +++
> > arch/arm64/kernel/mte.c | 14 ++++++
> > include/linux/kasan.h | 2 +
> > mm/kasan/hw_tags.c | 76 +++++++++++++++++++++++++++++-
> > mm/kasan/kasan.h | 10 ++++
> > 8 files changed, 116 insertions(+), 2 deletions(-)
> >
> > diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
> > index 0a1418ab72fd..fcb70dd821ec 100644
> > --- a/Documentation/dev-tools/kasan.rst
> > +++ b/Documentation/dev-tools/kasan.rst
> > @@ -143,6 +143,9 @@ disabling KASAN altogether or controlling its features:
> > Asymmetric mode: a bad access is detected synchronously on reads and
> > asynchronously on writes.
> >
> > +- ``kasan.store_only=off`` or ``kasan.store_only=on`` controls whether KASAN
> > + checks the store (write) accesses only or all accesses (default: ``off``)
> > +
> > - ``kasan.vmalloc=off`` or ``=on`` disables or enables tagging of vmalloc
> > allocations (default: ``on``).
> >
> > diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
> > index 5213248e081b..ae29cd3db78d 100644
> > --- a/arch/arm64/include/asm/memory.h
> > +++ b/arch/arm64/include/asm/memory.h
> > @@ -308,6 +308,7 @@ static inline const void *__tag_set(const void *addr, u8 tag)
> > #define arch_enable_tag_checks_sync() mte_enable_kernel_sync()
> > #define arch_enable_tag_checks_async() mte_enable_kernel_async()
> > #define arch_enable_tag_checks_asymm() mte_enable_kernel_asymm()
> > +#define arch_enable_tag_checks_store_only() mte_enable_kernel_store_only()
> > #define arch_suppress_tag_checks_start() mte_enable_tco()
> > #define arch_suppress_tag_checks_stop() mte_disable_tco()
> > #define arch_force_async_tag_fault() mte_check_tfsr_exit()
> > diff --git a/arch/arm64/include/asm/mte-kasan.h b/arch/arm64/include/asm/mte-kasan.h
> > index 2e98028c1965..3e1cc341d47a 100644
> > --- a/arch/arm64/include/asm/mte-kasan.h
> > +++ b/arch/arm64/include/asm/mte-kasan.h
> > @@ -200,6 +200,7 @@ static inline void mte_set_mem_tag_range(void *addr, size_t size, u8 tag,
> > void mte_enable_kernel_sync(void);
> > void mte_enable_kernel_async(void);
> > void mte_enable_kernel_asymm(void);
> > +int mte_enable_kernel_store_only(void);
> >
> > #else /* CONFIG_ARM64_MTE */
> >
> > @@ -251,6 +252,11 @@ static inline void mte_enable_kernel_asymm(void)
> > {
> > }
> >
> > +static inline int mte_enable_kenrel_store_only(void)
>
> Typo in the function name. Please build/boot test without MTE/KASAN enabled.
Oops... Sorry for mistake :\
>
> > +{
> > + return -EINVAL;
> > +}
> > +
> > #endif /* CONFIG_ARM64_MTE */
> >
> > #endif /* __ASSEMBLY__ */
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 9ad065f15f1d..7b724fcf20a7 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -2404,6 +2404,11 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)
> >
> > kasan_init_hw_tags_cpu();
> > }
> > +
> > +static void cpu_enable_mte_store_only(struct arm64_cpu_capabilities const *cap)
> > +{
> > + kasan_late_init_hw_tags_cpu();
> > +}
> > #endif /* CONFIG_ARM64_MTE */
> >
> > static void user_feature_fixup(void)
> > @@ -2922,6 +2927,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
> > .capability = ARM64_MTE_STORE_ONLY,
> > .type = ARM64_CPUCAP_SYSTEM_FEATURE,
> > .matches = has_cpuid_feature,
> > + .cpu_enable = cpu_enable_mte_store_only,
> > ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, MTESTOREONLY, IMP)
> > },
> > #endif /* CONFIG_ARM64_MTE */
> > diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
> > index e5e773844889..8eb1f66f2ccd 100644
> > --- a/arch/arm64/kernel/mte.c
> > +++ b/arch/arm64/kernel/mte.c
> > @@ -157,6 +157,20 @@ void mte_enable_kernel_asymm(void)
> > mte_enable_kernel_sync();
> > }
> > }
> > +
> > +int mte_enable_kernel_store_only(void)
> > +{
> > + if (!cpus_have_cap(ARM64_MTE_STORE_ONLY))
> > + return -EINVAL;
> > +
> > + sysreg_clear_set(sctlr_el1, SCTLR_EL1_TCSO_MASK,
> > + SYS_FIELD_PREP(SCTLR_EL1, TCSO, 1));
> > + isb();
> > +
> > + pr_info_once("MTE: enabled stonly mode at EL1\n");
> > +
> > + return 0;
> > +}
> > #endif
> >
> > #ifdef CONFIG_KASAN_HW_TAGS
> > diff --git a/include/linux/kasan.h b/include/linux/kasan.h
> > index 890011071f2b..28951b29c593 100644
> > --- a/include/linux/kasan.h
> > +++ b/include/linux/kasan.h
> > @@ -552,9 +552,11 @@ static inline void kasan_init_sw_tags(void) { }
> > #ifdef CONFIG_KASAN_HW_TAGS
> > void kasan_init_hw_tags_cpu(void);
> > void __init kasan_init_hw_tags(void);
> > +void kasan_late_init_hw_tags_cpu(void);
> > #else
> > static inline void kasan_init_hw_tags_cpu(void) { }
> > static inline void kasan_init_hw_tags(void) { }
> > +static inline void kasan_late_init_hw_tags_cpu(void) { }
> > #endif
> >
> > #ifdef CONFIG_KASAN_VMALLOC
> > diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
> > index 9a6927394b54..c2f90c06076e 100644
> > --- a/mm/kasan/hw_tags.c
> > +++ b/mm/kasan/hw_tags.c
> > @@ -41,9 +41,16 @@ enum kasan_arg_vmalloc {
> > KASAN_ARG_VMALLOC_ON,
> > };
> >
> > +enum kasan_arg_store_only {
> > + KASAN_ARG_STORE_ONLY_DEFAULT,
> > + KASAN_ARG_STORE_ONLY_OFF,
> > + KASAN_ARG_STORE_ONLY_ON,
> > +};
> > +
> > static enum kasan_arg kasan_arg __ro_after_init;
> > static enum kasan_arg_mode kasan_arg_mode __ro_after_init;
> > static enum kasan_arg_vmalloc kasan_arg_vmalloc __initdata;
> > +static enum kasan_arg_store_only kasan_arg_store_only __ro_after_init;
> >
> > /*
> > * Whether KASAN is enabled at all.
> > @@ -67,6 +74,9 @@ DEFINE_STATIC_KEY_FALSE(kasan_flag_vmalloc);
> > #endif
> > EXPORT_SYMBOL_GPL(kasan_flag_vmalloc);
> >
> > +DEFINE_STATIC_KEY_FALSE(kasan_flag_store_only);
>
> Is there a reason to have this as a static key? I think a normal
> global bool would work, just as a normal variable works for
> kasan_mode.
Just for align with the other arguments.
since the kasan_flags_store_only is used only for kunit-test,
not called in any other place, this optimisation is meaningless.
It's fine to change as global bool.
>
> > +EXPORT_SYMBOL_GPL(kasan_flag_store_only);
> > +
> > #define PAGE_ALLOC_SAMPLE_DEFAULT 1
> > #define PAGE_ALLOC_SAMPLE_ORDER_DEFAULT 3
> >
> > @@ -141,6 +151,23 @@ static int __init early_kasan_flag_vmalloc(char *arg)
> > }
> > early_param("kasan.vmalloc", early_kasan_flag_vmalloc);
> >
> > +/* kasan.store_only=off/on */
> > +static int __init early_kasan_flag_store_only(char *arg)
> > +{
> > + if (!arg)
> > + return -EINVAL;
> > +
> > + if (!strcmp(arg, "off"))
> > + kasan_arg_store_only = KASAN_ARG_STORE_ONLY_OFF;
> > + else if (!strcmp(arg, "on"))
> > + kasan_arg_store_only = KASAN_ARG_STORE_ONLY_ON;
> > + else
> > + return -EINVAL;
> > +
> > + return 0;
> > +}
> > +early_param("kasan.store_only", early_kasan_flag_store_only);
> > +
> > static inline const char *kasan_mode_info(void)
> > {
> > if (kasan_mode == KASAN_MODE_ASYNC)
> > @@ -219,6 +246,20 @@ void kasan_init_hw_tags_cpu(void)
> > kasan_enable_hw_tags();
> > }
> >
> > +/*
> > + * kasan_late_init_hw_tags_cpu_post() is called for each CPU after
> > + * all cpus are bring-up at boot.
>
> "CPUs"
> "brought up"
>
> And please spell-check other comments.
Thanks.
>
> > + * Not marked as __init as a CPU can be hot-plugged after boot.
> > + */
> > +void kasan_late_init_hw_tags_cpu(void)
> > +{
> > + /*
> > + * Enable stonly mode only when explicitly requested through the command line.
>
> "store-only"
>
> > + * If system doesn't support, kasan checks all operation.
>
> "If the system doesn't support this mode, KASAN will check both load
> and store operations."
Thanks for suggestion :)
>
> > + */
> > + kasan_enable_store_only();
> > +}
> > +
> > /* kasan_init_hw_tags() is called once on boot CPU. */
> > void __init kasan_init_hw_tags(void)
> > {
> > @@ -257,15 +298,28 @@ void __init kasan_init_hw_tags(void)
> > break;
> > }
> >
> > + switch (kasan_arg_store_only) {
> > + case KASAN_ARG_STORE_ONLY_DEFAULT:
> > + /* Default is specified by kasan_flag_store_only definition. */
> > + break;
> > + case KASAN_ARG_STORE_ONLY_OFF:
> > + static_branch_disable(&kasan_flag_store_only);
> > + break;
> > + case KASAN_ARG_STORE_ONLY_ON:
> > + static_branch_enable(&kasan_flag_store_only);
> > + break;
> > + }
>
> Let's move this part to kasan_late_init_hw_tags_cpu. Since that's
> where the final decision of whether the store-only mode is enabled is
> taken, we should just set the global flag there.
Okay.
>
> > +
> > kasan_init_tags();
> >
> > /* KASAN is now initialized, enable it. */
> > static_branch_enable(&kasan_flag_enabled);
> >
> > - pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s)\n",
> > + pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s store_only=%s\n",
>
> Let's put "store_only" here next to "mode".
Hmm, I think it's not a proper place to print store_only in here.
I think it would be better to print log related store_only at
kasan_late_init_hw_tags_cpu() like:
print_info("KernelAddressSanitizer checks store(write) access only.\n");
When store_only=on.
>
> You're also missing a comma.
>
> > kasan_mode_info(),
> > str_on_off(kasan_vmalloc_enabled()),
> > - str_on_off(kasan_stack_collection_enabled()));
> > + str_on_off(kasan_stack_collection_enabled()),
> > + str_on_off(kasan_store_only_enabled()));
> > }
> >
> > #ifdef CONFIG_KASAN_VMALLOC
> > @@ -394,6 +448,22 @@ void kasan_enable_hw_tags(void)
> > hw_enable_tag_checks_sync();
> > }
> >
> > +void kasan_enable_store_only(void)
>
> Do we need this as a separate function? I think we can just move the
> code to kasan_late_init_hw_tags_cpu.
>
> > +{
> > + if (kasan_arg_store_only == KASAN_ARG_STORE_ONLY_ON) {
> > + if (hw_enable_tag_checks_store_only()) {
> > + static_branch_disable(&kasan_flag_store_only);
> > + kasan_arg_store_only = KASAN_ARG_STORE_ONLY_OFF;
> > + pr_warn_once("KernelAddressSanitizer: store only mode isn't supported (hw-tags)\n");
>
> No need for the "KernelAddressSanitizer" prefix, it's already defined
> via pr_fmt().
Okay.
>
> > + }
> > + }
> > +}
> > +
> > +bool kasan_store_only_enabled(void)
> > +{
> > + return static_branch_unlikely(&kasan_flag_store_only);
> > +}
> > +
> > #if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
> >
> > EXPORT_SYMBOL_IF_KUNIT(kasan_enable_hw_tags);
> > @@ -404,4 +474,6 @@ VISIBLE_IF_KUNIT void kasan_force_async_fault(void)
> > }
> > EXPORT_SYMBOL_IF_KUNIT(kasan_force_async_fault);
> >
> > +EXPORT_SYMBOL_IF_KUNIT(kasan_store_only_enabled);
> > +
> > #endif
> > diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> > index 129178be5e64..1d853de1c499 100644
> > --- a/mm/kasan/kasan.h
> > +++ b/mm/kasan/kasan.h
> > @@ -33,6 +33,7 @@ static inline bool kasan_stack_collection_enabled(void)
> > #include "../slab.h"
> >
> > DECLARE_STATIC_KEY_TRUE(kasan_flag_vmalloc);
> > +DECLARE_STATIC_KEY_FALSE(kasan_flag_stonly);
>
> kasan_flag_store_only
>
> Did you build test this at all?
Yes, But there is no place where directly use kasan_flag_stonly,
I think I miss it. Thanks!
>
>
> >
> > enum kasan_mode {
> > KASAN_MODE_SYNC,
> > @@ -428,6 +429,7 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
> > #define hw_enable_tag_checks_sync() arch_enable_tag_checks_sync()
> > #define hw_enable_tag_checks_async() arch_enable_tag_checks_async()
> > #define hw_enable_tag_checks_asymm() arch_enable_tag_checks_asymm()
> > +#define hw_enable_tag_checks_store_only() arch_enable_tag_checks_store_only()
> > #define hw_suppress_tag_checks_start() arch_suppress_tag_checks_start()
> > #define hw_suppress_tag_checks_stop() arch_suppress_tag_checks_stop()
> > #define hw_force_async_tag_fault() arch_force_async_tag_fault()
> > @@ -437,10 +439,18 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
> > arch_set_mem_tag_range((addr), (size), (tag), (init))
> >
> > void kasan_enable_hw_tags(void);
> > +void kasan_enable_store_only(void);
> > +bool kasan_store_only_enabled(void);
> >
> > #else /* CONFIG_KASAN_HW_TAGS */
> >
> > static inline void kasan_enable_hw_tags(void) { }
> > +static inline void kasan_enable_store_only(void) { }
> > +
> > +static inline bool kasan_store_only_enabled(void)
> > +{
> > + return false;
> > +}
> >
> > #endif /* CONFIG_KASAN_HW_TAGS */
> >
> > --
> > LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
> >
> > --
> > You received this message because you are subscribed to the Google Groups "kasan-dev" group.
> > To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+unsubscribe@googlegroups.com.
> > To view this discussion visit https://groups.google.com/d/msgid/kasan-dev/20250813175335.3980268-2-yeoreum.yun%40arm.com.
--
Sincerely,
Yeoreum Yun
next prev parent reply other threads:[~2025-08-14 9:43 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-13 17:53 [PATCH v2 0/2] introduce kasan.store_only option in hw-tags Yeoreum Yun
2025-08-13 17:53 ` [PATCH v2 1/2] kasan/hw-tags: introduce kasan.store_only option Yeoreum Yun
2025-08-14 5:03 ` Andrey Konovalov
2025-08-14 8:51 ` Yeoreum Yun [this message]
2025-08-15 11:19 ` Catalin Marinas
2025-08-15 11:13 ` Catalin Marinas
2025-08-15 13:51 ` Yeoreum Yun
2025-08-15 15:10 ` Yeoreum Yun
2025-08-15 17:44 ` Catalin Marinas
2025-08-15 14:47 ` Yeoreum Yun
2025-08-15 17:46 ` Catalin Marinas
2025-08-13 17:53 ` [PATCH v2 2/2] kasan: apply store-only mode in kasan kunit testcases Yeoreum Yun
2025-08-14 5:04 ` Andrey Konovalov
2025-08-14 11:13 ` Yeoreum Yun
2025-08-15 6:14 ` Andrey Konovalov
2025-08-15 8:06 ` Yeoreum Yun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aJ2jgqKYL2C6bUjC@e129823.arm.com \
--to=yeoreum.yun@arm.com \
--cc=akpm@linux-foundation.org \
--cc=andreyknvl@gmail.com \
--cc=ardb@kernel.org \
--cc=broonie@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=corbet@lwn.net \
--cc=david@redhat.com \
--cc=dvyukov@google.com \
--cc=elver@google.com \
--cc=glider@google.com \
--cc=hardevsinh.palaniya@siliconsignals.io \
--cc=james.morse@arm.com \
--cc=jhubbard@nvidia.com \
--cc=kaleshsingh@google.com \
--cc=kasan-dev@googlegroups.com \
--cc=leitao@debian.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=maz@kernel.org \
--cc=oliver.upton@linux.dev \
--cc=pankaj.gupta@amd.com \
--cc=ryabinin.a.a@gmail.com \
--cc=scott@os.amperecomputing.com \
--cc=vincenzo.frascino@arm.com \
--cc=will@kernel.org \
--cc=workflows@vger.kernel.org \
--cc=yang@os.amperecomputing.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.