[PATCH v3 0/2] introduce kasan.write_only option in hw-tags

1 view
Skip to first unread message

Yeoreum Yun

unread,
Aug 16, 2025, 7:00:26 AMAug 16
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Hardware tag based KASAN is implemented using the Memory Tagging Extension
(MTE) feature.

MTE is built on top of the ARMv8.0 virtual address tagging TBI
(Top Byte Ignore) feature and allows software to access a 4-bit
allocation tag for each 16-byte granule in the physical address space.
A logical tag is derived from bits 59-56 of the virtual
address used for the memory access. A CPU with MTE enabled will compare
the logical tag against the allocation tag and potentially raise an
tag check fault on mismatch, subject to system registers configuration.

Since ARMv8.9, FEAT_MTE_STORE_ONLY can be used to restrict raise of tag
check fault on store operation only.

Using this feature (FEAT_MTE_STORE_ONLY), introduce KASAN write-only mode
which restricts KASAN check write (store) operation only.
This mode omits KASAN check for read (fetch/load) operation.
Therefore, it might be used not only debugging purpose but also in
normal environment.

This patch is based on v6.17-rc1.

Patch History
=============
from v2 to v3:
- change MET_STORE_ONLY feature as BOOT_CPU_FEATURE
- change store_only to write_only
- move write_only setup into the place other option's setup place
- change static key of kasan_flag_write_only to static boolean.
- change macro KUNIT_EXPECT_KASAN_SUCCESS to KUNIT_EXPECT_KASAN_FAIL_READ.
- https://lore.kernel.org/all/20250813175335.398...@arm.com/

from v1 to v2:
- change cryptic name -- stonly to store_only
- remove some TCF check with store which can make memory courruption.
- https://lore.kernel.org/all/20250811173626.187...@arm.com/

Yeoreum Yun (2):
kasan/hw-tags: introduce kasan.write_only option
kasan: apply write-only mode in kasan kunit testcases

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 | 2 +-
arch/arm64/kernel/mte.c | 18 +++
mm/kasan/hw_tags.c | 54 ++++++-
mm/kasan/kasan.h | 7 +
mm/kasan/kasan_test_c.c | 237 ++++++++++++++++++++---------
8 files changed, 250 insertions(+), 78 deletions(-)


base-commit: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}

Yeoreum Yun

unread,
Aug 16, 2025, 7:00:30 AMAug 16
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Since Armv8.9, FEATURE_MTE_STORE_ONLY feature is introduced to restrict
raise of tag check fault on store operation only.
Introcude KASAN write only mode based on this feature.

KASAN write only mode restricts KASAN checks operation for write only and
omits the checks for fetch/read operations 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.write_only" arguments.
When "kasan.write_only=on", KASAN checks write operation only otherwise
KASAN checks all operations.

This changes the MTE_STORE_ONLY feature as BOOT_CPU_FEATURE like
ARM64_MTE_ASYMM so that makes it initialise in kasan_init_hw_tags()
with other function together.

Signed-off-by: Yeoreum Yun <yeore...@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 | 2 +-
arch/arm64/kernel/mte.c | 18 ++++++++++
mm/kasan/hw_tags.c | 54 ++++++++++++++++++++++++++++--
mm/kasan/kasan.h | 7 ++++
7 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
index 0a1418ab72fd..fe1a1e152275 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.write_only=off`` or ``kasan.write_only=on`` controls whether KASAN
+ checks the write (store) 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..f1505c4acb38 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_write_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..0f9b08e8fb8d 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_kernel_store_only(void)
+{
+ return -EINVAL;
+}
+
#endif /* CONFIG_ARM64_MTE */

#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9ad065f15f1d..505bd56e21a2 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2920,7 +2920,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
{
.desc = "Store Only MTE Tag Check",
.capability = ARM64_MTE_STORE_ONLY,
- .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .type = ARM64_CPUCAP_BOOT_CPU_FEATURE,
.matches = has_cpuid_feature,
ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, MTESTOREONLY, IMP)
},
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index e5e773844889..cd5452eb7486 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -157,6 +157,24 @@ void mte_enable_kernel_asymm(void)
mte_enable_kernel_sync();
}
}
+
+int mte_enable_kernel_store_only(void)
+{
+ /*
+ * If the CPU does not support MTE store only,
+ * the kernel checks all operations.
+ */
+ 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/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index 9a6927394b54..e745187f420a 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_write_only {
+ KASAN_ARG_WRITE_ONLY_DEFAULT,
+ KASAN_ARG_WRITE_ONLY_OFF,
+ KASAN_ARG_WRITE_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_write_only kasan_arg_write_only __ro_after_init;

/*
* Whether KASAN is enabled at all.
@@ -67,6 +74,8 @@ DEFINE_STATIC_KEY_FALSE(kasan_flag_vmalloc);
#endif
EXPORT_SYMBOL_GPL(kasan_flag_vmalloc);

+static bool kasan_flag_write_only;
+
#define PAGE_ALLOC_SAMPLE_DEFAULT 1
#define PAGE_ALLOC_SAMPLE_ORDER_DEFAULT 3

@@ -141,6 +150,23 @@ static int __init early_kasan_flag_vmalloc(char *arg)
}
early_param("kasan.vmalloc", early_kasan_flag_vmalloc);

+/* kasan.write_only=off/on */
+static int __init early_kasan_flag_write_only(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (!strcmp(arg, "off"))
+ kasan_arg_write_only = KASAN_ARG_WRITE_ONLY_OFF;
+ else if (!strcmp(arg, "on"))
+ kasan_arg_write_only = KASAN_ARG_WRITE_ONLY_ON;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+early_param("kasan.write_only", early_kasan_flag_write_only);
+
static inline const char *kasan_mode_info(void)
{
if (kasan_mode == KASAN_MODE_ASYNC)
@@ -257,15 +283,26 @@ void __init kasan_init_hw_tags(void)
break;
}

+ switch (kasan_arg_write_only) {
+ case KASAN_ARG_WRITE_ONLY_DEFAULT:
+ case KASAN_ARG_WRITE_ONLY_OFF:
+ kasan_flag_write_only = false;
+ break;
+ case KASAN_ARG_WRITE_ONLY_ON:
+ kasan_flag_write_only = true;
+ break;
+ }
+
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, write_only=%s\n",
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_arg_write_only));
}

#ifdef CONFIG_KASAN_VMALLOC
@@ -392,6 +429,13 @@ void kasan_enable_hw_tags(void)
hw_enable_tag_checks_asymm();
else
hw_enable_tag_checks_sync();
+
+ if (kasan_arg_mode == KASAN_ARG_WRITE_ONLY_ON &&
+ hw_enable_tag_checks_write_only()) {
+ kasan_arg_mode == KASAN_ARG_WRITE_ONLY_OFF;
+ kasan_flag_write_only = false;
+ pr_warn_once("System doesn't support write-only option. Disable it\n");
+ }
}

#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
@@ -404,4 +448,10 @@ VISIBLE_IF_KUNIT void kasan_force_async_fault(void)
}
EXPORT_SYMBOL_IF_KUNIT(kasan_force_async_fault);

+VISIBLE_IF_KUNIT bool kasan_write_only_enabled(void)
+{
+ return kasan_flag_write_only;
+}
+EXPORT_SYMBOL_IF_KUNIT(kasan_write_only_enabled);
+
#endif
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 129178be5e64..c1490136c96b 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -428,6 +428,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_write_only() arch_enable_tag_checks_write_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,11 +438,17 @@ 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);
+bool kasan_write_only_enabled(void);

#else /* CONFIG_KASAN_HW_TAGS */

static inline void kasan_enable_hw_tags(void) { }

+static inline bool kasan_write_only_enabled(void)
+{
+ return false;
+}
+
#endif /* CONFIG_KASAN_HW_TAGS */

#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}

Yeoreum Yun

unread,
Aug 16, 2025, 7:00:35 AMAug 16
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
When KASAN is configured in write-only mode,
fetch/load operations do not trigger tag check faults.

As a result, the outcome of some test cases may differ
compared to when KASAN is configured without write-only mode.

Therefore, by modifying pre-exist testcases
check the write only makes tag check fault (TCF) where
writing is perform in "allocated memory" but tag is invalid
(i.e) redzone write in atomic_set() testcases.
Otherwise check the invalid fetch/read doesn't generate TCF.

Also, skip some testcases affected by initial value
(i.e) atomic_cmpxchg() testcase maybe successd if
it passes valid atomic_t address and invalid oldaval address.
In this case, if invalid atomic_t doesn't have the same oldval,
it won't trigger write operation so the test will pass.

Signed-off-by: Yeoreum Yun <yeore...@arm.com>
---
mm/kasan/kasan_test_c.c | 237 +++++++++++++++++++++++++++-------------
1 file changed, 162 insertions(+), 75 deletions(-)

diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c
index 2aa12dfa427a..8cf93715fdac 100644
--- a/mm/kasan/kasan_test_c.c
+++ b/mm/kasan/kasan_test_c.c
@@ -94,11 +94,13 @@ static void kasan_test_exit(struct kunit *test)
}

/**
- * KUNIT_EXPECT_KASAN_FAIL - check that the executed expression produces a
- * KASAN report; causes a KUnit test failure otherwise.
+ * _KUNIT_EXPECT_KASAN_TEMPLATE - check that the executed expression produces
+ * a KASAN report or not; a KUnit test failure when it's different from @produce.
*
* @test: Currently executing KUnit test.
- * @expression: Expression that must produce a KASAN report.
+ * @expr: Expression produce a KASAN report or not.
+ * @expr_str: Expression string
+ * @produce: expression should produce a KASAN report.
*
* For hardware tag-based KASAN, when a synchronous tag fault happens, tag
* checking is auto-disabled. When this happens, this test handler reenables
@@ -110,25 +112,29 @@ static void kasan_test_exit(struct kunit *test)
* Use READ/WRITE_ONCE() for the accesses and compiler barriers around the
* expression to prevent that.
*
- * In between KUNIT_EXPECT_KASAN_FAIL checks, test_status.report_found is kept
+ * In between _KUNIT_EXPECT_KASAN_TEMPLATE checks, test_status.report_found is kept
* as false. This allows detecting KASAN reports that happen outside of the
* checks by asserting !test_status.report_found at the start of
- * KUNIT_EXPECT_KASAN_FAIL and in kasan_test_exit.
+ * _KUNIT_EXPECT_KASAN_TEMPLATE and in kasan_test_exit.
*/
-#define KUNIT_EXPECT_KASAN_FAIL(test, expression) do { \
+#define _KUNIT_EXPECT_KASAN_TEMPLATE(test, expr, expr_str, produce) \
+do { \
if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) && \
kasan_sync_fault_possible()) \
migrate_disable(); \
KUNIT_EXPECT_FALSE(test, READ_ONCE(test_status.report_found)); \
barrier(); \
- expression; \
+ expr; \
barrier(); \
if (kasan_async_fault_possible()) \
kasan_force_async_fault(); \
- if (!READ_ONCE(test_status.report_found)) { \
- KUNIT_FAIL(test, KUNIT_SUBTEST_INDENT "KASAN failure " \
- "expected in \"" #expression \
- "\", but none occurred"); \
+ if (READ_ONCE(test_status.report_found) != produce) { \
+ KUNIT_FAIL(test, KUNIT_SUBTEST_INDENT "KASAN %s " \
+ "expected in \"" expr_str \
+ "\", but %soccurred", \
+ (produce ? "failure" : "success"), \
+ (test_status.report_found ? \
+ "" : "none ")); \
} \
if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) && \
kasan_sync_fault_possible()) { \
@@ -141,6 +147,29 @@ static void kasan_test_exit(struct kunit *test)
WRITE_ONCE(test_status.async_fault, false); \
} while (0)

+/*
+ * KUNIT_EXPECT_KASAN_FAIL - check that the executed expression produces a
+ * KASAN report; causes a KUnit test failure otherwise.
+ *
+ * @test: Currently executing KUnit test.
+ * @expr: Expression produce a KASAN report.
+ */
+#define KUNIT_EXPECT_KASAN_FAIL(test, expr) \
+ _KUNIT_EXPECT_KASAN_TEMPLATE(test, expr, #expr, true)
+
+/*
+ * KUNIT_EXPECT_KASAN_FAIL_READ - check that the executed expression produces
+ * a KASAN report for read access.
+ * It causes a KUnit test failure. if KASAN report isn't produced for read access.
+ * For write access, it cause a KUnit test failure if a KASAN report is produced
+ *
+ * @test: Currently executing KUnit test.
+ * @expr: Expression doesn't produce a KASAN report.
+ */
+#define KUNIT_EXPECT_KASAN_FAIL_READ(test, expr) \
+ _KUNIT_EXPECT_KASAN_TEMPLATE(test, expr, #expr, \
+ !kasan_write_only_enabled()) \
+
#define KASAN_TEST_NEEDS_CONFIG_ON(test, config) do { \
if (!IS_ENABLED(config)) \
kunit_skip((test), "Test requires " #config "=y"); \
@@ -183,8 +212,8 @@ static void kmalloc_oob_right(struct kunit *test)
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size + 5] = 'y');

/* Out-of-bounds access past the aligned kmalloc object. */
- KUNIT_EXPECT_KASAN_FAIL(test, ptr[0] =
- ptr[size + KASAN_GRANULE_SIZE + 5]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ptr[0] =
+ ptr[size + KASAN_GRANULE_SIZE + 5]);

kfree(ptr);
}
@@ -198,7 +227,8 @@ static void kmalloc_oob_left(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);

OPTIMIZER_HIDE_VAR(ptr);
- KUNIT_EXPECT_KASAN_FAIL(test, *ptr = *(ptr - 1));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, *ptr = *(ptr - 1));
+
kfree(ptr);
}

@@ -211,7 +241,8 @@ static void kmalloc_node_oob_right(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);

OPTIMIZER_HIDE_VAR(ptr);
- KUNIT_EXPECT_KASAN_FAIL(test, ptr[0] = ptr[size]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ptr[0] = ptr[size]);
+
kfree(ptr);
}

@@ -291,7 +322,7 @@ static void kmalloc_large_uaf(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
kfree(ptr);

- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[0]);
}

static void kmalloc_large_invalid_free(struct kunit *test)
@@ -323,7 +354,8 @@ static void page_alloc_oob_right(struct kunit *test)
ptr = page_address(pages);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);

- KUNIT_EXPECT_KASAN_FAIL(test, ptr[0] = ptr[size]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ptr[0] = ptr[size]);
+
free_pages((unsigned long)ptr, order);
}

@@ -338,7 +370,7 @@ static void page_alloc_uaf(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
free_pages((unsigned long)ptr, order);

- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[0]);
}

static void krealloc_more_oob_helper(struct kunit *test,
@@ -455,10 +487,10 @@ static void krealloc_uaf(struct kunit *test)
ptr1 = kmalloc(size1, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr1);
kfree(ptr1);
-
KUNIT_EXPECT_KASAN_FAIL(test, ptr2 = krealloc(ptr1, size2, GFP_KERNEL));
KUNIT_ASSERT_NULL(test, ptr2);
- KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)ptr1);
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, *(volatile char *)ptr1);
}

static void kmalloc_oob_16(struct kunit *test)
@@ -501,7 +533,8 @@ static void kmalloc_uaf_16(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr2);
kfree(ptr2);

- KUNIT_EXPECT_KASAN_FAIL(test, *ptr1 = *ptr2);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, *ptr1 = *ptr2);
+
kfree(ptr1);
}

@@ -640,8 +673,10 @@ static void kmalloc_memmove_invalid_size(struct kunit *test)
memset((char *)ptr, 0, 64);
OPTIMIZER_HIDE_VAR(ptr);
OPTIMIZER_HIDE_VAR(invalid_size);
- KUNIT_EXPECT_KASAN_FAIL(test,
- memmove((char *)ptr, (char *)ptr + 4, invalid_size));
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
+ memmove((char *)ptr, (char *)ptr + 4, invalid_size));
+
kfree(ptr);
}

@@ -654,7 +689,8 @@ static void kmalloc_uaf(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);

kfree(ptr);
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[8]);
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[8]);
}

static void kmalloc_uaf_memset(struct kunit *test)
@@ -701,7 +737,8 @@ static void kmalloc_uaf2(struct kunit *test)
goto again;
}

- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr1)[40]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr1)[40]);
+
KUNIT_EXPECT_PTR_NE(test, ptr1, ptr2);

kfree(ptr2);
@@ -727,19 +764,19 @@ static void kmalloc_uaf3(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr2);
kfree(ptr2);

- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr1)[8]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr1)[8]);
}

static void kasan_atomics_helper(struct kunit *test, void *unsafe, void *safe)
{
int *i_unsafe = unsafe;

- KUNIT_EXPECT_KASAN_FAIL(test, READ_ONCE(*i_unsafe));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, READ_ONCE(*i_unsafe));
+
KUNIT_EXPECT_KASAN_FAIL(test, WRITE_ONCE(*i_unsafe, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, smp_load_acquire(i_unsafe));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, smp_load_acquire(i_unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, smp_store_release(i_unsafe, 42));
-
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_read(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, atomic_read(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_set(unsafe, 42));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_add(42, unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_sub(42, unsafe));
@@ -752,18 +789,35 @@ static void kasan_atomics_helper(struct kunit *test, void *unsafe, void *safe)
KUNIT_EXPECT_KASAN_FAIL(test, atomic_xchg(unsafe, 42));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_cmpxchg(unsafe, 21, 42));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(unsafe, safe, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(safe, unsafe, 42));
+
+ /*
+ * The result of the test below may vary due to garbage values of unsafe in
+ * store-only mode. Therefore, skip this test when KASAN is configured
+ * in store-only mode.
+ */
+ if (!kasan_write_only_enabled())
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(safe, unsafe, 42));
+
KUNIT_EXPECT_KASAN_FAIL(test, atomic_sub_and_test(42, unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_add_negative(42, unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_add_unless(unsafe, 21, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_not_zero(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_unless_negative(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_unless_positive(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_if_positive(unsafe));

- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_read(unsafe));
+ /*
+ * The result of the test below may vary due to garbage values of unsafe in
+ * store-only mode. Therefore, skip this test when KASAN is configured
+ * in store-only mode.
+ */
+ if (!kasan_write_only_enabled()) {
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_add_unless(unsafe, 21, 42));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_not_zero(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_unless_negative(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_unless_positive(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_if_positive(unsafe));
+ }
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, atomic_long_read(unsafe));
+
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_set(unsafe, 42));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add(42, unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_sub(42, unsafe));
@@ -776,16 +830,32 @@ static void kasan_atomics_helper(struct kunit *test, void *unsafe, void *safe)
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_xchg(unsafe, 42));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_cmpxchg(unsafe, 21, 42));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(unsafe, safe, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(safe, unsafe, 42));
+
+ /*
+ * The result of the test below may vary due to garbage values in
+ * store-only mode. Therefore, skip this test when KASAN is configured
+ * in store-only mode.
+ */
+ if (!kasan_write_only_enabled())
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(safe, unsafe, 42));
+
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_sub_and_test(42, unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_negative(42, unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_unless(unsafe, 21, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_not_zero(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_unless_negative(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_unless_positive(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_if_positive(unsafe));
+
+ /*
+ * The result of the test below may vary due to garbage values in
+ * store-only mode. Therefore, skip this test when KASAN is configured
+ * in store-only mode.
+ */
+ if (!kasan_write_only_enabled()) {
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_unless(unsafe, 21, 42));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_not_zero(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_unless_negative(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_unless_positive(unsafe));
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_if_positive(unsafe));
+ }
}

static void kasan_atomics(struct kunit *test)
@@ -842,8 +912,9 @@ static void ksize_unpoisons_memory(struct kunit *test)
/* These must trigger a KASAN report. */
if (IS_ENABLED(CONFIG_KASAN_GENERIC))
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size + 5]);
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[real_size - 1]);
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[size + 5]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[real_size - 1]);

kfree(ptr);
}
@@ -863,8 +934,8 @@ static void ksize_uaf(struct kunit *test)

OPTIMIZER_HIDE_VAR(ptr);
KUNIT_EXPECT_KASAN_FAIL(test, ksize(ptr));
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[0]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[size]);
}

/*
@@ -886,6 +957,7 @@ static void rcu_uaf_reclaim(struct rcu_head *rp)
container_of(rp, struct kasan_rcu_info, rcu);

kfree(fp);
+
((volatile struct kasan_rcu_info *)fp)->i;
}

@@ -899,9 +971,9 @@ static void rcu_uaf(struct kunit *test)
global_rcu_ptr = rcu_dereference_protected(
(struct kasan_rcu_info __rcu *)ptr, NULL);

- KUNIT_EXPECT_KASAN_FAIL(test,
- call_rcu(&global_rcu_ptr->rcu, rcu_uaf_reclaim);
- rcu_barrier());
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
+ call_rcu(&global_rcu_ptr->rcu, rcu_uaf_reclaim);
+ rcu_barrier());
}

static void workqueue_uaf_work(struct work_struct *work)
@@ -924,8 +996,8 @@ static void workqueue_uaf(struct kunit *test)
queue_work(workqueue, work);
destroy_workqueue(workqueue);

- KUNIT_EXPECT_KASAN_FAIL(test,
- ((volatile struct work_struct *)work)->data);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
+ ((volatile struct work_struct *)work)->data);
}

static void kfree_via_page(struct kunit *test)
@@ -972,7 +1044,7 @@ static void kmem_cache_oob(struct kunit *test)
return;
}

- KUNIT_EXPECT_KASAN_FAIL(test, *p = p[size + OOB_TAG_OFF]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, *p = p[size + OOB_TAG_OFF]);

kmem_cache_free(cache, p);
kmem_cache_destroy(cache);
@@ -1068,7 +1140,7 @@ static void kmem_cache_rcu_uaf(struct kunit *test)
*/
rcu_barrier();

- KUNIT_EXPECT_KASAN_FAIL(test, READ_ONCE(*p));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, READ_ONCE(*p));

kmem_cache_destroy(cache);
}
@@ -1207,7 +1279,7 @@ static void mempool_oob_right_helper(struct kunit *test, mempool_t *pool, size_t
KUNIT_EXPECT_KASAN_FAIL(test,
((volatile char *)&elem[size])[0]);
else
- KUNIT_EXPECT_KASAN_FAIL(test,
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
((volatile char *)&elem[round_up(size, KASAN_GRANULE_SIZE)])[0]);

mempool_free(elem, pool);
@@ -1273,7 +1345,8 @@ static void mempool_uaf_helper(struct kunit *test, mempool_t *pool, bool page)
mempool_free(elem, pool);

ptr = page ? page_address((struct page *)elem) : elem;
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[0]);
}

static void mempool_kmalloc_uaf(struct kunit *test)
@@ -1532,7 +1605,8 @@ static void kasan_memchr(struct kunit *test)

OPTIMIZER_HIDE_VAR(ptr);
OPTIMIZER_HIDE_VAR(size);
- KUNIT_EXPECT_KASAN_FAIL(test,
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
kasan_ptr_result = memchr(ptr, '1', size + 1));

kfree(ptr);
@@ -1559,8 +1633,10 @@ static void kasan_memcmp(struct kunit *test)

OPTIMIZER_HIDE_VAR(ptr);
OPTIMIZER_HIDE_VAR(size);
- KUNIT_EXPECT_KASAN_FAIL(test,
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
kasan_int_result = memcmp(ptr, arr, size+1));
+
kfree(ptr);
}

@@ -1594,7 +1670,7 @@ static void kasan_strings(struct kunit *test)
strscpy(ptr, src + 1, KASAN_GRANULE_SIZE));

/* strscpy should fail if the first byte is unreadable. */
- KUNIT_EXPECT_KASAN_FAIL(test, strscpy(ptr, src + KASAN_GRANULE_SIZE,
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, strscpy(ptr, src + KASAN_GRANULE_SIZE,
KASAN_GRANULE_SIZE));

kfree(src);
@@ -1607,17 +1683,13 @@ static void kasan_strings(struct kunit *test)
* will likely point to zeroed byte.
*/
ptr += 16;
- KUNIT_EXPECT_KASAN_FAIL(test, kasan_ptr_result = strchr(ptr, '1'));

- KUNIT_EXPECT_KASAN_FAIL(test, kasan_ptr_result = strrchr(ptr, '1'));
-
- KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result = strcmp(ptr, "2"));
-
- KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result = strncmp(ptr, "2", 1));
-
- KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result = strlen(ptr));
-
- KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result = strnlen(ptr, 1));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_ptr_result = strchr(ptr, '1'));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_ptr_result = strrchr(ptr, '1'));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_int_result = strcmp(ptr, "2"));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_int_result = strncmp(ptr, "2", 1));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_int_result = strlen(ptr));
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_int_result = strnlen(ptr, 1));
}

static void kasan_bitops_modify(struct kunit *test, int nr, void *addr)
@@ -1636,12 +1708,22 @@ static void kasan_bitops_test_and_modify(struct kunit *test, int nr, void *addr)
{
KUNIT_EXPECT_KASAN_FAIL(test, test_and_set_bit(nr, addr));
KUNIT_EXPECT_KASAN_FAIL(test, __test_and_set_bit(nr, addr));
- KUNIT_EXPECT_KASAN_FAIL(test, test_and_set_bit_lock(nr, addr));
+
+ /*
+ * When KASAN is running in store-only mode,
+ * a fault won't occur when the bit is set.
+ * Therefore, skip the test_and_set_bit_lock test in store-only mode.
+ */
+ if (!kasan_write_only_enabled())
+ KUNIT_EXPECT_KASAN_FAIL(test, test_and_set_bit_lock(nr, addr));
+
KUNIT_EXPECT_KASAN_FAIL(test, test_and_clear_bit(nr, addr));
KUNIT_EXPECT_KASAN_FAIL(test, __test_and_clear_bit(nr, addr));
KUNIT_EXPECT_KASAN_FAIL(test, test_and_change_bit(nr, addr));
KUNIT_EXPECT_KASAN_FAIL(test, __test_and_change_bit(nr, addr));
- KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result = test_bit(nr, addr));
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, kasan_int_result = test_bit(nr, addr));
+
if (nr < 7)
KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result =
xor_unlock_is_negative_byte(1 << nr, addr));
@@ -1765,7 +1847,7 @@ static void vmalloc_oob(struct kunit *test)
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)v_ptr)[size]);

/* An aligned access into the first out-of-bounds granule. */
- KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)v_ptr)[size + 5]);
+ KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)v_ptr)[size + 5]);

/* Check that in-bounds accesses to the physical page are valid. */
page = vmalloc_to_page(v_ptr);
@@ -2042,15 +2124,20 @@ static void copy_user_test_oob(struct kunit *test)

KUNIT_EXPECT_KASAN_FAIL(test,
unused = copy_from_user(kmem, usermem, size + 1));
- KUNIT_EXPECT_KASAN_FAIL(test,
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
unused = copy_to_user(usermem, kmem, size + 1));
+
KUNIT_EXPECT_KASAN_FAIL(test,
unused = __copy_from_user(kmem, usermem, size + 1));
- KUNIT_EXPECT_KASAN_FAIL(test,
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
unused = __copy_to_user(usermem, kmem, size + 1));
+
KUNIT_EXPECT_KASAN_FAIL(test,
unused = __copy_from_user_inatomic(kmem, usermem, size + 1));
- KUNIT_EXPECT_KASAN_FAIL(test,
+
+ KUNIT_EXPECT_KASAN_FAIL_READ(test,
unused = __copy_to_user_inatomic(usermem, kmem, size + 1));

/*
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}

kernel test robot

unread,
Aug 17, 2025, 8:14:39 PMAug 17
to Yeoreum Yun, ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, oe-kbu...@lists.linux.dev, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Hi Yeoreum,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 8f5ae30d69d7543eee0d70083daf4de8fe15d585]

url: https://github.com/intel-lab-lkp/linux/commits/Yeoreum-Yun/kasan-hw-tags-introduce-kasan-write_only-option/20250816-190300
base: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
patch link: https://lore.kernel.org/r/20250816110018.4055617-2-yeoreum.yun%40arm.com
patch subject: [PATCH v3 1/2] kasan/hw-tags: introduce kasan.write_only option
config: arm64-randconfig-r053-20250818 (https://download.01.org/0day-ci/archive/20250818/202508180747...@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250818/202508180747...@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <l...@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508180747...@intel.com/

All warnings (new ones prefixed by >>):

mm/kasan/hw_tags.c: In function 'kasan_enable_hw_tags':
>> mm/kasan/hw_tags.c:433:21: warning: comparison between 'enum kasan_arg_mode' and 'enum kasan_arg_write_only' [-Wenum-compare]
433 | if (kasan_arg_mode == KASAN_ARG_WRITE_ONLY_ON &&
| ^~
mm/kasan/hw_tags.c:435:18: warning: comparison between 'enum kasan_arg_mode' and 'enum kasan_arg_write_only' [-Wenum-compare]
435 | kasan_arg_mode == KASAN_ARG_WRITE_ONLY_OFF;
| ^~
>> mm/kasan/hw_tags.c:435:18: warning: statement with no effect [-Wunused-value]
435 | kasan_arg_mode == KASAN_ARG_WRITE_ONLY_OFF;
| ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~


vim +433 mm/kasan/hw_tags.c

423
424 void kasan_enable_hw_tags(void)
425 {
426 if (kasan_arg_mode == KASAN_ARG_MODE_ASYNC)
427 hw_enable_tag_checks_async();
428 else if (kasan_arg_mode == KASAN_ARG_MODE_ASYMM)
429 hw_enable_tag_checks_asymm();
430 else
431 hw_enable_tag_checks_sync();
432
> 433 if (kasan_arg_mode == KASAN_ARG_WRITE_ONLY_ON &&
434 hw_enable_tag_checks_write_only()) {
> 435 kasan_arg_mode == KASAN_ARG_WRITE_ONLY_OFF;
436 kasan_flag_write_only = false;
437 pr_warn_once("System doesn't support write-only option. Disable it\n");
438 }
439 }
440

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

Yeoreum Yun

unread,
Aug 18, 2025, 3:19:38 AMAug 18
to kernel test robot, ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, oe-kbu...@lists.linux.dev, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi.
Grr. Sorry I think I've sent the wrong version.
I'll fix it.

Sorry to make a noise.

--
Sincerely,
Yeoreum Yun

Yeoreum Yun

unread,
Aug 18, 2025, 3:51:00 AMAug 18
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Hardware tag based KASAN is implemented using the Memory Tagging Extension
(MTE) feature.

MTE is built on top of the ARMv8.0 virtual address tagging TBI
(Top Byte Ignore) feature and allows software to access a 4-bit
allocation tag for each 16-byte granule in the physical address space.
A logical tag is derived from bits 59-56 of the virtual
address used for the memory access. A CPU with MTE enabled will compare
the logical tag against the allocation tag and potentially raise an
tag check fault on mismatch, subject to system registers configuration.

Since ARMv8.9, FEAT_MTE_STORE_ONLY can be used to restrict raise of tag
check fault on store operation only.

Using this feature (FEAT_MTE_STORE_ONLY), introduce KASAN write-only mode
which restricts KASAN check write (store) operation only.
This mode omits KASAN check for read (fetch/load) operation.
Therefore, it might be used not only debugging purpose but also in
normal environment.

This patch is based on v6.17-rc1.

Patch History
=============
from v3 to v4:
- fix wrong condition
- https://lore.kernel.org/all/20250816110018.405...@arm.com/

from v2 to v3:
- change MET_STORE_ONLY feature as BOOT_CPU_FEATURE
- change store_only to write_only
- move write_only setup into the place other option's setup place
- change static key of kasan_flag_write_only to static boolean.
- change macro KUNIT_EXPECT_KASAN_SUCCESS to KUNIT_EXPECT_KASAN_FAIL_READ.
- https://lore.kernel.org/all/20250813175335.398...@arm.com/

from v1 to v2:
- change cryptic name -- stonly to store_only
- remove some TCF check with store which can make memory courruption.
- https://lore.kernel.org/all/20250811173626.187...@arm.com/


Yeoreum Yun (2):
kasan/hw-tags: introduce kasan.write_only option
kasan: apply write-only mode in kasan kunit testcases

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 | 2 +-

Yeoreum Yun

unread,
Aug 18, 2025, 3:51:05 AMAug 18
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Since Armv8.9, FEATURE_MTE_STORE_ONLY feature is introduced to restrict
raise of tag check fault on store operation only.
Introcude KASAN write only mode based on this feature.

KASAN write only mode restricts KASAN checks operation for write only and
omits the checks for fetch/read operations 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.write_only" arguments.
When "kasan.write_only=on", KASAN checks write operation only otherwise
KASAN checks all operations.

This changes the MTE_STORE_ONLY feature as BOOT_CPU_FEATURE like
ARM64_MTE_ASYMM so that makes it initialise in kasan_init_hw_tags()
with other function together.

Signed-off-by: Yeoreum Yun <yeore...@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 | 2 +-
+{
+ /*
+ * If the CPU does not support MTE store only,
+ * the kernel checks all operations.
+ */
+ 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/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index 9a6927394b54..df67b48739b4 100644
+ if (kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_ON &&
+ hw_enable_tag_checks_write_only()) {
+ kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_OFF;

Yeoreum Yun

unread,
Aug 18, 2025, 3:51:08 AMAug 18
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
When KASAN is configured in write-only mode,
fetch/load operations do not trigger tag check faults.

As a result, the outcome of some test cases may differ
compared to when KASAN is configured without write-only mode.

Therefore, by modifying pre-exist testcases
check the write only makes tag check fault (TCF) where
writing is perform in "allocated memory" but tag is invalid
(i.e) redzone write in atomic_set() testcases.
Otherwise check the invalid fetch/read doesn't generate TCF.

Also, skip some testcases affected by initial value
(i.e) atomic_cmpxchg() testcase maybe successd if
it passes valid atomic_t address and invalid oldaval address.
In this case, if invalid atomic_t doesn't have the same oldval,
it won't trigger write operation so the test will pass.

Signed-off-by: Yeoreum Yun <yeore...@arm.com>
---
mm/kasan/kasan_test_c.c | 237 +++++++++++++++++++++++++++-------------
1 file changed, 162 insertions(+), 75 deletions(-)

diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c
index e0968acc03aa..cc0730aa18d1 100644
+
+ /*
+
+ /*
+ * The result of the test below may vary due to garbage values in
+ * store-only mode. Therefore, skip this test when KASAN is configured
+ * in store-only mode.
+ */
+ if (!kasan_write_only_enabled())
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(safe, unsafe, 42));
+
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_sub_and_test(42, unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_negative(42, unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_unless(unsafe, 21, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_not_zero(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_unless_negative(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_unless_positive(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_if_positive(unsafe));
+
+ /*
+
+ /*

Ben Horgan

unread,
Aug 18, 2025, 5:52:51 AMAug 18
to Yeoreum Yun, ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi Yeoreum,
Would it be better to make this function return void and add a static
key in the manner of mte_async_or_asymm_mode, perhaps
mte_store_only_mode? This information could then be used to help
determine whether it is required to enable and disable tco in
__get_kernel_nofault() and load_unaligned_zeropad(). The function
signature would also match that of the other hw_enable_tag_...().

> +
> + 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");
nit: stonly can be expanded to store only
Thanks,

Ben

Andrey Konovalov

unread,
Aug 18, 2025, 5:53:42 AMAug 18
to Yeoreum Yun, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Let's keep this part similar to the other parameters for consistency:

/* Default is specified by kasan_flag_write_only definition. */
break;
We already set kasan_flag_write_only by this point, right? Let's check
that one then.

> + hw_enable_tag_checks_write_only()) {
> + kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_OFF;

Typo in == in the line above. But also I think we can just drop the
line: kasan_arg_write_only is KASAN_ARG_WRITE_ONLY_ON after all, it's
just not supported and thus kasan_flag_write_only is set to false to
reflect that.

> + kasan_flag_write_only = false;
> + pr_warn_once("System doesn't support write-only option. Disable it\n");

Let's do pr_err like the rest of KASAN code. And:

pr_err_once("write-only mode is not supported and thus not enabled\n");
This should go next to kasan_force_async_fault().

>
> #else /* CONFIG_KASAN_HW_TAGS */
>
> static inline void kasan_enable_hw_tags(void) { }
>
> +static inline bool kasan_write_only_enabled(void)
> +{
> + return false;
> +}

And this too.

Yeoreum Yun

unread,
Aug 18, 2025, 9:11:55 AMAug 18
to Andrey Konovalov, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi Andrey,

[...]

> > static inline const char *kasan_mode_info(void)
> > {
> > if (kasan_mode == KASAN_MODE_ASYNC)
> > @@ -257,15 +283,26 @@ void __init kasan_init_hw_tags(void)
> > break;
> > }
> >
> > + switch (kasan_arg_write_only) {
> > + case KASAN_ARG_WRITE_ONLY_DEFAULT:
>
> Let's keep this part similar to the other parameters for consistency:
>
> /* Default is specified by kasan_flag_write_only definition. */
> break;

Okay.
Yes. if user specifies the write_only == on.
>
> > + hw_enable_tag_checks_write_only()) {
> > + kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_OFF;
>
> Typo in == in the line above. But also I think we can just drop the
> line: kasan_arg_write_only is KASAN_ARG_WRITE_ONLY_ON after all, it's
> just not supported and thus kasan_flag_write_only is set to false to
> reflect that.

Sorry :\ I've missed this fix from patch 3... this should be == to =.

However, we couldn't remove kasan_arg_write_only check in condition.
If one of cpu get failed to hw_enable_tag_checks_write_only() then
By changing this with KASAN_ARG_WRITE_ONLY_OFF, It prevent to call
hw_eanble_tag_checks_write_only() in other cpu.

As you said, kasan_flag_write_only reflects the state.
But like other option, I keep the condition to call the hw_enable_xxx()
by checking the "argments" and keep the "hw enable state" with
kasan_flag_write_only.

so let me change == to = only in here.

>
> > + kasan_flag_write_only = false;
> > + pr_warn_once("System doesn't support write-only option. Disable it\n");
>
> Let's do pr_err like the rest of KASAN code. And:
>
> pr_err_once("write-only mode is not supported and thus not enabled\n");

Okay. Thanks for suggestion.
Right. I'll move them. Thanks!

Thanks!

--
Sincerely,
Yeoreum Yun

Yeoreum Yun

unread,
Aug 18, 2025, 9:54:51 AMAug 18
to Ben Horgan, ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi Ben,

[..]
This is the same point Catalin points out from patch v2.
But for usage of kunit test, it need to keep return as int.

> and add a static key in
> the manner of mte_async_or_asymm_mode, perhaps mte_store_only_mode? This
> information could then be used to help determine whether it is required to
> enable and disable tco in __get_kernel_nofault() and
> load_unaligned_zeropad().

Yes. Since the mte_store_only enabled, it doesn't need to enable tco
since load/fetch doesn't increase the TSFR.
However This sounds like an over optimisation.
I think it would be enough to check when mte_async_or_asymm_mode()
otherwise in for __get_kernel_nofault() or load_unaligned_zeropad(),
it should use like:
- __mte_enable_tco_async_and_store_only()
or
- __mte_enable_tco_async(op) // whether op is load or store (boolean or enum)

But this seems ugly too.

So I think it woule be better to remain as it is --
without static_key for store only since there is no usage.

>
> > +
> > + 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");
> nit: stonly can be expanded to store only

Thanks. I'll change it.
--
Sincerely,
Yeoreum Yun

Andrey Konovalov

unread,
Aug 18, 2025, 10:42:16 AMAug 18
to Yeoreum Yun, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
On Mon, Aug 18, 2025 at 3:11 PM Yeoreum Yun <yeore...@arm.com> wrote:
>
> > > + hw_enable_tag_checks_write_only()) {
> > > + kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_OFF;
> >
> > Typo in == in the line above. But also I think we can just drop the
> > line: kasan_arg_write_only is KASAN_ARG_WRITE_ONLY_ON after all, it's
> > just not supported and thus kasan_flag_write_only is set to false to
> > reflect that.
>
> Sorry :\ I've missed this fix from patch 3... this should be == to =.
>
> However, we couldn't remove kasan_arg_write_only check in condition.
> If one of cpu get failed to hw_enable_tag_checks_write_only() then
> By changing this with KASAN_ARG_WRITE_ONLY_OFF, It prevent to call
> hw_eanble_tag_checks_write_only() in other cpu.

Is it possible that the write-only mode will fail to be enabled on one
CPU but then get enabled successfully for another?

What would happen with the current code if the first CPU succeeds in
enabling the write-only mode, and the second one fails?

> As you said, kasan_flag_write_only reflects the state.
> But like other option, I keep the condition to call the hw_enable_xxx()
> by checking the "argments" and keep the "hw enable state" with
> kasan_flag_write_only.

Assuming we keep this behavior, please add a comment explaining all this.

Yeoreum Yun

unread,
Aug 18, 2025, 11:19:26 AMAug 18
to Andrey Konovalov, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi Andery,
> On Mon, Aug 18, 2025 at 3:11 PM Yeoreum Yun <yeore...@arm.com> wrote:
> >
> > > > + hw_enable_tag_checks_write_only()) {
> > > > + kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_OFF;
> > >
> > > Typo in == in the line above. But also I think we can just drop the
> > > line: kasan_arg_write_only is KASAN_ARG_WRITE_ONLY_ON after all, it's
> > > just not supported and thus kasan_flag_write_only is set to false to
> > > reflect that.
> >
> > Sorry :\ I've missed this fix from patch 3... this should be == to =.
> >
> > However, we couldn't remove kasan_arg_write_only check in condition.
> > If one of cpu get failed to hw_enable_tag_checks_write_only() then
> > By changing this with KASAN_ARG_WRITE_ONLY_OFF, It prevent to call
> > hw_eanble_tag_checks_write_only() in other cpu.
>
> Is it possible that the write-only mode will fail to be enabled on one
> CPU but then get enabled successfully for another?
>
> What would happen with the current code if the first CPU succeeds in
> enabling the write-only mode, and the second one fails?

The only failure case is when CPU doesn't support the MTE_STORE_ONLY feature.
Since MTE_STORE_ONLY is BOOT CPU feature, the CPUs have two state --
all CPUs have a MTE_STORE_ONLY feature or they doesn't have it.

So when boot-cpu try to enable the write-only option according to
"write_only=on" boot argument but the cpu doesn't have this feature,
hw_enable_tag_checks_write_only() will fail and prevent the calling
hw_enable_tag_checks_write_only() in other cpu by kasan_arg_write_only
as OFF to prevent other cpu call this function since it'll be failed
anyway.

So there is no case for failure -- the first CPU succeeds but second one
fails. if first one succeeds, all cpus will success ans vice versa.

This condition just to prevent to call
hw_enable_tags_checks_write_only() by other cpu if first cpu found it
doesn't support MTE_STORE_ONLY feature since other doesn't need to call
hw_eanble_tag_checks_write_only() function -- it'll be failed
so it is meaningless call.

>
> > As you said, kasan_flag_write_only reflects the state.
> > But like other option, I keep the condition to call the hw_enable_xxx()
> > by checking the "argments" and keep the "hw enable state" with
> > kasan_flag_write_only.
>
> Assuming we keep this behavior, please add a comment explaining all this.

Okay. :)

--
Sincerely,
Yeoreum Yun

Yeoreum Yun

unread,
Aug 20, 2025, 3:12:51 AMAug 20
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Hardware tag based KASAN is implemented using the Memory Tagging Extension
(MTE) feature.

MTE is built on top of the ARMv8.0 virtual address tagging TBI
(Top Byte Ignore) feature and allows software to access a 4-bit
allocation tag for each 16-byte granule in the physical address space.
A logical tag is derived from bits 59-56 of the virtual
address used for the memory access. A CPU with MTE enabled will compare
the logical tag against the allocation tag and potentially raise an
tag check fault on mismatch, subject to system registers configuration.

Since ARMv8.9, FEAT_MTE_STORE_ONLY can be used to restrict raise of tag
check fault on store operation only.

Using this feature (FEAT_MTE_STORE_ONLY), introduce KASAN write-only mode
which restricts KASAN check write (store) operation only.
This mode omits KASAN check for read (fetch/load) operation.
Therefore, it might be used not only debugging purpose but also in
normal environment.

This patch is based on v6.17-rc1.

Patch History
=============
from v4 to v5:
- fix wrong allocation
- add small comments
- https://lore.kernel.org/all/20250818075051.99...@arm.com/

from v3 to v4:
- fix wrong condition
- https://lore.kernel.org/all/20250816110018.405...@arm.com/

from v2 to v3:
- change MET_STORE_ONLY feature as BOOT_CPU_FEATURE
- change store_only to write_only
- move write_only setup into the place other option's setup place
- change static key of kasan_flag_write_only to static boolean.
- change macro KUNIT_EXPECT_KASAN_SUCCESS to KUNIT_EXPECT_KASAN_FAIL_READ.
- https://lore.kernel.org/all/20250813175335.398...@arm.com/

from v1 to v2:
- change cryptic name -- stonly to store_only
- remove some TCF check with store which can make memory courruption.
- https://lore.kernel.org/all/20250811173626.187...@arm.com/


Yeoreum Yun (2):
kasan/hw-tags: introduce kasan.write_only option
kasan: apply write-only mode in kasan kunit testcases

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 | 2 +-
arch/arm64/kernel/mte.c | 18 +++
mm/kasan/hw_tags.c | 70 ++++++++-
mm/kasan/kasan.h | 7 +
mm/kasan/kasan_test_c.c | 237 ++++++++++++++++++++---------
8 files changed, 266 insertions(+), 78 deletions(-)

Yeoreum Yun

unread,
Aug 20, 2025, 3:12:54 AMAug 20
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
Since Armv8.9, FEATURE_MTE_STORE_ONLY feature is introduced to restrict
raise of tag check fault on store operation only.
Introcude KASAN write only mode based on this feature.

KASAN write only mode restricts KASAN checks operation for write only and
omits the checks for fetch/read operations 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.write_only" arguments.
When "kasan.write_only=on", KASAN checks write operation only otherwise
KASAN checks all operations.

This changes the MTE_STORE_ONLY feature as BOOT_CPU_FEATURE like
ARM64_MTE_ASYMM so that makes it initialise in kasan_init_hw_tags()
with other function together.

Signed-off-by: Yeoreum Yun <yeore...@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 | 2 +-
arch/arm64/kernel/mte.c | 18 ++++++++
mm/kasan/hw_tags.c | 70 +++++++++++++++++++++++++++++-
mm/kasan/kasan.h | 7 +++
7 files changed, 104 insertions(+), 3 deletions(-)
+ return -EINVAL;
+}
+
#endif /* CONFIG_ARM64_MTE */

#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9ad065f15f1d..505bd56e21a2 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2920,7 +2920,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
{
.desc = "Store Only MTE Tag Check",
.capability = ARM64_MTE_STORE_ONLY,
- .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .type = ARM64_CPUCAP_BOOT_CPU_FEATURE,
.matches = has_cpuid_feature,
ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, MTESTOREONLY, IMP)
},
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index e5e773844889..54a52dc5c1ae 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -157,6 +157,24 @@ void mte_enable_kernel_asymm(void)
mte_enable_kernel_sync();
}
}
+
+int mte_enable_kernel_store_only(void)
+{
+ /*
+ * If the CPU does not support MTE store only,
+ * the kernel checks all operations.
+ */
+ 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 store only mode at EL1\n");
+
+ return 0;
+}
#endif

#ifdef CONFIG_KASAN_HW_TAGS
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index 9a6927394b54..334e9e84983e 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_write_only {
+ KASAN_ARG_WRITE_ONLY_DEFAULT,
+ KASAN_ARG_WRITE_ONLY_OFF,
+ KASAN_ARG_WRITE_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_write_only kasan_arg_write_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);

+/* Whether to check write access only. */
+static bool kasan_flag_write_only = false;
+
#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.write_only=off/on */
+static int __init early_kasan_flag_write_only(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (!strcmp(arg, "off"))
+ kasan_arg_write_only = KASAN_ARG_WRITE_ONLY_OFF;
+ else if (!strcmp(arg, "on"))
+ kasan_arg_write_only = KASAN_ARG_WRITE_ONLY_ON;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+early_param("kasan.write_only", early_kasan_flag_write_only);
+
static inline const char *kasan_mode_info(void)
{
if (kasan_mode == KASAN_MODE_ASYNC)
@@ -257,15 +284,28 @@ void __init kasan_init_hw_tags(void)
break;
}

+ switch (kasan_arg_write_only) {
+ case KASAN_ARG_WRITE_ONLY_DEFAULT:
+ /* Default is specified by kasan_flag_write_only definition. */
+ break;
+ case KASAN_ARG_WRITE_ONLY_OFF:
+ kasan_flag_write_only = false;
+ break;
+ case KASAN_ARG_WRITE_ONLY_ON:
+ kasan_flag_write_only = true;
+ break;
+ }
+
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, write_only=%s\n",
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_arg_write_only));
}

#ifdef CONFIG_KASAN_VMALLOC
@@ -392,6 +432,26 @@ void kasan_enable_hw_tags(void)
hw_enable_tag_checks_asymm();
else
hw_enable_tag_checks_sync();
+
+ /*
+ * CPUs can only be in one of two states:
+ * - All CPUs support the write_only feature
+ * - No CPUs support the write_only feature
+ *
+ * If the first CPU attempts hw_enable_tag_checks_write_only() and
+ * finds the feature unsupported, kasan_arg_write_only is set to OFF
+ * to avoid further unnecessary calls on other CPUs.
+ *
+ * Although this could be tracked with a single variable, both
+ * kasan_arg_write_only (boot argument) and kasan_flag_write_only
+ * (hardware state) are kept separate, consistent with other options.
+ */
+ if (kasan_arg_write_only == KASAN_ARG_WRITE_ONLY_ON &&
+ hw_enable_tag_checks_write_only()) {
+ kasan_arg_write_only = KASAN_ARG_WRITE_ONLY_OFF;
+ kasan_flag_write_only = false;
+ pr_err_once("write-only mode is not supported and thus not enabled\n");
+ }
}

#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
@@ -404,4 +464,10 @@ VISIBLE_IF_KUNIT void kasan_force_async_fault(void)
}
EXPORT_SYMBOL_IF_KUNIT(kasan_force_async_fault);

+VISIBLE_IF_KUNIT bool kasan_write_only_enabled(void)
+{
+ return kasan_flag_write_only;
+}
+EXPORT_SYMBOL_IF_KUNIT(kasan_write_only_enabled);
+
#endif
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 129178be5e64..844eedf2ef9c 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -431,6 +431,7 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
#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()
+#define hw_enable_tag_checks_write_only() arch_enable_tag_checks_write_only()
#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, init) \
@@ -451,11 +452,17 @@ void __init kasan_init_tags(void);
#if defined(CONFIG_KASAN_HW_TAGS) && IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)

void kasan_force_async_fault(void);
+bool kasan_write_only_enabled(void);

#else /* CONFIG_KASAN_HW_TAGS && CONFIG_KASAN_KUNIT_TEST */

static inline void kasan_force_async_fault(void) { }

+static inline bool kasan_write_only_enabled(void)
+{
+ return false;
+}
+
#endif /* CONFIG_KASAN_HW_TAGS && CONFIG_KASAN_KUNIT_TEST */

#ifdef CONFIG_KASAN_SW_TAGS
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}

Yeoreum Yun

unread,
Aug 20, 2025, 3:12:59 AMAug 20
to ryabin...@gmail.com, gli...@google.com, andre...@gmail.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org, Yeoreum Yun
When KASAN is configured in write-only mode,
fetch/load operations do not trigger tag check faults.

As a result, the outcome of some test cases may differ
compared to when KASAN is configured without write-only mode.

Therefore, by modifying pre-exist testcases
check the write only makes tag check fault (TCF) where
writing is perform in "allocated memory" but tag is invalid
(i.e) redzone write in atomic_set() testcases.
Otherwise check the invalid fetch/read doesn't generate TCF.

Also, skip some testcases affected by initial value
(i.e) atomic_cmpxchg() testcase maybe successd if
it passes valid atomic_t address and invalid oldaval address.
In this case, if invalid atomic_t doesn't have the same oldval,
it won't trigger write operation so the test will pass.

Signed-off-by: Yeoreum Yun <yeore...@arm.com>
---
+
+ /*
+
+ /*
+ * The result of the test below may vary due to garbage values in
+ * store-only mode. Therefore, skip this test when KASAN is configured
+ * in store-only mode.
+ */
+ if (!kasan_write_only_enabled())
+ KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(safe, unsafe, 42));
+
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_sub_and_test(42, unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_and_test(unsafe));
KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_negative(42, unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_unless(unsafe, 21, 42));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_not_zero(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_unless_negative(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_unless_positive(unsafe));
- KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_if_positive(unsafe));
+
+ /*
+
+ /*

Andrey Konovalov

unread,
Aug 28, 2025, 4:14:44 PMAug 28
to Yeoreum Yun, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Nit: a dot missing at the end of the sentence.
Nit: access => accesses
For the KASAN parts:

Reviewed-by: Andrey Konovalov <andre...@gmail.com>

Thank you!

Andrey Konovalov

unread,
Aug 28, 2025, 4:14:47 PMAug 28
to Yeoreum Yun, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
On Wed, Aug 20, 2025 at 9:12 AM Yeoreum Yun <yeore...@arm.com> wrote:
>
Let's name this macro "KUNIT_EXPECT_KASAN_RESULT" and the last argument "fail".

> + * a KASAN report or not; a KUnit test failure when it's different from @produce.

..; causes a KUnit test failure when the result is different from @fail.

> *
> * @test: Currently executing KUnit test.
> - * @expression: Expression that must produce a KASAN report.
> + * @expr: Expression produce a KASAN report or not.

Expression to be tested.

> + * @expr_str: Expression string

Expression to be tested encoded as a string.

> + * @produce: expression should produce a KASAN report.

@fail: Whether expression should produce a KASAN report.
Let's keep the message as is for the case when a KASAN report is expected; i.e.:

KASAN failure expected in X, but none occurred

And for the case when KASAN report is not expected, let's do:

KASAN failure not expected in X, but occurred

> } \
> if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) && \
> kasan_sync_fault_possible()) { \
> @@ -141,6 +147,29 @@ static void kasan_test_exit(struct kunit *test)
> WRITE_ONCE(test_status.async_fault, false); \
> } while (0)
>
> +/*
> + * KUNIT_EXPECT_KASAN_FAIL - check that the executed expression produces a
> + * KASAN report; causes a KUnit test failure otherwise.
> + *
> + * @test: Currently executing KUnit test.
> + * @expr: Expression produce a KASAN report.

Expression that must produce a KASAN report.

> + */
> +#define KUNIT_EXPECT_KASAN_FAIL(test, expr) \
> + _KUNIT_EXPECT_KASAN_TEMPLATE(test, expr, #expr, true)
> +
> +/*
> + * KUNIT_EXPECT_KASAN_FAIL_READ - check that the executed expression produces
> + * a KASAN report for read access.
> + * It causes a KUnit test failure. if KASAN report isn't produced for read access.
> + * For write access, it cause a KUnit test failure if a KASAN report is produced

KUNIT_EXPECT_KASAN_FAIL_READ - check that the executed expression
produces a KASAN report when the write-only mode is not enabled;
causes a KUnit test failure otherwise.

Note: At the moment, this macro does not check whether the produced
KASAN report is a report about a bad read access. It is only intended
for checking the write-only KASAN mode functionality without failing
KASAN tests.

> + *
> + * @test: Currently executing KUnit test.
> + * @expr: Expression doesn't produce a KASAN report.

Expression that must only produce a KASAN report when the write-only
mode is not enabled.
Keep this empty line.
No need for this empty line.

> KUNIT_EXPECT_KASAN_FAIL(test, WRITE_ONCE(*i_unsafe, 42));
> - KUNIT_EXPECT_KASAN_FAIL(test, smp_load_acquire(i_unsafe));
> + KUNIT_EXPECT_KASAN_FAIL_READ(test, smp_load_acquire(i_unsafe));
> KUNIT_EXPECT_KASAN_FAIL(test, smp_store_release(i_unsafe, 42));
> -

Keep this empty line.

> - KUNIT_EXPECT_KASAN_FAIL(test, atomic_read(unsafe));
> + KUNIT_EXPECT_KASAN_FAIL_READ(test, atomic_read(unsafe));
> KUNIT_EXPECT_KASAN_FAIL(test, atomic_set(unsafe, 42));
> KUNIT_EXPECT_KASAN_FAIL(test, atomic_add(42, unsafe));
> KUNIT_EXPECT_KASAN_FAIL(test, atomic_sub(42, unsafe));
> @@ -752,18 +789,35 @@ static void kasan_atomics_helper(struct kunit *test, void *unsafe, void *safe)
> KUNIT_EXPECT_KASAN_FAIL(test, atomic_xchg(unsafe, 42));
> KUNIT_EXPECT_KASAN_FAIL(test, atomic_cmpxchg(unsafe, 21, 42));
> KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(unsafe, safe, 42));
> - KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(safe, unsafe, 42));
> +
> + /*
> + * The result of the test below may vary due to garbage values of unsafe in
> + * store-only mode. Therefore, skip this test when KASAN is configured
> + * in store-only mode.

store-only => the write-only

Here and below.
No need for this empty line - this shows that the comment above
applies to all of these checks.

> + KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[size + 5]);
> + KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[real_size - 1]);
>
> kfree(ptr);
> }
> @@ -863,8 +934,8 @@ static void ksize_uaf(struct kunit *test)
>
> OPTIMIZER_HIDE_VAR(ptr);
> KUNIT_EXPECT_KASAN_FAIL(test, ksize(ptr));
> - KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
> - KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
> + KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[0]);
> + KUNIT_EXPECT_KASAN_FAIL_READ(test, ((volatile char *)ptr)[size]);
> }
>
> /*
> @@ -886,6 +957,7 @@ static void rcu_uaf_reclaim(struct rcu_head *rp)
> container_of(rp, struct kasan_rcu_info, rcu);
>
> kfree(fp);
> +

No need for this empty line.
No need for this empty line.
I don't think there's need for the empty lines between the checks above.

>
> /*


> --
> LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
>

Yeoreum Yun

unread,
Sep 1, 2025, 6:14:52 AMSep 1
to Andrey Konovalov, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi Andery,

[...]

> > diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
> > index 0a1418ab72fd..fe1a1e152275 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.write_only=off`` or ``kasan.write_only=on`` controls whether KASAN
> > + checks the write (store) accesses only or all accesses (default: ``off``)
>
> Nit: a dot missing at the end of the sentence.

Thanks! I'll add it.

[...]

> > #ifdef CONFIG_KASAN_HW_TAGS
> > diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
> > index 9a6927394b54..334e9e84983e 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_write_only {
> > + KASAN_ARG_WRITE_ONLY_DEFAULT,
> > + KASAN_ARG_WRITE_ONLY_OFF,
> > + KASAN_ARG_WRITE_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_write_only kasan_arg_write_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);
> >
> > +/* Whether to check write access only. */
>
> Nit: access => accesses

Thanks. I'll change it.

[...]

> For the KASAN parts:
>
> Reviewed-by: Andrey Konovalov <andre...@gmail.com>
>
> Thank you!

Thanks :D

--
Sincerely,
Yeoreum Yun

Yeoreum Yun

unread,
Sep 1, 2025, 6:20:25 AMSep 1
to Andrey Konovalov, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, vincenzo...@arm.com, cor...@lwn.net, catalin...@arm.com, wi...@kernel.org, ak...@linux-foundation.org, sc...@os.amperecomputing.com, jhub...@nvidia.com, pankaj...@amd.com, lei...@debian.org, kales...@google.com, m...@kernel.org, bro...@kernel.org, oliver...@linux.dev, james...@arm.com, ar...@kernel.org, hardevsin...@siliconsignals.io, da...@redhat.com, ya...@os.amperecomputing.com, kasa...@googlegroups.com, work...@vger.kernel.org, linu...@vger.kernel.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linu...@kvack.org
Hi Andrey,
Thanks for your suggestion.
I'll apply with these!

> > *
> > * @test: Currently executing KUnit test.
> > - * @expression: Expression that must produce a KASAN report.
> > + * @expr: Expression produce a KASAN report or not.
>
> Expression to be tested.
>
> > + * @expr_str: Expression string
>

Okay.

> Expression to be tested encoded as a string.
>
> > + * @produce: expression should produce a KASAN report.
>
> @fail: Whether expression should produce a KASAN report.

I'll change with this :)
Thanks. I'll change as your suggestion :)

>
> > } \
> > if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) && \
> > kasan_sync_fault_possible()) { \
> > @@ -141,6 +147,29 @@ static void kasan_test_exit(struct kunit *test)
> > WRITE_ONCE(test_status.async_fault, false); \
> > } while (0)
> >
> > +/*
> > + * KUNIT_EXPECT_KASAN_FAIL - check that the executed expression produces a
> > + * KASAN report; causes a KUnit test failure otherwise.
> > + *
> > + * @test: Currently executing KUnit test.
> > + * @expr: Expression produce a KASAN report.
>
> Expression that must produce a KASAN report.

Thanks.

>
> > + */
> > +#define KUNIT_EXPECT_KASAN_FAIL(test, expr) \
> > + _KUNIT_EXPECT_KASAN_TEMPLATE(test, expr, #expr, true)
> > +
> > +/*
> > + * KUNIT_EXPECT_KASAN_FAIL_READ - check that the executed expression produces
> > + * a KASAN report for read access.
> > + * It causes a KUnit test failure. if KASAN report isn't produced for read access.
> > + * For write access, it cause a KUnit test failure if a KASAN report is produced
>
> KUNIT_EXPECT_KASAN_FAIL_READ - check that the executed expression
> produces a KASAN report when the write-only mode is not enabled;
> causes a KUnit test failure otherwise.
>
> Note: At the moment, this macro does not check whether the produced
> KASAN report is a report about a bad read access. It is only intended
> for checking the write-only KASAN mode functionality without failing
> KASAN tests.
>
> > + *
> > + * @test: Currently executing KUnit test.
> > + * @expr: Expression doesn't produce a KASAN report.
>
> Expression that must only produce a KASAN report when the write-only
> mode is not enabled.

Thanks for your perfect suggsetion :)
Sorry for my bad habit :\
I'll restore all of uneccessary removal/adding line.

Thanks.
Thanks. I'll change them..

[...]

--
Sincerely,
Yeoreum Yun
Reply all
Reply to author
Forward
0 new messages