Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kasan/hw-tags: introduce kasan.write_only option

Patch series "introduce kasan.write_only option in hw-tags", v8.

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 (of 2):

Since Armv8.9, FEATURE_MTE_STORE_ONLY feature is introduced to restrict
raise of tag check fault on store operation only. Introduce 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.

Link: https://lkml.kernel.org/r/20250916222755.466009-1-yeoreum.yun@arm.com
Link: https://lkml.kernel.org/r/20250916222755.466009-2-yeoreum.yun@arm.com
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Breno Leitao <leitao@debian.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: D Scott Phillips <scott@os.amperecomputing.com>
Cc: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
Cc: James Morse <james.morse@arm.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kalesh Singh <kaleshsingh@google.com>
Cc: levi.yun <yeoreum.yun@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: Pankaj Gupta <pankaj.gupta@amd.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Yang Shi <yang@os.amperecomputing.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Yeoreum Yun and committed by
Andrew Morton
31d8edb5 84efbefa

+79 -3
+3
Documentation/dev-tools/kasan.rst
··· 143 143 Asymmetric mode: a bad access is detected synchronously on reads and 144 144 asynchronously on writes. 145 145 146 + - ``kasan.write_only=off`` or ``kasan.write_only=on`` controls whether KASAN 147 + checks the write (store) accesses only or all accesses (default: ``off``). 148 + 146 149 - ``kasan.vmalloc=off`` or ``=on`` disables or enables tagging of vmalloc 147 150 allocations (default: ``on``). 148 151
+1
arch/arm64/include/asm/memory.h
··· 308 308 #define arch_enable_tag_checks_sync() mte_enable_kernel_sync() 309 309 #define arch_enable_tag_checks_async() mte_enable_kernel_async() 310 310 #define arch_enable_tag_checks_asymm() mte_enable_kernel_asymm() 311 + #define arch_enable_tag_checks_write_only() mte_enable_kernel_store_only() 311 312 #define arch_suppress_tag_checks_start() mte_enable_tco() 312 313 #define arch_suppress_tag_checks_stop() mte_disable_tco() 313 314 #define arch_force_async_tag_fault() mte_check_tfsr_exit()
+6
arch/arm64/include/asm/mte-kasan.h
··· 200 200 void mte_enable_kernel_sync(void); 201 201 void mte_enable_kernel_async(void); 202 202 void mte_enable_kernel_asymm(void); 203 + int mte_enable_kernel_store_only(void); 203 204 204 205 #else /* CONFIG_ARM64_MTE */ 205 206 ··· 250 249 251 250 static inline void mte_enable_kernel_asymm(void) 252 251 { 252 + } 253 + 254 + static inline int mte_enable_kernel_store_only(void) 255 + { 256 + return -EINVAL; 253 257 } 254 258 255 259 #endif /* CONFIG_ARM64_MTE */
+1 -1
arch/arm64/kernel/cpufeature.c
··· 2945 2945 { 2946 2946 .desc = "Store Only MTE Tag Check", 2947 2947 .capability = ARM64_MTE_STORE_ONLY, 2948 - .type = ARM64_CPUCAP_SYSTEM_FEATURE, 2948 + .type = ARM64_CPUCAP_BOOT_CPU_FEATURE, 2949 2949 .matches = has_cpuid_feature, 2950 2950 ARM64_CPUID_FIELDS(ID_AA64PFR2_EL1, MTESTOREONLY, IMP) 2951 2951 },
+18
arch/arm64/kernel/mte.c
··· 157 157 mte_enable_kernel_sync(); 158 158 } 159 159 } 160 + 161 + int mte_enable_kernel_store_only(void) 162 + { 163 + /* 164 + * If the CPU does not support MTE store only, 165 + * the kernel checks all operations. 166 + */ 167 + if (!cpus_have_cap(ARM64_MTE_STORE_ONLY)) 168 + return -EINVAL; 169 + 170 + sysreg_clear_set(sctlr_el1, SCTLR_EL1_TCSO_MASK, 171 + SYS_FIELD_PREP(SCTLR_EL1, TCSO, 1)); 172 + isb(); 173 + 174 + pr_info_once("MTE: enabled store only mode at EL1\n"); 175 + 176 + return 0; 177 + } 160 178 #endif 161 179 162 180 #ifdef CONFIG_KASAN_HW_TAGS
+43 -2
mm/kasan/hw_tags.c
··· 60 60 #endif 61 61 EXPORT_SYMBOL_GPL(kasan_flag_vmalloc); 62 62 63 + /* Whether to check write accesses only. */ 64 + static bool kasan_flag_write_only = false; 65 + 63 66 #define PAGE_ALLOC_SAMPLE_DEFAULT 1 64 67 #define PAGE_ALLOC_SAMPLE_ORDER_DEFAULT 3 65 68 ··· 136 133 return 0; 137 134 } 138 135 early_param("kasan.vmalloc", early_kasan_flag_vmalloc); 136 + 137 + /* kasan.write_only=off/on */ 138 + static int __init early_kasan_flag_write_only(char *arg) 139 + { 140 + if (!arg) 141 + return -EINVAL; 142 + 143 + if (!strcmp(arg, "off")) 144 + kasan_flag_write_only = false; 145 + else if (!strcmp(arg, "on")) 146 + kasan_flag_write_only = true; 147 + else 148 + return -EINVAL; 149 + 150 + return 0; 151 + } 152 + early_param("kasan.write_only", early_kasan_flag_write_only); 139 153 140 154 static inline const char *kasan_mode_info(void) 141 155 { ··· 275 255 /* KASAN is now initialized, enable it. */ 276 256 kasan_enable(); 277 257 278 - pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s)\n", 258 + pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s, write_only=%s)\n", 279 259 kasan_mode_info(), 280 260 str_on_off(kasan_vmalloc_enabled()), 281 - str_on_off(kasan_stack_collection_enabled())); 261 + str_on_off(kasan_stack_collection_enabled()), 262 + str_on_off(kasan_flag_write_only)); 282 263 } 283 264 284 265 #ifdef CONFIG_KASAN_VMALLOC ··· 406 385 hw_enable_tag_checks_asymm(); 407 386 else 408 387 hw_enable_tag_checks_sync(); 388 + 389 + /* 390 + * CPUs can only be in one of two states: 391 + * - All CPUs support the write_only feature 392 + * - No CPUs support the write_only feature 393 + * 394 + * If the first CPU attempts hw_enable_tag_checks_write_only() and 395 + * finds the feature unsupported, kasan_flag_write_only is set to OFF 396 + * to avoid further unnecessary calls on other CPUs. 397 + */ 398 + if (kasan_flag_write_only && hw_enable_tag_checks_write_only()) { 399 + kasan_flag_write_only = false; 400 + pr_err_once("write-only mode is not supported and thus not enabled\n"); 401 + } 409 402 } 410 403 411 404 #if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST) ··· 431 396 hw_force_async_tag_fault(); 432 397 } 433 398 EXPORT_SYMBOL_IF_KUNIT(kasan_force_async_fault); 399 + 400 + VISIBLE_IF_KUNIT bool kasan_write_only_enabled(void) 401 + { 402 + return kasan_flag_write_only; 403 + } 404 + EXPORT_SYMBOL_IF_KUNIT(kasan_write_only_enabled); 434 405 435 406 #endif
+7
mm/kasan/kasan.h
··· 437 437 #define hw_suppress_tag_checks_start() arch_suppress_tag_checks_start() 438 438 #define hw_suppress_tag_checks_stop() arch_suppress_tag_checks_stop() 439 439 #define hw_force_async_tag_fault() arch_force_async_tag_fault() 440 + #define hw_enable_tag_checks_write_only() arch_enable_tag_checks_write_only() 440 441 #define hw_get_random_tag() arch_get_random_tag() 441 442 #define hw_get_mem_tag(addr) arch_get_mem_tag(addr) 442 443 #define hw_set_mem_tag_range(addr, size, tag, init) \ ··· 458 457 #if defined(CONFIG_KASAN_HW_TAGS) && IS_ENABLED(CONFIG_KASAN_KUNIT_TEST) 459 458 460 459 void kasan_force_async_fault(void); 460 + bool kasan_write_only_enabled(void); 461 461 462 462 #else /* CONFIG_KASAN_HW_TAGS && CONFIG_KASAN_KUNIT_TEST */ 463 463 464 464 static inline void kasan_force_async_fault(void) { } 465 + 466 + static inline bool kasan_write_only_enabled(void) 467 + { 468 + return false; 469 + } 465 470 466 471 #endif /* CONFIG_KASAN_HW_TAGS && CONFIG_KASAN_KUNIT_TEST */ 467 472