* [RFC PATCH 1/2] kasan: hw_tags: Use KASAN_PAGE_REDZONE for vmalloc redzoning
2026-06-12 4:44 [RFC PATCH 0/2] kasan: hw_tags: Add option to tag only at allocation time Dev Jain
@ 2026-06-12 4:44 ` Dev Jain
2026-06-12 4:44 ` [RFC PATCH 2/2] kasan: hw_tags: Add boot option to elide free time poisoning Dev Jain
1 sibling, 0 replies; 3+ messages in thread
From: Dev Jain @ 2026-06-12 4:44 UTC (permalink / raw)
To: ryabinin.a.a, akpm, corbet
Cc: Dev Jain, glider, andreyknvl, dvyukov, vincenzo.frascino,
kasan-dev, linux-mm, linux-kernel, skhan, workflows, linux-doc,
linux-arm-kernel, ryan.roberts, anshuman.khandual, kaleshsingh,
21cnbao, david, will, catalin.marinas
In preparation for adding "tag only on alloc" boot time option, use
KASAN_PAGE_REDZONE instead of KASAN_TAG_INVALID for poisoning the tail end
of the vmalloc allocation.
Although both values are the same for hw tags, KASAN_SLAB_REDZONE is used
for poisoning the tail end of a kmalloc object allocation, so maintain
the pattern.
Signed-off-by: Dev Jain <dev.jain@arm.com>
---
mm/kasan/hw_tags.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index cbef5e450954e..c1a2b48808ed7 100644
--- a/mm/kasan/hw_tags.c
+++ b/mm/kasan/hw_tags.c
@@ -375,7 +375,7 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
redzone_start = round_up((unsigned long)start + size,
KASAN_GRANULE_SIZE);
redzone_size = round_up(redzone_start, PAGE_SIZE) - redzone_start;
- kasan_poison((void *)redzone_start, redzone_size, KASAN_TAG_INVALID,
+ kasan_poison((void *)redzone_start, redzone_size, KASAN_PAGE_REDZONE,
flags & KASAN_VMALLOC_INIT);
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH 2/2] kasan: hw_tags: Add boot option to elide free time poisoning
2026-06-12 4:44 [RFC PATCH 0/2] kasan: hw_tags: Add option to tag only at allocation time Dev Jain
2026-06-12 4:44 ` [RFC PATCH 1/2] kasan: hw_tags: Use KASAN_PAGE_REDZONE for vmalloc redzoning Dev Jain
@ 2026-06-12 4:44 ` Dev Jain
1 sibling, 0 replies; 3+ messages in thread
From: Dev Jain @ 2026-06-12 4:44 UTC (permalink / raw)
To: ryabinin.a.a, akpm, corbet
Cc: Dev Jain, glider, andreyknvl, dvyukov, vincenzo.frascino,
kasan-dev, linux-mm, linux-kernel, skhan, workflows, linux-doc,
linux-arm-kernel, ryan.roberts, anshuman.khandual, kaleshsingh,
21cnbao, david, will, catalin.marinas
Introduce a boot option to tag only at allocation time of the objects. This
reduces KASAN MTE overhead, the tradeoff being reduced ability
of catching bugs.
Now, when a memory object will be freed, it will retain the random tag it
had at allocation time. This compromises on catching UAF bugs, till the
time the object is not reallocated.
Hence, not catching "use-after-free-before-reallocation" and not catching
"double-free" will be the compromise for reduced KASAN overhead.
Keep this as a boot time feature to prevent building two kernel images.
To implement the feature, we need to effectively render kasan_poison()
redundant for hw tags case, but keep it working in the case where it is
used not in an object-freeing code path, but the redzoning path (which
means, poisoning the tail end of a vmalloc or kmalloc allocation).
We achieve this by overloading the poison values for the hw tags case: we
define the four poison values as 0x0E, 0x1E, 0x2E, 0x3E. In kasan_poison(),
if we arrive with KASAN_SLAB_REDZONE or KASAN_PAGE_REDZONE, do a bitwise
OR on the value of the tag to make it equal to KASAN_TAG_INVALID.
If not, then, if init is true, zero out the memory and bail out.
Signed-off-by: Dev Jain <dev.jain@arm.com>
---
Documentation/dev-tools/kasan.rst | 4 +++
mm/kasan/hw_tags.c | 43 ++++++++++++++++++++++++++++++-
mm/kasan/kasan.h | 23 ++++++++++++++++-
3 files changed, 68 insertions(+), 2 deletions(-)
diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
index 4968b2aa60c80..b0c30584b5062 100644
--- a/Documentation/dev-tools/kasan.rst
+++ b/Documentation/dev-tools/kasan.rst
@@ -146,6 +146,10 @@ disabling KASAN altogether or controlling its features:
- ``kasan.vmalloc=off`` or ``=on`` disables or enables tagging of vmalloc
allocations (default: ``on``).
+- ``kasan.tag_only_on_alloc=off`` or ``=on`` disables or enables skipping
+ free-time tagging (poisoning) while keeping allocation-time tagging enabled
+ (default: ``off``).
+
- ``kasan.page_alloc.sample=<sampling interval>`` makes KASAN tag only every
Nth page_alloc allocation with the order equal or greater than
``kasan.page_alloc.sample.order``, where N is the value of the ``sample``
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index c1a2b48808ed7..a392e34d11e3a 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_tag_only_on_alloc {
+ KASAN_ARG_TAG_ONLY_ON_ALLOC_DEFAULT,
+ KASAN_ARG_TAG_ONLY_ON_ALLOC_OFF,
+ KASAN_ARG_TAG_ONLY_ON_ALLOC_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_tag_only_on_alloc kasan_arg_tag_only_on_alloc __initdata;
/*
* Whether the selected mode is synchronous, asynchronous, or asymmetric.
@@ -63,6 +70,10 @@ EXPORT_SYMBOL_GPL(kasan_flag_vmalloc);
/* Whether to check write accesses only. */
static bool kasan_flag_write_only = false;
+/* Whether to skip free-time tagging. */
+DEFINE_STATIC_KEY_FALSE(kasan_flag_tag_only_on_alloc);
+EXPORT_SYMBOL_GPL(kasan_flag_tag_only_on_alloc);
+
#define PAGE_ALLOC_SAMPLE_DEFAULT 1
#define PAGE_ALLOC_SAMPLE_ORDER_DEFAULT 3
@@ -154,6 +165,23 @@ static int __init early_kasan_flag_write_only(char *arg)
}
early_param("kasan.write_only", early_kasan_flag_write_only);
+/* kasan.tag_only_on_alloc=off/on */
+static int __init early_kasan_flag_tag_only_on_alloc(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (!strcmp(arg, "off"))
+ kasan_arg_tag_only_on_alloc = KASAN_ARG_TAG_ONLY_ON_ALLOC_OFF;
+ else if (!strcmp(arg, "on"))
+ kasan_arg_tag_only_on_alloc = KASAN_ARG_TAG_ONLY_ON_ALLOC_ON;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+early_param("kasan.tag_only_on_alloc", early_kasan_flag_tag_only_on_alloc);
+
static inline const char *kasan_mode_info(void)
{
if (kasan_mode == KASAN_MODE_ASYNC)
@@ -270,14 +298,27 @@ void __init kasan_init_hw_tags(void)
break;
}
+ switch (kasan_arg_tag_only_on_alloc) {
+ case KASAN_ARG_TAG_ONLY_ON_ALLOC_DEFAULT:
+ /* Default is specified by kasan_flag_tag_only_on_alloc. */
+ break;
+ case KASAN_ARG_TAG_ONLY_ON_ALLOC_OFF:
+ static_branch_disable(&kasan_flag_tag_only_on_alloc);
+ break;
+ case KASAN_ARG_TAG_ONLY_ON_ALLOC_ON:
+ static_branch_enable(&kasan_flag_tag_only_on_alloc);
+ break;
+ }
+
kasan_init_tags();
/* KASAN is now initialized, enable it. */
kasan_enable();
- pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s, write_only=%s)\n",
+ pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, tag_only_on_alloc=%s, stacktrace=%s, write_only=%s)\n",
kasan_mode_info(),
str_on_off(kasan_vmalloc_enabled()),
+ str_on_off(kasan_tag_only_on_alloc_enabled()),
str_on_off(kasan_stack_collection_enabled()),
str_on_off(kasan_flag_write_only));
}
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index fc9169a547662..4fa8abb312faa 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_tag_only_on_alloc);
enum kasan_mode {
KASAN_MODE_SYNC,
@@ -52,6 +53,11 @@ static inline bool kasan_vmalloc_enabled(void)
return static_branch_likely(&kasan_flag_vmalloc);
}
+static inline bool kasan_tag_only_on_alloc_enabled(void)
+{
+ return static_branch_unlikely(&kasan_flag_tag_only_on_alloc);
+}
+
static inline bool kasan_async_fault_possible(void)
{
return kasan_mode == KASAN_MODE_ASYNC || kasan_mode == KASAN_MODE_ASYMM;
@@ -145,12 +151,17 @@ static inline bool kasan_requires_meta(void)
#define KASAN_SLAB_REDZONE 0xFC /* redzone for slab object */
#define KASAN_SLAB_FREE 0xFB /* freed slab object */
#define KASAN_VMALLOC_INVALID 0xF8 /* inaccessible space in vmap area */
+#elif defined(CONFIG_KASAN_HW_TAGS)
+#define KASAN_PAGE_FREE 0x0E
+#define KASAN_PAGE_REDZONE 0x1E
+#define KASAN_SLAB_REDZONE 0x2E
+#define KASAN_SLAB_FREE 0x3E
#else
#define KASAN_PAGE_FREE KASAN_TAG_INVALID
#define KASAN_PAGE_REDZONE KASAN_TAG_INVALID
#define KASAN_SLAB_REDZONE KASAN_TAG_INVALID
#define KASAN_SLAB_FREE KASAN_TAG_INVALID
-#define KASAN_VMALLOC_INVALID KASAN_TAG_INVALID /* only used for SW_TAGS */
+#define KASAN_VMALLOC_INVALID KASAN_TAG_INVALID
#endif
#ifdef CONFIG_KASAN_GENERIC
@@ -478,6 +489,16 @@ static inline u8 kasan_random_tag(void) { return 0; }
static inline void kasan_poison(const void *addr, size_t size, u8 value, bool init)
{
+ if (kasan_tag_only_on_alloc_enabled()) {
+ if ((value != KASAN_SLAB_REDZONE) && (value != KASAN_PAGE_REDZONE)) {
+ if (init)
+ memset((void *)kasan_reset_tag(addr), 0, size);
+ return;
+ }
+ }
+
+ value |= 0xF0;
+
if (WARN_ON((unsigned long)addr & KASAN_GRANULE_MASK))
return;
if (WARN_ON(size & KASAN_GRANULE_MASK))
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread