[PATCH v3 00/12] kasan: unify kasan_arch_is_ready() and remove arch-specific implementations

2 views
Skip to first unread message

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:27:44 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
This patch series addresses the fragmentation in KASAN initialization
across architectures by introducing a unified approach that eliminates
duplicate static keys and arch-specific kasan_arch_is_ready()
implementations.

The core issue is that different architectures have inconsistent approaches
to KASAN readiness tracking:
- PowerPC, LoongArch, and um arch, each implement own kasan_arch_is_ready()
- Only HW_TAGS mode had a unified static key (kasan_flag_enabled)
- Generic and SW_TAGS modes relied on arch-specific solutions
or always-on behavior

This series implements two-level approach:
1. kasan_enabled() - compile-time check for KASAN configuration
2. kasan_shadow_initialized() - runtime check for shadow memory readiness

Key improvements:
- Unified static key infrastructure across all KASAN modes
- Runtime overhead only for architectures that actually need it
- Compile-time optimization for arch. with early KASAN initialization
- Complete elimination of arch-specific kasan_arch_is_ready()
- Consistent interface and reduced code duplication

Previous v2 thread: https://lore.kernel.org/all/20250626153147.1...@gmail.com/

Changes in v3 (sorry for the 3-week gap):

0. Included in TO, CC only KASAN devs and people who commented in v2.

1. Addressed Andrey Konovalov's feedback:
- Kept separate kasan_enabled() and kasan_shadow_initialized() functions
- Added proper __wrapper functions with clean separation

2. Addressed Christophe Leroy's performance comments:
- CONFIG_ARCH_DEFER_KASAN is only selected by architectures that need it
- No static key overhead for architectures that can enable KASAN early
- PowerPC 32-bit and book3e get compile-time optimization

3. Addressed Heiko Carstens and Alexander Gordeev s390 comments:
- s390 doesn't select ARCH_DEFER_KASAN (no unnecessary static key overhead)
- kasan_enable() is a no-op for architectures with early KASAN setup

4. Improved wrapper architecture:
- All existing wrapper functions in include/linux/kasan.h now check both
kasan_enabled() && kasan_shadow_initialized()
- Internal implementation functions focus purely on core functionality
- Shadow readiness logic is centralized in headers per Andrey's guidance

Architecture-specific changes:
- PowerPC radix MMU: selects ARCH_DEFER_KASAN for runtime control
- LoongArch: selects ARCH_DEFER_KASAN, removes custom kasan_early_stage
- um: selects ARCH_DEFER_KASAN, removes kasan_um_is_ready
- Other architectures: get compile-time optimization, no runtime overhead

The series maintains full backward compatibility while providing optimal
performance for each architecture's needs.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049

=== Current mainline KUnit status

To see if there is any regression, I've tested via compiling a kernel
with CONFIG_KASAN_KUNIT_TEST and running QEMU VM. There are failing tests
in SW_TAGS and GENERIC modes in arm64:

arm64 CONFIG_KASAN_HW_TAGS:
# kasan: pass:62 fail:0 skip:13 total:75
# Totals: pass:62 fail:0 skip:13 total:75
ok 1 kasan

arm64 CONFIG_KASAN_SW_TAGS=y:
# kasan: pass:65 fail:1 skip:9 total:75
# Totals: pass:65 fail:1 skip:9 total:75
not ok 1 kasan
# kasan_strings: EXPECTATION FAILED at mm/kasan/kasan_test_c.c:1598
KASAN failure expected in "strscpy(ptr, src + KASAN_GRANULE_SIZE, KASAN_GRANULE_SIZE)", but none occurred

arm64 CONFIG_KASAN_GENERIC=y, CONFIG_KASAN_OUTLINE=y:
# kasan: pass:61 fail:1 skip:13 total:75
# Totals: pass:61 fail:1 skip:13 total:75
not ok 1 kasan
# same failure as above

x86_64 CONFIG_KASAN_GENERIC=y:
# kasan: pass:58 fail:0 skip:17 total:75
# Totals: pass:58 fail:0 skip:17 total:75
ok 1 kasan

=== Testing with patches

Testing in v3:

- Compiled every affected arch with no errors:

$ make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \
OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \
HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld \
ARCH=$ARCH

$ clang --version
ClangBuiltLinux clang version 19.1.4
Target: x86_64-unknown-linux-gnu
Thread model: posix

- make ARCH=um produces the warning during compiling:
MODPOST Module.symvers
WARNING: modpost: vmlinux: section mismatch in reference: \
kasan_init+0x43 (section: .ltext) -> \
kasan_init_generic (section: .init.text)

AFAIU, it's due to the code in arch/um/kernel/mem.c, where kasan_init()
is placed in own section ".kasan_init", which calls kasan_init_generic()
which is marked with "__init".

- Booting via qemu-system- and running KUnit tests:

* arm64 (GENERIC, HW_TAGS, SW_TAGS): no regression, same above results.
* x86_64 (GENERIC): no regression, no errors

Sabyrzhan Tasbolatov (12):
lib/kasan: introduce CONFIG_ARCH_DEFER_KASAN option
kasan: unify static kasan_flag_enabled across modes
kasan/powerpc: select ARCH_DEFER_KASAN and call kasan_init_generic
kasan/arm64: call kasan_init_generic in kasan_init
kasan/arm: call kasan_init_generic in kasan_init
kasan/xtensa: call kasan_init_generic in kasan_init
kasan/loongarch: select ARCH_DEFER_KASAN and call kasan_init_generic
kasan/um: select ARCH_DEFER_KASAN and call kasan_init_generic
kasan/x86: call kasan_init_generic in kasan_init
kasan/s390: call kasan_init_generic in kasan_init
kasan/riscv: call kasan_init_generic in kasan_init
kasan: add shadow checks to wrappers and rename kasan_arch_is_ready

arch/arm/mm/kasan_init.c | 2 +-
arch/arm64/mm/kasan_init.c | 4 +--
arch/loongarch/Kconfig | 1 +
arch/loongarch/include/asm/kasan.h | 7 -----
arch/loongarch/mm/kasan_init.c | 7 ++---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/kasan.h | 12 --------
arch/powerpc/mm/kasan/init_32.c | 2 +-
arch/powerpc/mm/kasan/init_book3e_64.c | 2 +-
arch/powerpc/mm/kasan/init_book3s_64.c | 6 +---
arch/riscv/mm/kasan_init.c | 1 +
arch/s390/kernel/early.c | 3 +-
arch/um/Kconfig | 1 +
arch/um/include/asm/kasan.h | 5 ---
arch/um/kernel/mem.c | 4 +--
arch/x86/mm/kasan_init_64.c | 2 +-
arch/xtensa/mm/kasan_init.c | 2 +-
include/linux/kasan-enabled.h | 34 ++++++++++++++++-----
include/linux/kasan.h | 42 ++++++++++++++++++++------
lib/Kconfig.kasan | 8 +++++
mm/kasan/common.c | 18 +++++++----
mm/kasan/generic.c | 23 ++++++++------
mm/kasan/hw_tags.c | 9 +-----
mm/kasan/kasan.h | 36 ++++++++++++++++------
mm/kasan/shadow.c | 32 +++++---------------
mm/kasan/sw_tags.c | 2 ++
26 files changed, 146 insertions(+), 120 deletions(-)

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:27:47 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Introduce CONFIG_ARCH_DEFER_KASAN to identify architectures that need
to defer KASAN initialization until shadow memory is properly set up.

Some architectures (like PowerPC with radix MMU) need to set up their
shadow memory mappings before KASAN can be safely enabled, while others
(like s390, x86, arm) can enable KASAN much earlier or even from the
beginning.

This option allows us to:
1. Use static keys only where needed (avoiding overhead)
2. Use compile-time constants for arch that don't need runtime checks
3. Maintain optimal performance for both scenarios

Architectures that need deferred KASAN should select this option.
Architectures that can enable KASAN early will get compile-time
optimizations instead of runtime checks.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v3:
- Introduced CONFIG_ARCH_DEFER_KASAN to control static key usage
---
lib/Kconfig.kasan | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index f82889a830f..38456560c85 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -19,6 +19,14 @@ config ARCH_DISABLE_KASAN_INLINE
Disables both inline and stack instrumentation. Selected by
architectures that do not support these instrumentation types.

+config ARCH_DEFER_KASAN
+ bool
+ help
+ Architectures should select this if they need to defer KASAN
+ initialization until shadow memory is properly set up. This
+ enables runtime control via static keys. Otherwise, KASAN uses
+ compile-time constants for better performance.
+
config CC_HAS_KASAN_GENERIC
def_bool $(cc-option, -fsanitize=kernel-address)

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:27:52 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Historically, the runtime static key kasan_flag_enabled existed only for
CONFIG_KASAN_HW_TAGS mode. Generic and SW_TAGS modes either relied on
architecture-specific kasan_arch_is_ready() implementations or evaluated
KASAN checks unconditionally, leading to code duplication.

This patch implements two-level approach:

1. kasan_enabled() - controls if KASAN is enabled at all (compile-time)
2. kasan_shadow_initialized() - tracks shadow memory
initialization (runtime)

For architectures that select ARCH_DEFER_KASAN: kasan_shadow_initialized()
uses a static key that gets enabled when shadow memory is ready.

For architectures that don't: kasan_shadow_initialized() returns
IS_ENABLED(CONFIG_KASAN) since shadow is ready from the start.

This provides:
- Consistent interface across all KASAN modes
- Runtime control only where actually needed
- Compile-time constants for optimal performance where possible
- Clear separation between "KASAN configured" vs "shadow ready"

Also adds kasan_init_generic() function that enables the shadow flag and
handles initialization for Generic mode, and updates SW_TAGS and HW_TAGS
to use the unified kasan_shadow_enable() function.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v3:
- Only architectures that need deferred KASAN get runtime overhead
- Added kasan_shadow_initialized() for shadow memory readiness tracking
- kasan_enabled() now provides compile-time check for KASAN configuration
---
include/linux/kasan-enabled.h | 34 ++++++++++++++++++++++++++--------
include/linux/kasan.h | 6 ++++++
mm/kasan/common.c | 9 +++++++++
mm/kasan/generic.c | 11 +++++++++++
mm/kasan/hw_tags.c | 9 +--------
mm/kasan/sw_tags.c | 2 ++
6 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/include/linux/kasan-enabled.h b/include/linux/kasan-enabled.h
index 6f612d69ea0..fa99dc58f95 100644
--- a/include/linux/kasan-enabled.h
+++ b/include/linux/kasan-enabled.h
@@ -4,32 +4,50 @@

#include <linux/static_key.h>

-#ifdef CONFIG_KASAN_HW_TAGS
+/* Controls whether KASAN is enabled at all (compile-time check). */
+static __always_inline bool kasan_enabled(void)
+{
+ return IS_ENABLED(CONFIG_KASAN);
+}

+#ifdef CONFIG_ARCH_DEFER_KASAN
+/*
+ * Global runtime flag for architectures that need deferred KASAN.
+ * Switched to 'true' by the appropriate kasan_init_*()
+ * once KASAN is fully initialized.
+ */
DECLARE_STATIC_KEY_FALSE(kasan_flag_enabled);

-static __always_inline bool kasan_enabled(void)
+static __always_inline bool kasan_shadow_initialized(void)
{
return static_branch_likely(&kasan_flag_enabled);
}

-static inline bool kasan_hw_tags_enabled(void)
+static inline void kasan_enable(void)
+{
+ static_branch_enable(&kasan_flag_enabled);
+}
+#else
+/* For architectures that can enable KASAN early, use compile-time check. */
+static __always_inline bool kasan_shadow_initialized(void)
{
return kasan_enabled();
}

-#else /* CONFIG_KASAN_HW_TAGS */
+/* No-op for architectures that don't need deferred KASAN. */
+static inline void kasan_enable(void) {}
+#endif /* CONFIG_ARCH_DEFER_KASAN */

-static inline bool kasan_enabled(void)
+#ifdef CONFIG_KASAN_HW_TAGS
+static inline bool kasan_hw_tags_enabled(void)
{
- return IS_ENABLED(CONFIG_KASAN);
+ return kasan_enabled();
}
-
+#else
static inline bool kasan_hw_tags_enabled(void)
{
return false;
}
-
#endif /* CONFIG_KASAN_HW_TAGS */

#endif /* LINUX_KASAN_ENABLED_H */
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 890011071f2..51a8293d1af 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -543,6 +543,12 @@ void kasan_report_async(void);

#endif /* CONFIG_KASAN_HW_TAGS */

+#ifdef CONFIG_KASAN_GENERIC
+void __init kasan_init_generic(void);
+#else
+static inline void kasan_init_generic(void) { }
+#endif
+
#ifdef CONFIG_KASAN_SW_TAGS
void __init kasan_init_sw_tags(void);
#else
diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index ed4873e18c7..c3a6446404d 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -32,6 +32,15 @@
#include "kasan.h"
#include "../slab.h"

+#ifdef CONFIG_ARCH_DEFER_KASAN
+/*
+ * Definition of the unified static key declared in kasan-enabled.h.
+ * This provides consistent runtime enable/disable across KASAN modes.
+ */
+DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled);
+EXPORT_SYMBOL(kasan_flag_enabled);
+#endif
+
struct slab *kasan_addr_to_slab(const void *addr)
{
if (virt_addr_valid(addr))
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index d54e89f8c3e..03b6d322ff6 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -36,6 +36,17 @@
#include "kasan.h"
#include "../slab.h"

+/*
+ * Initialize Generic KASAN and enable runtime checks.
+ * This should be called from arch kasan_init() once shadow memory is ready.
+ */
+void __init kasan_init_generic(void)
+{
+ kasan_enable();
+
+ pr_info("KernelAddressSanitizer initialized (generic)\n");
+}
+
/*
* All functions below always inlined so compiler could
* perform better optimizations in each of __asan_loadX/__assn_storeX
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index 9a6927394b5..c8289a3feab 100644
--- a/mm/kasan/hw_tags.c
+++ b/mm/kasan/hw_tags.c
@@ -45,13 +45,6 @@ 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;

-/*
- * Whether KASAN is enabled at all.
- * The value remains false until KASAN is initialized by kasan_init_hw_tags().
- */
-DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled);
-EXPORT_SYMBOL(kasan_flag_enabled);
-
/*
* Whether the selected mode is synchronous, asynchronous, or asymmetric.
* Defaults to KASAN_MODE_SYNC.
@@ -260,7 +253,7 @@ void __init kasan_init_hw_tags(void)
kasan_init_tags();

/* KASAN is now initialized, enable it. */
- static_branch_enable(&kasan_flag_enabled);
+ kasan_enable();

pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s)\n",
kasan_mode_info(),
diff --git a/mm/kasan/sw_tags.c b/mm/kasan/sw_tags.c
index b9382b5b6a3..275bcbbf612 100644
--- a/mm/kasan/sw_tags.c
+++ b/mm/kasan/sw_tags.c
@@ -45,6 +45,8 @@ void __init kasan_init_sw_tags(void)

kasan_init_tags();

+ kasan_enable();
+
pr_info("KernelAddressSanitizer initialized (sw-tags, stacktrace=%s)\n",
str_on_off(kasan_stack_collection_enabled()));
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:27:54 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
PowerPC with radix MMU is the primary architecture that needs deferred
KASAN initialization, as it requires complex shadow memory setup before
KASAN can be safely enabled.

Select ARCH_DEFER_KASAN for PPC_RADIX_MMU to enable the static key
mechanism for runtime KASAN control. Other PowerPC configurations
(like book3e and 32-bit) can enable KASAN early and will use
compile-time constants instead.

Also call kasan_init_generic() which handles Generic KASAN initialization.
For PowerPC radix MMU (which selects ARCH_DEFER_KASAN), this enables
the static key. For other PowerPC variants, kasan_enable() is a no-op
and kasan_enabled() returns IS_ENABLED(CONFIG_KASAN).

Remove the PowerPC-specific static key and kasan_arch_is_ready()
implementation in favor of the unified interface.

This ensures that:
- PowerPC radix gets the runtime control it needs
- Other PowerPC variants get optimal compile-time behavior
- No unnecessary overhead is added where not needed

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Fixes: 55d77bae7342 ("kasan: fix Oops due to missing calls to kasan_arch_is_ready()")
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v3:
- Added CONFIG_ARCH_DEFER_KASAN selection for PPC_RADIX_MMU only
- Kept ARCH_DISABLE_KASAN_INLINE selection since it's needed independently
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/kasan.h | 12 ------------
arch/powerpc/mm/kasan/init_32.c | 2 +-
arch/powerpc/mm/kasan/init_book3e_64.c | 2 +-
arch/powerpc/mm/kasan/init_book3s_64.c | 6 +-----
5 files changed, 4 insertions(+), 19 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c3e0cc83f12..e5a6aae6a77 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -123,6 +123,7 @@ config PPC
#
select ARCH_32BIT_OFF_T if PPC32
select ARCH_DISABLE_KASAN_INLINE if PPC_RADIX_MMU
+ select ARCH_DEFER_KASAN if PPC_RADIX_MMU
select ARCH_DMA_DEFAULT_COHERENT if !NOT_COHERENT_CACHE
select ARCH_ENABLE_MEMORY_HOTPLUG
select ARCH_ENABLE_MEMORY_HOTREMOVE
diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h
index b5bbb94c51f..957a57c1db5 100644
--- a/arch/powerpc/include/asm/kasan.h
+++ b/arch/powerpc/include/asm/kasan.h
@@ -53,18 +53,6 @@
#endif

#ifdef CONFIG_KASAN
-#ifdef CONFIG_PPC_BOOK3S_64
-DECLARE_STATIC_KEY_FALSE(powerpc_kasan_enabled_key);
-
-static __always_inline bool kasan_arch_is_ready(void)
-{
- if (static_branch_likely(&powerpc_kasan_enabled_key))
- return true;
- return false;
-}
-
-#define kasan_arch_is_ready kasan_arch_is_ready
-#endif

void kasan_early_init(void);
void kasan_mmu_init(void);
diff --git a/arch/powerpc/mm/kasan/init_32.c b/arch/powerpc/mm/kasan/init_32.c
index 03666d790a5..1d083597464 100644
--- a/arch/powerpc/mm/kasan/init_32.c
+++ b/arch/powerpc/mm/kasan/init_32.c
@@ -165,7 +165,7 @@ void __init kasan_init(void)

/* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KASAN init done\n");
+ kasan_init_generic();
}

void __init kasan_late_init(void)
diff --git a/arch/powerpc/mm/kasan/init_book3e_64.c b/arch/powerpc/mm/kasan/init_book3e_64.c
index 60c78aac0f6..0d3a73d6d4b 100644
--- a/arch/powerpc/mm/kasan/init_book3e_64.c
+++ b/arch/powerpc/mm/kasan/init_book3e_64.c
@@ -127,7 +127,7 @@ void __init kasan_init(void)

/* Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KASAN init done\n");
+ kasan_init_generic();
}

void __init kasan_late_init(void) { }
diff --git a/arch/powerpc/mm/kasan/init_book3s_64.c b/arch/powerpc/mm/kasan/init_book3s_64.c
index 7d959544c07..dcafa641804 100644
--- a/arch/powerpc/mm/kasan/init_book3s_64.c
+++ b/arch/powerpc/mm/kasan/init_book3s_64.c
@@ -19,8 +19,6 @@
#include <linux/memblock.h>
#include <asm/pgalloc.h>

-DEFINE_STATIC_KEY_FALSE(powerpc_kasan_enabled_key);
-
static void __init kasan_init_phys_region(void *start, void *end)
{
unsigned long k_start, k_end, k_cur;
@@ -92,11 +90,9 @@ void __init kasan_init(void)
*/
memset(kasan_early_shadow_page, 0, PAGE_SIZE);

- static_branch_inc(&powerpc_kasan_enabled_key);
-
/* Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KASAN init done\n");
+ kasan_init_generic();
}

void __init kasan_early_init(void) { }
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:27:57 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization.
Since arm64 doesn't select ARCH_DEFER_KASAN, this will be a no-op for
the runtime flag but will print the initialization banner.

For SW_TAGS and HW_TAGS modes, their respective init functions will
handle the flag enabling.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/arm64/mm/kasan_init.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c
index d541ce45dae..abeb81bf6eb 100644
--- a/arch/arm64/mm/kasan_init.c
+++ b/arch/arm64/mm/kasan_init.c
@@ -399,14 +399,12 @@ void __init kasan_init(void)
{
kasan_init_shadow();
kasan_init_depth();
-#if defined(CONFIG_KASAN_GENERIC)
+ kasan_init_generic();
/*
* Generic KASAN is now fully initialized.
* Software and Hardware Tag-Based modes still require
* kasan_init_sw_tags() and kasan_init_hw_tags() correspondingly.
*/
- pr_info("KernelAddressSanitizer initialized (generic)\n");
-#endif
}

#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:00 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since arm doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, but kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/arm/mm/kasan_init.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/mm/kasan_init.c b/arch/arm/mm/kasan_init.c
index 111d4f70313..c6625e808bf 100644
--- a/arch/arm/mm/kasan_init.c
+++ b/arch/arm/mm/kasan_init.c
@@ -300,6 +300,6 @@ void __init kasan_init(void)
local_flush_tlb_all();

memset(kasan_early_shadow_page, 0, PAGE_SIZE);
- pr_info("Kernel address sanitizer initialized\n");
init_task.kasan_depth = 0;
+ kasan_init_generic();
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:04 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since xtensa doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op.

Note that arch/xtensa still uses "current" instead of "init_task" pointer
in `current->kasan_depth = 0;` to enable error messages. This is left
unchanged as it cannot be tested.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/xtensa/mm/kasan_init.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/xtensa/mm/kasan_init.c b/arch/xtensa/mm/kasan_init.c
index f39c4d83173..0524b9ed5e6 100644
--- a/arch/xtensa/mm/kasan_init.c
+++ b/arch/xtensa/mm/kasan_init.c
@@ -94,5 +94,5 @@ void __init kasan_init(void)

/* At this point kasan is fully initialized. Enable error messages. */
current->kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized\n");
+ kasan_init_generic();
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:07 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
LoongArch needs deferred KASAN initialization as it has a custom
kasan_arch_is_ready() implementation that tracks shadow memory
readiness via the kasan_early_stage flag.

Select ARCH_DEFER_KASAN to enable the unified static key mechanism
for runtime KASAN control. Call kasan_init_generic() which handles
Generic KASAN initialization and enables the static key.

Replace kasan_arch_is_ready() with kasan_enabled() and delete the
flag kasan_early_stage in favor of the unified kasan_enabled()
interface.

Note that init_task.kasan_depth = 0 is called after kasan_init_generic(),
which is different than in other arch kasan_init(). This is left
unchanged as it cannot be tested.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v3:
- Added CONFIG_ARCH_DEFER_KASAN selection to enable proper runtime control
---
arch/loongarch/Kconfig | 1 +
arch/loongarch/include/asm/kasan.h | 7 -------
arch/loongarch/mm/kasan_init.c | 7 ++-----
3 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 4b19f93379a..07130809a35 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -9,6 +9,7 @@ config LOONGARCH
select ACPI_PPTT if ACPI
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
select ARCH_BINFMT_ELF_STATE
+ select ARCH_DEFER_KASAN
select ARCH_DISABLE_KASAN_INLINE
select ARCH_ENABLE_MEMORY_HOTPLUG
select ARCH_ENABLE_MEMORY_HOTREMOVE
diff --git a/arch/loongarch/include/asm/kasan.h b/arch/loongarch/include/asm/kasan.h
index 62f139a9c87..0e50e5b5e05 100644
--- a/arch/loongarch/include/asm/kasan.h
+++ b/arch/loongarch/include/asm/kasan.h
@@ -66,7 +66,6 @@
#define XKPRANGE_WC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_WC_KASAN_OFFSET)
#define XKVRANGE_VC_SHADOW_OFFSET (KASAN_SHADOW_START + XKVRANGE_VC_KASAN_OFFSET)

-extern bool kasan_early_stage;
extern unsigned char kasan_early_shadow_page[PAGE_SIZE];

#define kasan_mem_to_shadow kasan_mem_to_shadow
@@ -75,12 +74,6 @@ void *kasan_mem_to_shadow(const void *addr);
#define kasan_shadow_to_mem kasan_shadow_to_mem
const void *kasan_shadow_to_mem(const void *shadow_addr);

-#define kasan_arch_is_ready kasan_arch_is_ready
-static __always_inline bool kasan_arch_is_ready(void)
-{
- return !kasan_early_stage;
-}
-
#define addr_has_metadata addr_has_metadata
static __always_inline bool addr_has_metadata(const void *addr)
{
diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c
index d2681272d8f..cf8315f9119 100644
--- a/arch/loongarch/mm/kasan_init.c
+++ b/arch/loongarch/mm/kasan_init.c
@@ -40,11 +40,9 @@ static pgd_t kasan_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);
#define __pte_none(early, pte) (early ? pte_none(pte) : \
((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page)))

-bool kasan_early_stage = true;
-
void *kasan_mem_to_shadow(const void *addr)
{
- if (!kasan_arch_is_ready()) {
+ if (!kasan_enabled()) {
return (void *)(kasan_early_shadow_page);
} else {
unsigned long maddr = (unsigned long)addr;
@@ -298,7 +296,7 @@ void __init kasan_init(void)
kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START),
kasan_mem_to_shadow((void *)KFENCE_AREA_END));

- kasan_early_stage = false;
+ kasan_init_generic();

/* Populate the linear mapping */
for_each_mem_range(i, &pa_start, &pa_end) {
@@ -329,5 +327,4 @@ void __init kasan_init(void)

/* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized.\n");
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:11 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
UserMode Linux needs deferred KASAN initialization as it has a custom
kasan_arch_is_ready() implementation that tracks shadow memory readiness
via the kasan_um_is_ready flag.

Select ARCH_DEFER_KASAN to enable the unified static key mechanism
for runtime KASAN control. Call kasan_init_generic() which handles
Generic KASAN initialization and enables the static key.

Delete the key kasan_um_is_ready in favor of the unified kasan_enabled()
interface.

Note that kasan_init_generic has __init macro, which is called by
kasan_init() which is not marked with __init in arch/um code.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v3:
- Added CONFIG_ARCH_DEFER_KASAN selection for proper runtime control
---
arch/um/Kconfig | 1 +
arch/um/include/asm/kasan.h | 5 -----
arch/um/kernel/mem.c | 4 ++--
3 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index f08e8a7fac9..fd6d78bba52 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -8,6 +8,7 @@ config UML
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
select ARCH_HAS_CPU_FINALIZE_INIT
select ARCH_HAS_FORTIFY_SOURCE
+ select ARCH_DEFER_KASAN
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_HAS_KCOV
select ARCH_HAS_STRNCPY_FROM_USER
diff --git a/arch/um/include/asm/kasan.h b/arch/um/include/asm/kasan.h
index f97bb1f7b85..81bcdc0f962 100644
--- a/arch/um/include/asm/kasan.h
+++ b/arch/um/include/asm/kasan.h
@@ -24,11 +24,6 @@

#ifdef CONFIG_KASAN
void kasan_init(void);
-extern int kasan_um_is_ready;
-
-#ifdef CONFIG_STATIC_LINK
-#define kasan_arch_is_ready() (kasan_um_is_ready)
-#endif
#else
static inline void kasan_init(void) { }
#endif /* CONFIG_KASAN */
diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c
index 76bec7de81b..058cb70e330 100644
--- a/arch/um/kernel/mem.c
+++ b/arch/um/kernel/mem.c
@@ -21,9 +21,9 @@
#include <os.h>
#include <um_malloc.h>
#include <linux/sched/task.h>
+#include <linux/kasan.h>

#ifdef CONFIG_KASAN
-int kasan_um_is_ready;
void kasan_init(void)
{
/*
@@ -32,7 +32,7 @@ void kasan_init(void)
*/
kasan_map_memory((void *)KASAN_SHADOW_START, KASAN_SHADOW_SIZE);
init_task.kasan_depth = 0;
- kasan_um_is_ready = true;
+ kasan_init_generic();
}

static void (*kasan_init_ptr)(void)
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:14 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since x86 doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, and kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/x86/mm/kasan_init_64.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index 0539efd0d21..998b6010d6d 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -451,5 +451,5 @@ void __init kasan_init(void)
__flush_tlb_all();

init_task.kasan_depth = 0;

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:17 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since s390 doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, and kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

s390 sets up KASAN mappings in the decompressor and can run with KASAN
enabled from very early, so it doesn't need runtime control.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/s390/kernel/early.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 54cf0923050..7ada1324f6a 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <asm/asm-extable.h>
#include <linux/memblock.h>
+#include <linux/kasan.h>
#include <asm/access-regs.h>
#include <asm/asm-offsets.h>
#include <asm/machine.h>
@@ -65,7 +66,7 @@ static void __init kasan_early_init(void)
{
#ifdef CONFIG_KASAN
init_task.kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized\n");
+ kasan_init_generic();
#endif
}

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:20 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since riscv doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, and kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/riscv/mm/kasan_init.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c
index 41c635d6aca..ba2709b1eec 100644
--- a/arch/riscv/mm/kasan_init.c
+++ b/arch/riscv/mm/kasan_init.c
@@ -530,6 +530,7 @@ void __init kasan_init(void)

memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
init_task.kasan_depth = 0;
+ kasan_init_generic();

csr_write(CSR_SATP, PFN_DOWN(__pa(swapper_pg_dir)) | satp_mode);
local_flush_tlb_all();
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 17, 2025, 10:28:23 AM7/17/25
to h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
This patch completes:
1. Adding kasan_shadow_initialized() checks to existing wrapper functions
2. Replacing kasan_arch_is_ready() calls with kasan_shadow_initialized()
3. Creating wrapper functions for internal functions that need shadow
readiness checks
4. Removing the kasan_arch_is_ready() fallback definition

The two-level approach is now fully implemented:
- kasan_enabled() - controls whether KASAN is enabled at all.
(compile-time for most archs)
- kasan_shadow_initialized() - tracks shadow memory initialization
(static key for ARCH_DEFER_KASAN archs, compile-time for others)

This provides complete elimination of kasan_arch_is_ready() calls from
KASAN implementation while moving all shadow readiness logic to
wrapper functions.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v3:
- Addresses Andrey's feedback to move shadow checks to wrappers
- Rename kasan_arch_is_ready with kasan_shadow_initialized
- Added kasan_shadow_initialized() checks to all necessary wrapper functions
- Eliminated all remaining kasan_arch_is_ready() usage per reviewer guidance
---
include/linux/kasan.h | 36 +++++++++++++++++++++++++++---------
mm/kasan/common.c | 9 +++------
mm/kasan/generic.c | 12 +++---------
mm/kasan/kasan.h | 36 ++++++++++++++++++++++++++----------
mm/kasan/shadow.c | 32 +++++++-------------------------
5 files changed, 66 insertions(+), 59 deletions(-)

diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 51a8293d1af..292bd741d8d 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -194,7 +194,7 @@ bool __kasan_slab_pre_free(struct kmem_cache *s, void *object,
static __always_inline bool kasan_slab_pre_free(struct kmem_cache *s,
void *object)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
return __kasan_slab_pre_free(s, object, _RET_IP_);
return false;
}
@@ -229,7 +229,7 @@ static __always_inline bool kasan_slab_free(struct kmem_cache *s,
void *object, bool init,
bool still_accessible)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
return __kasan_slab_free(s, object, init, still_accessible);
return false;
}
@@ -237,7 +237,7 @@ static __always_inline bool kasan_slab_free(struct kmem_cache *s,
void __kasan_kfree_large(void *ptr, unsigned long ip);
static __always_inline void kasan_kfree_large(void *ptr)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
__kasan_kfree_large(ptr, _RET_IP_);
}

@@ -302,7 +302,7 @@ bool __kasan_mempool_poison_pages(struct page *page, unsigned int order,
static __always_inline bool kasan_mempool_poison_pages(struct page *page,
unsigned int order)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
return __kasan_mempool_poison_pages(page, order, _RET_IP_);
return true;
}
@@ -356,7 +356,7 @@ bool __kasan_mempool_poison_object(void *ptr, unsigned long ip);
*/
static __always_inline bool kasan_mempool_poison_object(void *ptr)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
return __kasan_mempool_poison_object(ptr, _RET_IP_);
return true;
}
@@ -568,11 +568,29 @@ static inline void kasan_init_hw_tags(void) { }
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)

void kasan_populate_early_vm_area_shadow(void *start, unsigned long size);
-int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
-void kasan_release_vmalloc(unsigned long start, unsigned long end,
+
+int __kasan_populate_vmalloc(unsigned long addr, unsigned long size);
+static inline int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
+{
+ if (!kasan_shadow_initialized())
+ return 0;
+ return __kasan_populate_vmalloc(addr, size);
+}
+
+void __kasan_release_vmalloc(unsigned long start, unsigned long end,
unsigned long free_region_start,
unsigned long free_region_end,
unsigned long flags);
+static inline void kasan_release_vmalloc(unsigned long start,
+ unsigned long end,
+ unsigned long free_region_start,
+ unsigned long free_region_end,
+ unsigned long flags)
+{
+ if (kasan_shadow_initialized())
+ __kasan_release_vmalloc(start, end, free_region_start,
+ free_region_end, flags);
+}

#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */

@@ -598,7 +616,7 @@ static __always_inline void *kasan_unpoison_vmalloc(const void *start,
unsigned long size,
kasan_vmalloc_flags_t flags)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
return __kasan_unpoison_vmalloc(start, size, flags);
return (void *)start;
}
@@ -607,7 +625,7 @@ void __kasan_poison_vmalloc(const void *start, unsigned long size);
static __always_inline void kasan_poison_vmalloc(const void *start,
unsigned long size)
{
- if (kasan_enabled())
+ if (kasan_enabled() && kasan_shadow_initialized())
__kasan_poison_vmalloc(start, size);
}

diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index c3a6446404d..b561734767d 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -259,7 +259,7 @@ static inline void poison_slab_object(struct kmem_cache *cache, void *object,
bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
unsigned long ip)
{
- if (!kasan_arch_is_ready() || is_kfence_address(object))
+ if (is_kfence_address(object))
return false;
return check_slab_allocation(cache, object, ip);
}
@@ -267,7 +267,7 @@ bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
bool still_accessible)
{
- if (!kasan_arch_is_ready() || is_kfence_address(object))
+ if (is_kfence_address(object))
return false;

poison_slab_object(cache, object, init, still_accessible);
@@ -291,9 +291,6 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,

static inline bool check_page_allocation(void *ptr, unsigned long ip)
{
- if (!kasan_arch_is_ready())
- return false;
-
if (ptr != page_address(virt_to_head_page(ptr))) {
kasan_report_invalid_free(ptr, ip, KASAN_REPORT_INVALID_FREE);
return true;
@@ -520,7 +517,7 @@ bool __kasan_mempool_poison_object(void *ptr, unsigned long ip)
return true;
}

- if (is_kfence_address(ptr) || !kasan_arch_is_ready())
+ if (is_kfence_address(ptr))
return true;

slab = folio_slab(folio);
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index 03b6d322ff6..1d20b925b9d 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -176,7 +176,7 @@ static __always_inline bool check_region_inline(const void *addr,
size_t size, bool write,
unsigned long ret_ip)
{
- if (!kasan_arch_is_ready())
+ if (!kasan_shadow_initialized())
return true;

if (unlikely(size == 0))
@@ -200,13 +200,10 @@ bool kasan_check_range(const void *addr, size_t size, bool write,
return check_region_inline(addr, size, write, ret_ip);
}

-bool kasan_byte_accessible(const void *addr)
+bool __kasan_byte_accessible(const void *addr)
{
s8 shadow_byte;

- if (!kasan_arch_is_ready())
- return true;
-
shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(addr));

return shadow_byte >= 0 && shadow_byte < KASAN_GRANULE_SIZE;
@@ -506,9 +503,6 @@ static void release_alloc_meta(struct kasan_alloc_meta *meta)

static void release_free_meta(const void *object, struct kasan_free_meta *meta)
{
- if (!kasan_arch_is_ready())
- return;
-
/* Check if free meta is valid. */
if (*(u8 *)kasan_mem_to_shadow(object) != KASAN_SLAB_FREE_META)
return;
@@ -573,7 +567,7 @@ void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags)
kasan_save_track(&alloc_meta->alloc_track, flags);
}

-void kasan_save_free_info(struct kmem_cache *cache, void *object)
+void __kasan_save_free_info(struct kmem_cache *cache, void *object)
{
struct kasan_free_meta *free_meta;

diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 129178be5e6..67a0a1095d2 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -398,7 +398,13 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags, depot_flags_t depot_flags);
void kasan_set_track(struct kasan_track *track, depot_stack_handle_t stack);
void kasan_save_track(struct kasan_track *track, gfp_t flags);
void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags);
-void kasan_save_free_info(struct kmem_cache *cache, void *object);
+
+void __kasan_save_free_info(struct kmem_cache *cache, void *object);
+static inline void kasan_save_free_info(struct kmem_cache *cache, void *object)
+{
+ if (kasan_enabled() && kasan_shadow_initialized())
+ __kasan_save_free_info(cache, object);
+}

#ifdef CONFIG_KASAN_GENERIC
bool kasan_quarantine_put(struct kmem_cache *cache, void *object);
@@ -499,6 +505,7 @@ static inline bool kasan_byte_accessible(const void *addr)

#else /* CONFIG_KASAN_HW_TAGS */

+void __kasan_poison(const void *addr, size_t size, u8 value, bool init);
/**
* kasan_poison - mark the memory range as inaccessible
* @addr: range start address, must be aligned to KASAN_GRANULE_SIZE
@@ -506,7 +513,11 @@ static inline bool kasan_byte_accessible(const void *addr)
* @value: value that's written to metadata for the range
* @init: whether to initialize the memory range (only for hardware tag-based)
*/
-void kasan_poison(const void *addr, size_t size, u8 value, bool init);
+static inline void kasan_poison(const void *addr, size_t size, u8 value, bool init)
+{
+ if (kasan_shadow_initialized())
+ __kasan_poison(addr, size, value, init);
+}

/**
* kasan_unpoison - mark the memory range as accessible
@@ -521,12 +532,19 @@ void kasan_poison(const void *addr, size_t size, u8 value, bool init);
*/
void kasan_unpoison(const void *addr, size_t size, bool init);

-bool kasan_byte_accessible(const void *addr);
+bool __kasan_byte_accessible(const void *addr);
+static inline bool kasan_byte_accessible(const void *addr)
+{
+ if (!kasan_shadow_initialized())
+ return true;
+ return __kasan_byte_accessible(addr);
+}

#endif /* CONFIG_KASAN_HW_TAGS */

#ifdef CONFIG_KASAN_GENERIC

+void __kasan_poison_last_granule(const void *address, size_t size);
/**
* kasan_poison_last_granule - mark the last granule of the memory range as
* inaccessible
@@ -536,7 +554,11 @@ bool kasan_byte_accessible(const void *addr);
* This function is only available for the generic mode, as it's the only mode
* that has partially poisoned memory granules.
*/
-void kasan_poison_last_granule(const void *address, size_t size);
+static inline void kasan_poison_last_granule(const void *address, size_t size)
+{
+ if (kasan_shadow_initialized())
+ __kasan_poison_last_granule(address, size);
+}

#else /* CONFIG_KASAN_GENERIC */

@@ -544,12 +566,6 @@ static inline void kasan_poison_last_granule(const void *address, size_t size) {

#endif /* CONFIG_KASAN_GENERIC */

-#ifndef kasan_arch_is_ready
-static inline bool kasan_arch_is_ready(void) { return true; }
-#elif !defined(CONFIG_KASAN_GENERIC) || !defined(CONFIG_KASAN_OUTLINE)
-#error kasan_arch_is_ready only works in KASAN generic outline mode!
-#endif
-
#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)

void kasan_kunit_test_suite_start(void);
diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index d2c70cd2afb..90c508cad63 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -121,13 +121,10 @@ void *__hwasan_memcpy(void *dest, const void *src, ssize_t len) __alias(__asan_m
EXPORT_SYMBOL(__hwasan_memcpy);
#endif

-void kasan_poison(const void *addr, size_t size, u8 value, bool init)
+void __kasan_poison(const void *addr, size_t size, u8 value, bool init)
{
void *shadow_start, *shadow_end;

- if (!kasan_arch_is_ready())
- return;
-
/*
* Perform shadow offset calculation based on untagged address, as
* some of the callers (e.g. kasan_poison_new_object) pass tagged
@@ -145,14 +142,11 @@ void kasan_poison(const void *addr, size_t size, u8 value, bool init)

__memset(shadow_start, value, shadow_end - shadow_start);
}
-EXPORT_SYMBOL_GPL(kasan_poison);
+EXPORT_SYMBOL_GPL(__kasan_poison);

#ifdef CONFIG_KASAN_GENERIC
-void kasan_poison_last_granule(const void *addr, size_t size)
+void __kasan_poison_last_granule(const void *addr, size_t size)
{
- if (!kasan_arch_is_ready())
- return;
-
if (size & KASAN_GRANULE_MASK) {
u8 *shadow = (u8 *)kasan_mem_to_shadow(addr + size);
*shadow = size & KASAN_GRANULE_MASK;
@@ -353,7 +347,7 @@ static int ___alloc_pages_bulk(struct page **pages, int nr_pages)
return 0;
}

-static int __kasan_populate_vmalloc(unsigned long start, unsigned long end)
+static int __kasan_populate_vmalloc_do(unsigned long start, unsigned long end)
{
unsigned long nr_pages, nr_total = PFN_UP(end - start);
struct vmalloc_populate_data data;
@@ -385,14 +379,11 @@ static int __kasan_populate_vmalloc(unsigned long start, unsigned long end)
return ret;
}

-int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
+int __kasan_populate_vmalloc(unsigned long addr, unsigned long size)
{
unsigned long shadow_start, shadow_end;
int ret;

- if (!kasan_arch_is_ready())
- return 0;
-
if (!is_vmalloc_or_module_addr((void *)addr))
return 0;

@@ -414,7 +405,7 @@ int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
shadow_start = PAGE_ALIGN_DOWN(shadow_start);
shadow_end = PAGE_ALIGN(shadow_end);

- ret = __kasan_populate_vmalloc(shadow_start, shadow_end);
+ ret = __kasan_populate_vmalloc_do(shadow_start, shadow_end);
if (ret)
return ret;

@@ -551,7 +542,7 @@ static int kasan_depopulate_vmalloc_pte(pte_t *ptep, unsigned long addr,
* pages entirely covered by the free region, we will not run in to any
* trouble - any simultaneous allocations will be for disjoint regions.
*/
-void kasan_release_vmalloc(unsigned long start, unsigned long end,
+void __kasan_release_vmalloc(unsigned long start, unsigned long end,
unsigned long free_region_start,
unsigned long free_region_end,
unsigned long flags)
@@ -560,9 +551,6 @@ void kasan_release_vmalloc(unsigned long start, unsigned long end,
unsigned long region_start, region_end;
unsigned long size;

- if (!kasan_arch_is_ready())
- return;
-
region_start = ALIGN(start, KASAN_MEMORY_PER_SHADOW_PAGE);
region_end = ALIGN_DOWN(end, KASAN_MEMORY_PER_SHADOW_PAGE);

@@ -611,9 +599,6 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
* with setting memory tags, so the KASAN_VMALLOC_INIT flag is ignored.
*/

- if (!kasan_arch_is_ready())
- return (void *)start;
-
if (!is_vmalloc_or_module_addr(start))
return (void *)start;

@@ -636,9 +621,6 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
*/
void __kasan_poison_vmalloc(const void *start, unsigned long size)
{
- if (!kasan_arch_is_ready())
- return;
-
if (!is_vmalloc_or_module_addr(start))
return;

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Jul 18, 2025, 4:05:54 AM7/18/25
to Andrew Morton, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, Peter Zijlstra, Johannes Berg
On Fri, Jul 18, 2025 at 3:10 AM Andrew Morton <ak...@linux-foundation.org> wrote:
>
> On Thu, 17 Jul 2025 19:27:21 +0500 Sabyrzhan Tasbolatov <snov...@gmail.com> wrote:
>
> > Introduce CONFIG_ARCH_DEFER_KASAN to identify architectures that need
> > to defer KASAN initialization until shadow memory is properly set up.
> >
> > Some architectures (like PowerPC with radix MMU) need to set up their
> > shadow memory mappings before KASAN can be safely enabled, while others
> > (like s390, x86, arm) can enable KASAN much earlier or even from the
> > beginning.
> >
> > This option allows us to:
> > 1. Use static keys only where needed (avoiding overhead)
> > 2. Use compile-time constants for arch that don't need runtime checks
> > 3. Maintain optimal performance for both scenarios
> >
> > Architectures that need deferred KASAN should select this option.
> > Architectures that can enable KASAN early will get compile-time
> > optimizations instead of runtime checks.
>
> Looks nice and appears quite mature. I'm reluctant to add it to mm.git
> during -rc6, especially given the lack of formal review and ack tags.
>
> But but but, that's what the mm-new branch is for. I guess I'll add it
> to get some additional exposure, but whether I'll advance it into
> mm-unstable/linux-next for this cycle is unclear.
>
> What do you (and others) think?

Thanks for the positive feedback!
Adding it to mm-new for additional exposure would be great.
Given the complexity of this cross-architecture change,
I think of taking the conservative approach of:
1. mm-new branch for exposure and review collection
2. Advancing to mm-unstable/linux-next only after we get proper acks from
KASAN maintainers/reviewers, at least.

The series has been thoroughly tested by me - compiled all affected arch and
ran QEMU on arm64, x86 with KUnits.

+ Forgot to add in CC Johannes Berg, Peter Zijlstra who commented in v1.
https://lore.kernel.org/all/20250625095224.1...@gmail.com/

Alexander Gordeev

unread,
Jul 18, 2025, 8:38:24 AM7/18/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
Acked-by: Alexander Gordeev <agor...@linux.ibm.com>

Alexander Gordeev

unread,
Jul 18, 2025, 8:39:05 AM7/18/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, ak...@linux-foundation.org, ryabin...@gmail.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
On Thu, Jul 17, 2025 at 07:27:21PM +0500, Sabyrzhan Tasbolatov wrote:
> Introduce CONFIG_ARCH_DEFER_KASAN to identify architectures that need
> to defer KASAN initialization until shadow memory is properly set up.
>
> Some architectures (like PowerPC with radix MMU) need to set up their
> shadow memory mappings before KASAN can be safely enabled, while others
> (like s390, x86, arm) can enable KASAN much earlier or even from the
> beginning.
>
> This option allows us to:
> 1. Use static keys only where needed (avoiding overhead)
> 2. Use compile-time constants for arch that don't need runtime checks
> 3. Maintain optimal performance for both scenarios
>
> Architectures that need deferred KASAN should select this option.
> Architectures that can enable KASAN early will get compile-time
> optimizations instead of runtime checks.
>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
> Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
> ---
> Changes in v3:
> - Introduced CONFIG_ARCH_DEFER_KASAN to control static key usage
> ---
> lib/Kconfig.kasan | 8 ++++++++
> 1 file changed, 8 insertions(+)

Acked-by: Alexander Gordeev <agor...@linux.ibm.com> # s390

Andrey Ryabinin

unread,
Jul 21, 2025, 6:59:48 PM7/21/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 7/17/25 4:27 PM, Sabyrzhan Tasbolatov wrote:

> === Testing with patches
>
> Testing in v3:
>
> - Compiled every affected arch with no errors:
>
> $ make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \
> OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \
> HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld \
> ARCH=$ARCH
>
> $ clang --version
> ClangBuiltLinux clang version 19.1.4
> Target: x86_64-unknown-linux-gnu
> Thread model: posix
>
> - make ARCH=um produces the warning during compiling:
> MODPOST Module.symvers
> WARNING: modpost: vmlinux: section mismatch in reference: \
> kasan_init+0x43 (section: .ltext) -> \
> kasan_init_generic (section: .init.text)
>
> AFAIU, it's due to the code in arch/um/kernel/mem.c, where kasan_init()
> is placed in own section ".kasan_init", which calls kasan_init_generic()
> which is marked with "__init".
>
> - Booting via qemu-system- and running KUnit tests:
>
> * arm64 (GENERIC, HW_TAGS, SW_TAGS): no regression, same above results.
> * x86_64 (GENERIC): no regression, no errors
>

It would be interesting to see whether ARCH_DEFER_KASAN=y arches work.
These series add static key into __asan_load*()/_store*() which are called
from everywhere, including the code patching static branches during the switch.

I have suspicion that the code patching static branches during static key switch
might not be prepared to the fact the current CPU might try to execute this static
branch in the middle of switch.

Andrey Ryabinin

unread,
Jul 21, 2025, 7:00:00 PM7/21/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 7/17/25 4:27 PM, Sabyrzhan Tasbolatov wrote:
This needs to be merged with the next patch where this option at least has some users.

Andrey Ryabinin

unread,
Jul 21, 2025, 7:00:07 PM7/21/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 7/17/25 4:27 PM, Sabyrzhan Tasbolatov wrote:
This is obviously broken for the HW_TAGS case. kasan_enable() does nothing,
and kasan_hw_tags_enabled() now always return true.

Andrey Ryabinin

unread,
Jul 21, 2025, 7:00:21 PM7/21/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 7/17/25 4:27 PM, Sabyrzhan Tasbolatov wrote:

This doesn't make sense, !kasan_enabled() is compile-time check which is always false here.

Andrey Ryabinin

unread,
Jul 21, 2025, 7:00:27 PM7/21/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 7/17/25 4:27 PM, Sabyrzhan Tasbolatov wrote:
I think this runs before jump_label_init(), and static keys shouldn't be switched before that.> }
>
> static void (*kasan_init_ptr)(void)

Andrey Ryabinin

unread,
Jul 21, 2025, 7:19:17 PM7/21/25
to Andrew Morton, Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 7/18/25 12:10 AM, Andrew Morton wrote:
> On Thu, 17 Jul 2025 19:27:21 +0500 Sabyrzhan Tasbolatov <snov...@gmail.com> wrote:
>
>> Introduce CONFIG_ARCH_DEFER_KASAN to identify architectures that need
>> to defer KASAN initialization until shadow memory is properly set up.
>>
>> Some architectures (like PowerPC with radix MMU) need to set up their
>> shadow memory mappings before KASAN can be safely enabled, while others
>> (like s390, x86, arm) can enable KASAN much earlier or even from the
>> beginning.
>>
>> This option allows us to:
>> 1. Use static keys only where needed (avoiding overhead)
>> 2. Use compile-time constants for arch that don't need runtime checks
>> 3. Maintain optimal performance for both scenarios
>>
>> Architectures that need deferred KASAN should select this option.
>> Architectures that can enable KASAN early will get compile-time
>> optimizations instead of runtime checks.
>
> Looks nice and appears quite mature. I'm reluctant to add it to mm.git
> during -rc6, especially given the lack of formal review and ack tags.
>
> But but but, that's what the mm-new branch is for. I guess I'll add it
> to get some additional exposure, but whether I'll advance it into
> mm-unstable/linux-next for this cycle is unclear.
>
> What do you (and others) think?

After looking a bit, it breaks UM and probably LoongArch too.
I'd say it needs more work and not ready even for mm-new.

Sabyrzhan Tasbolatov

unread,
Jul 22, 2025, 10:09:28 AM7/22/25
to Andrey Ryabinin, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
I should've used `!kasan_shadow_initialized()` check here which provides
the needed runtime behavior that kasan_early_stage used to provide.
Will do in v4. Thanks!

Sabyrzhan Tasbolatov

unread,
Jul 22, 2025, 10:17:25 AM7/22/25
to Andrey Ryabinin, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
On Tue, Jul 22, 2025 at 4:00 AM Andrey Ryabinin <ryabin...@gmail.com> wrote:
>
>
>
I got the warning in my local compilation and from kernel CI [1].

arch/um places kasan_init() in own `.kasan_init` section, while
kasan_init_generic() is called from __init.
Could you suggest a way how I can verify the functions call order?

I need to familiarize myself with how to run arch/um locally and try
to fix this warning.

[1] https://lore.kernel.org/all/CACzwLxicmky4CRdmABtN8m2c...@mail.gmail.com/

> >
> > static void (*kasan_init_ptr)(void)
>

Sabyrzhan Tasbolatov

unread,
Jul 22, 2025, 2:21:34 PM7/22/25
to Andrey Ryabinin, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
AFAIU, you're referring to this function in mm/kasan/generic.c:

static __always_inline bool check_region_inline(const void *addr,

size_t size, bool write,

unsigned long ret_ip)
{
if (!kasan_shadow_initialized())
return true;
...
}

and particularly, to architectures that selects ARCH_DEFER_KASAN=y, which are
loongarch, powerpc, um. So when these arch try to enable the static key:

1. static_branch_enable(&kasan_flag_enabled) called
2. Kernel patches code - changes jump instructions
3. Code patching involves memory writes
4. Memory writes can trigger any KASAN wrapper function
5. Wrapper calls kasan_shadow_initialized()
6. kasan_shadow_initialized() calls static_branch_likely(&kasan_flag_enabled)
7. This reads the static key being patched --- this is the potential issue?

The current runtime check is following in tis v3 patch series:

#ifdef CONFIG_ARCH_DEFER_KASAN
...
static __always_inline bool kasan_shadow_initialized(void)
{
return static_branch_likely(&kasan_flag_enabled);
}
...
#endif

I wonder, if I should add some protection only for KASAN_GENERIC,
where check_region_inline() is called (or for all KASAN modes?):

#ifdef CONFIG_ARCH_DEFER_KASAN
...
static __always_inline bool kasan_shadow_initialized(void)
{
/* Avoid recursion (?) during static key patching */
if (static_key_count(&kasan_flag_enabled.key) < 0)
return false;
return static_branch_likely(&kasan_flag_enabled);
}
...
#endif

Please suggest where the issue is and if I understood the problem.
I might try to run QEMU on powerpc with KUnits to see if I see any logs.

Andrey Ryabinin

unread,
Jul 23, 2025, 1:11:28 PM7/23/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
No, kasan_init() is in text section as the warning says. It's kasan_init_ptr in .kasan_init.
Adding __init to kasan_init() should fix the warning.


> Could you suggest a way how I can verify the functions call order?
>

By code inspection? or run uder gdb.

kasan_init() is initialization routine called before main().
jump_label_init() called from start_kernel()<-start_kernel_proc()<-... main()

> I need to familiarize myself with how to run arch/um locally

It's as simple as:
ARCH=um make
./linux rootfstype=hostfs ro init=/bin/bash

Andrey Ryabinin

unread,
Jul 23, 2025, 1:33:21 PM7/23/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
Yes, that's right.


> The current runtime check is following in tis v3 patch series:
>
> #ifdef CONFIG_ARCH_DEFER_KASAN
> ...
> static __always_inline bool kasan_shadow_initialized(void)
> {
> return static_branch_likely(&kasan_flag_enabled);
> }
> ...
> #endif
>
> I wonder, if I should add some protection only for KASAN_GENERIC,
> where check_region_inline() is called (or for all KASAN modes?):
>
> #ifdef CONFIG_ARCH_DEFER_KASAN
> ...
> static __always_inline bool kasan_shadow_initialized(void)
> {
> /* Avoid recursion (?) during static key patching */
> if (static_key_count(&kasan_flag_enabled.key) < 0)
> return false;
> return static_branch_likely(&kasan_flag_enabled);
> }
> ...
> #endif
>
> Please suggest where the issue is and if I understood the problem.

I don't know if it's a real problem or not. I'm just pointing out that we might
have tricky use case here and maybe that's a problem, because nobody had such use
case in mind. But maybe it's just fine.
I think we just need to boot test it, to see if this works.

> I might try to run QEMU on powerpc with KUnits to see if I see any logs.
powerpc used static key same way before your patches, so powerpc should be fine.

Sabyrzhan Tasbolatov

unread,
Aug 3, 2025, 3:27:43 PM8/3/25
to Andrey Ryabinin, Andrey Konovalov, Christophe Leroy, linuxp...@lists.ozlabs.org, h...@linux.ibm.com, ak...@linux-foundation.org, kasan-dev, Linux Kernel Mailing List, Linux Memory Management List
On Wed, Jul 23, 2025 at 10:33 PM Andrey Ryabinin <ryabin...@gmail.com> wrote:
>
> ...
>
> I don't know if it's a real problem or not. I'm just pointing out that we might
> have tricky use case here and maybe that's a problem, because nobody had such use
> case in mind. But maybe it's just fine.
> I think we just need to boot test it, to see if this works.
> ...
> powerpc used static key same way before your patches, so powerpc should be fine.

Hello,

Just heads up that I am still working on v4.
While I can verify the success on compile and booting with my changes
on x86, arm with SW/HW_TAGS modes, I'm having issues with PowerPC, UML
arch that selects ARCH_DEFER_KASAN.

Adding Christophe Leroy in TO. Please advise on the powerpc panic issue.

I started from scratch again, cherry picking changes one by one,
and verifying on powerpc which does select ARCH_DEFER_KASAN
and on arm64 with HW_TAGS on. While HW_TAGS works and boots
with the git diff [4], powerpc panics on botting, so I could've used the help,
while I'm trying to figure it out myself.

I know that PowerPC used the static key before and
I use the function to enable it and check it. Stuck in here.

TL;DR of upcoming v4 changes:

I've left the only check `kasan_enabled()` in include/linux/kasan-enabled.h
for the upcoming PATCH v4, removing kasan_shadow_initialized() from v3.
Because currently [1] in mainline, if HW_TAGS is enabled,
kasan_enabled() checks the static flag, otherwise IS_ENABLED(CONFIG_KASAN).

Andrey Konovalov mentioned in v2 to have a separate kasan_enabled()
to check if KASAN enabled at all and kasan_shadow_initialized() if
shadow memory is ready. AFAIU, the single kasan_enabled() covers both
scenarios separately in ARCH_DEFER_KASAN, and it doesn't need to
have a separate check in either case. Please correct me if I'm wrong.

[1] https://elixir.bootlin.com/linux/v6.16/source/include/linux/kasan-enabled.h#L11

Also kasan_arch_is_ready() is replaced with kasan_enabled() as I did in v1-v2,
since we control runtime/compile time checks via the single function.

#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
static __always_inline bool kasan_enabled(void)
{
return static_branch_likely(&kasan_flag_enabled);
}
...

== UML issue

Currently [2], UML uses integer flag, so adding ARCH_DEFER_KASAN
which relies on CONFIG_STATIC_LINK to arch/um/Kconfig seems problematic, or no?
I was trying to boot the ./linux ARCH=um and replacing ".kasan_init"
section with
__init kasan_init

and calling kasan_init() at the end of `__init arch_mm_preinit()` - I
got segfault,
couldn't get help from gdb or strace. I guess the patching just didn't work.

[2] https://elixir.bootlin.com/linux/v6.16/source/arch/um/kernel/mem.c#L35

#ifdef CONFIG_KASAN
void __init kasan_init(void)
{
/*
* kasan_map_memory will map all of the required address space and
* the host machine will allocate physical memory as necessary.
*/
kasan_map_memory((void *)KASAN_SHADOW_START, KASAN_SHADOW_SIZE);
init_task.kasan_depth = 0;
kasan_init_generic();
}
#endif

and removed ".kasan_init" section from:
arch/um/include/asm/common.lds.S
arch/um/kernel/dyn.lds.S

So not sure if I should actually keep kasan_arch_is_ready for UML code
that can not
use kasan_enabled() as STATIC_LINK is not enabled in UML defconfig.

== PowerPC panic

I've following this instruction to compile properly kernel for powerpc
https://github.com/linuxppc/wiki/wiki/Building-powerpc-kernels,

because compiling with clang and defconfig didn't boot the QEMU.

export CROSS_COMPILE=powerpc64le-linux-gnu-
export ARCH=powerpc
make ppc64le_defconfig

Running it in QEMU, I'm currently receiving the kernel panic [3].
**Without** git diff below [4], the QEMU boots with KASAN successfully.

I'm trying to understand the root-cause:
ftrace_process_locs -> ftrace_init -> start_kernel()
AFAIU, some code before kasan_init() is trying to do an instrumented
access(store/load) that ends up in __asan_*,
but shadow memory is not mapped yet.

My guess is that the issue is how we prepare the shadow mem.
differently in PPC_BOOK3S_64:
arch/powerpc/mm/kasan/init_book3s_64.c
and in PPC_BOOK3E_64:
arch/powerpc/mm/kasan/init_book3e_64.c

Like BOOK3S requires RADIX_MMU,
so I can't verify ARCH_DEFER_KASAN on BOOK3E.



[3]
$ ./run-qemu-powerpc.sh ./linux-kasan
+ IMAGE=./images/rootfs-el.cpio.gz
+ KERNEL=./linux-kasan
+ qemu-system-ppc64 -M pseries -m 1G -nographic -vga none -kernel
./linux-kasan/vmlinux -initrd ./images/rootfs-el.cpio.gz -append
noreboot -device e1000e,netdev=nic -netdev user,id=nic -s -pidfile
vm.pid
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-cfpc=workaround
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-sbbc=workaround
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-ibs=workaround
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-ccf-assist=on


SLOF **********************************************************************
QEMU Starting
Build Date = Mar 5 2025 10:48:26
FW Version = release 20210711
Press "s" to enter Open Firmware.

Populating /vdevice methods
Populating /vdevice/vty@71000000
Populating /vdevice/nvram@71000001
Populating /vdevice/v-scsi@71000002
SCSI: Looking for devices
8200000000000000 CD-ROM : "QEMU QEMU CD-ROM 2.5+"
Populating /pci@800000020000000
00 0000 (D) : 8086 10d3 network [ ethernet ]
No NVRAM common partition, re-initializing...
Scanning USB
Using default console: /vdevice/vty@71000000
Detected RAM kernel at 400000 (497c180 bytes)

Welcome to Open Firmware

Copyright (c) 2004, 2017 IBM Corporation All rights reserved.
This program and the accompanying materials are made available
under the terms of the BSD License available at
http://www.opensource.org/licenses/bsd-license.php

Booting from memory...
OF stdout device is: /vdevice/vty@71000000
Preparing to boot Linux version 6.16.0-g9a9f7176fa9d-dirty
(novitoll@PC) (powerpc64le-linux-gnu-gcc (Ubuntu
11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38)
#64 SMP Sun Aug 3 19:58:23 +05 2025
Detected machine type: 0000000000000101
command line: noreboot
Max number of cores passed to firmware: 2048 (NR_CPUS = 2048)
Calling ibm,client-architecture-support... done
memory layout at init:
memory_limit : 0000000000000000 (16 MB aligned)
alloc_bottom : 00000000053b0000
alloc_top : 0000000030000000
alloc_top_hi : 0000000040000000
rmo_top : 0000000030000000
ram_top : 0000000040000000
instantiating rtas at 0x000000002fff0000... done
prom_hold_cpus: skipped
copying OF device tree...
Building dt strings...
Building dt structure...
Device tree strings 0x00000000053c0000 -> 0x00000000053c0b78
Device tree struct 0x00000000053d0000 -> 0x00000000053e0000
Quiescing Open Firmware ...
Booting Linux via __start() @ 0x0000000000400000 ...
[ 0.000000][ T0] radix-mmu: Page sizes from device-tree:
[ 0.000000][ T0] radix-mmu: Page size shift = 12 AP=0x0
[ 0.000000][ T0] radix-mmu: Page size shift = 16 AP=0x5
[ 0.000000][ T0] radix-mmu: Page size shift = 21 AP=0x1
[ 0.000000][ T0] radix-mmu: Page size shift = 30 AP=0x2
[ 0.000000][ T0] Activating Kernel Userspace Access Prevention
[ 0.000000][ T0] Activating Kernel Userspace Execution Prevention
[ 0.000000][ T0] radix-mmu: Mapped
0x0000000000000000-0x0000000003e00000 with 2.00 MiB pages (exec)
[ 0.000000][ T0] radix-mmu: Mapped
0x0000000003e00000-0x0000000040000000 with 2.00 MiB pages
[ 0.000000][ T0] lpar: Using radix MMU under hypervisor
[ 0.000000][ T0] Linux version 6.16.0-g9a9f7176fa9d-dirty
(novitoll@PC) (powerpc64le-linux-gnu-gcc (Ubuntu
11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38)
#64 SMP Sun Aug 3 19:58:23 +05 2025
[ 0.000000][ T0] KernelAddressSanitizer initialized (generic)
[ 0.000000][ T0] OF: reserved mem: Reserved memory: No
reserved-memory node in the DT
[ 0.000000][ T0] Found initrd at 0xc000000004d90000:0xc0000000053ad15a
[ 0.000000][ T0] Hardware name: IBM pSeries (emulated by qemu)
POWER9 (architected) 0x4e1200 0xf000005 of:SLOF,HEAD pSeries
[ 0.000000][ T0] printk: legacy bootconsole [udbg0] enabled
[ 0.000000][ T0] Partition configured for 1 cpus.
[ 0.000000][ T0] CPU maps initialized for 1 thread per core
[ 0.000000][ T0] numa: Partition configured for 1 NUMA nodes.
[ 0.000000][ T0] -----------------------------------------------------
[ 0.000000][ T0] phys_mem_size = 0x40000000
[ 0.000000][ T0] dcache_bsize = 0x80
[ 0.000000][ T0] icache_bsize = 0x80
[ 0.000000][ T0] cpu_features = 0x0001c06b8f4f9187
[ 0.000000][ T0] possible = 0x003ffbfbffffb18f
[ 0.000000][ T0] always = 0x0000000000000180
[ 0.000000][ T0] cpu_user_features = 0xdc0065c2 0xaef00000
[ 0.000000][ T0] mmu_features = 0x3c007641
[ 0.000000][ T0] firmware_features = 0x00000285455a445f
[ 0.000000][ T0] vmalloc start = 0xc008000000000000
[ 0.000000][ T0] IO start = 0xc00a000000000000
[ 0.000000][ T0] vmemmap start = 0xc00c000000000000
[ 0.000000][ T0] -----------------------------------------------------
[ 0.000000][ T0] NODE_DATA(0) allocated [mem 0x3fb28800-0x3fb2ffff]
[ 0.000000][ T0] rfi-flush: fallback displacement flush available
[ 0.000000][ T0] rfi-flush: ori type flush available
[ 0.000000][ T0] rfi-flush: mttrig type flush available
[ 0.000000][ T0] count-cache-flush: hardware flush enabled.
[ 0.000000][ T0] link-stack-flush: software flush enabled.
[ 0.000000][ T0] stf-barrier: eieio barrier available
[ 0.000000][ T0] PPC64 nvram contains 65536 bytes
[ 0.000000][ T0] barrier-nospec: using ORI speculation barrier
[ 0.000000][ T0] Zone ranges:
[ 0.000000][ T0] Normal [mem 0x0000000000000000-0x000000003fffffff]
[ 0.000000][ T0] Device empty
[ 0.000000][ T0] Movable zone start for each node
[ 0.000000][ T0] Early memory node ranges
[ 0.000000][ T0] node 0: [mem 0x0000000000000000-0x000000003fffffff]
[ 0.000000][ T0] Initmem setup node 0 [mem
0x0000000000000000-0x000000003fffffff]
[ 0.000000][ T0] percpu: Embedded 3 pages/cpu s122904 r0 d73704 u196608
[ 0.000000][ T0] Kernel command line: noreboot
[ 0.000000][ T0] Unknown kernel command line parameters
"noreboot", will be passed to user space.
[ 0.000000][ T0] printk: log buffer data + meta data: 262144 +
917504 = 1179648 bytes
[ 0.000000][ T0] Dentry cache hash table entries: 131072 (order:
4, 1048576 bytes, linear)
[ 0.000000][ T0] Inode-cache hash table entries: 65536 (order:
3, 524288 bytes, linear)
[ 0.000000][ T0] Fallback order for Node 0: 0
[ 0.000000][ T0] Built 1 zonelists, mobility grouping on. Total
pages: 16384
[ 0.000000][ T0] Policy zone: Normal
[ 0.000000][ T0] mem auto-init: stack:off, heap alloc:off, heap free:off
[ 0.000000][ T0] stackdepot: allocating hash table via
alloc_large_system_hash
[ 0.000000][ T0] stackdepot hash table entries: 1048576 (order:
8, 16777216 bytes, linear)
[ 0.000000][ T0] SLUB: HWalign=128, Order=0-3, MinObjects=0,
CPUs=1, Nodes=1
[ 0.000000][ T0] BUG: Unable to handle kernel data access on
read at 0xc00e0000a83f4ff0
[ 0.000000][ T0] Faulting instruction address: 0xc000000000761aec
[ 0.000000][ T0] Oops: Kernel access of bad area, sig: 11 [#1]
[ 0.000000][ T0] BE PAGE_SIZE=64K MMU=Radix SMP NR_CPUS=2048 NUMA pSeries
[ 0.000000][ T0] Modules linked in:
[ 0.000000][ T0] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted
6.16.0-g9a9f7176fa9d-dirty #64 VOLUNTARY
[ 0.000000][ T0] Hardware name: IBM pSeries (emulated by qemu)
POWER9 (architected) 0x4e1200 0xf000005 of:SLOF,HEAD pSeries
[ 0.000000][ T0] NIP: c000000000761aec LR: c0000000003fa48c
CTR: c000000000302db4
[ 0.000000][ T0] REGS: c000000004687b20 TRAP: 0300 Not tainted
(6.16.0-g9a9f7176fa9d-dirty)
[ 0.000000][ T0] MSR: 8000000002001032 <SF,VEC,ME,IR,DR,RI>
CR: 24082424 XER: 00000000
[ 0.000000][ T0] CFAR: c0000000003fa488 DAR: c00e0000a83f4ff0
DSISR: 40000000 IRQMASK: 3
[ 0.000000][ T0] GPR00: c0000000003fa48c c000000004687dc0
c00000000230a500 c000000541fa7f80
[ 0.000000][ T0] GPR04: 0000000000000008 0000000000000001
c0000000019cf044 0000000000000001
[ 0.000000][ T0] GPR08: a80e000000000000 18000000a83f4ff0
0000000000000007 a80e000000000000
[ 0.000000][ T0] GPR12: c00e0000008d0fb6 c000000004980000
0000000000000002 0000000002579c28
[ 0.000000][ T0] GPR16: c0000000040824c0 000000000000000c
c000000006a80000 c00000000000e9f8
[ 0.000000][ T0] GPR20: 0000000000010000 c0000000031e4568
c000000004840860 0000000000000000
[ 0.000000][ T0] GPR24: 000000000000b9f4 0000000053b527f9
0000000000000000 c0000000031875d0
[ 0.000000][ T0] GPR28: 000000053b527f80 c0000000068d0340
c0000000068d0380 0000000000000000
[ 0.000000][ T0] NIP [c000000000761aec] __asan_store8+0x60/0xe0
[ 0.000000][ T0] LR [c0000000003fa48c] ftrace_process_locs+0x3c0/0xa20
[ 0.000000][ T0] Call Trace:
[ 0.000000][ T0] [c000000004687dc0] [0000000000000002] 0x2 (unreliable)
[ 0.000000][ T0] [c000000004687de0] [c0000000003fa48c]
ftrace_process_locs+0x3c0/0xa20
[ 0.000000][ T0] [c000000004687ee0] [c000000003065c14]
ftrace_init+0x68/0x1cc
[ 0.000000][ T0] [c000000004687f50] [c00000000300a57c]
start_kernel+0x1d8/0x4e0
[ 0.000000][ T0] [c000000004687fe0] [c00000000000da4c]
start_here_common+0x1c/0x20
[ 0.000000][ T0] Code: 792907c6 6529ffff 6129ffff 7c234840
4081005c 39230007 792a0760 2c2a0007 40820034 3d00a80e 7929e8c2
790807c6 <7d2940ae> 7d290775 4082006c 38210020
[ 0.000000][ T0] ---[ end trace 0000000000000000 ]---
[ 0.000000][ T0]
[ 0.000000][ T0] Kernel panic - not syncing: Attempted to kill
the idle task!
qemu-system-ppc64: OS terminated: `



[4]
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/kasan.h | 13 ----------
arch/powerpc/mm/kasan/init_32.c | 2 +-
arch/powerpc/mm/kasan/init_book3e_64.c | 2 +-
arch/powerpc/mm/kasan/init_book3s_64.c | 6 +----
include/linux/kasan-enabled.h | 24 +++++++++++++----
include/linux/kasan.h | 28 ++++++++++++++++++--
lib/Kconfig.kasan | 8 ++++++
mm/kasan/common.c | 18 ++++++++-----
mm/kasan/generic.c | 23 +++++++++-------
mm/kasan/hw_tags.c | 9 +------
mm/kasan/kasan.h | 36 +++++++++++++++++++-------
mm/kasan/shadow.c | 32 +++++------------------
mm/kasan/sw_tags.c | 2 ++
mm/kasan/tags.c | 2 +-
15 files changed, 120 insertions(+), 86 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 93402a1d9c9..11c8ef2d88e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -122,6 +122,7 @@ config PPC
# Please keep this list sorted alphabetically.
#
select ARCH_32BIT_OFF_T if PPC32
+ select ARCH_DEFER_KASAN if PPC_RADIX_MMU
select ARCH_DISABLE_KASAN_INLINE if PPC_RADIX_MMU
select ARCH_DMA_DEFAULT_COHERENT if !NOT_COHERENT_CACHE
select ARCH_ENABLE_MEMORY_HOTPLUG
diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h
index b5bbb94c51f..73466d3ff30 100644
--- a/arch/powerpc/include/asm/kasan.h
+++ b/arch/powerpc/include/asm/kasan.h
@@ -53,19 +53,6 @@
#endif
#ifdef CONFIG_KASAN
-#ifdef CONFIG_PPC_BOOK3S_64
-DECLARE_STATIC_KEY_FALSE(powerpc_kasan_enabled_key);
-
-static __always_inline bool kasan_arch_is_ready(void)
-{
- if (static_branch_likely(&powerpc_kasan_enabled_key))
- return true;
- return false;
-}
-
-#define kasan_arch_is_ready kasan_arch_is_ready
-#endif
-
void kasan_early_init(void);
void kasan_mmu_init(void);
void kasan_init(void);
diff --git a/arch/powerpc/mm/kasan/init_32.c b/arch/powerpc/mm/kasan/init_32.c
index 03666d790a5..1d083597464 100644
--- a/arch/powerpc/mm/kasan/init_32.c
+++ b/arch/powerpc/mm/kasan/init_32.c
@@ -165,7 +165,7 @@ void __init kasan_init(void)
/* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KASAN init done\n");
+ kasan_init_generic();
}
void __init kasan_late_init(void)
diff --git a/arch/powerpc/mm/kasan/init_book3e_64.c
b/arch/powerpc/mm/kasan/init_book3e_64.c
index 60c78aac0f6..0d3a73d6d4b 100644
--- a/arch/powerpc/mm/kasan/init_book3e_64.c
+++ b/arch/powerpc/mm/kasan/init_book3e_64.c
@@ -127,7 +127,7 @@ void __init kasan_init(void)
/* Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KASAN init done\n");
+ kasan_init_generic();
}
void __init kasan_late_init(void) { }
diff --git a/arch/powerpc/mm/kasan/init_book3s_64.c
b/arch/powerpc/mm/kasan/init_book3s_64.c
index 7d959544c07..dcafa641804 100644
--- a/arch/powerpc/mm/kasan/init_book3s_64.c
+++ b/arch/powerpc/mm/kasan/init_book3s_64.c
@@ -19,8 +19,6 @@
#include <linux/memblock.h>
#include <asm/pgalloc.h>
-DEFINE_STATIC_KEY_FALSE(powerpc_kasan_enabled_key);
-
static void __init kasan_init_phys_region(void *start, void *end)
{
unsigned long k_start, k_end, k_cur;
@@ -92,11 +90,9 @@ void __init kasan_init(void)
*/
memset(kasan_early_shadow_page, 0, PAGE_SIZE);
- static_branch_inc(&powerpc_kasan_enabled_key);
-
/* Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KASAN init done\n");
+ kasan_init_generic();
}
void __init kasan_early_init(void) { }
diff --git a/include/linux/kasan-enabled.h b/include/linux/kasan-enabled.h
index 6f612d69ea0..32db31b6418 100644
--- a/include/linux/kasan-enabled.h
+++ b/include/linux/kasan-enabled.h
@@ -4,32 +4,46 @@
#include <linux/static_key.h>
-#ifdef CONFIG_KASAN_HW_TAGS
-
+#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
+/*
+ * Global runtime flag for KASAN modes that need runtime control.
+ * Used by ARCH_DEFER_KASAN architectures and HW_TAGS mode.
+ */
DECLARE_STATIC_KEY_FALSE(kasan_flag_enabled);
+/*
+ * Runtime control for shadow memory initialization or HW_TAGS mode.
+ * Uses static key for architectures that need deferred KASAN or HW_TAGS.
+ */
static __always_inline bool kasan_enabled(void)
{
return static_branch_likely(&kasan_flag_enabled);
}
+static inline void kasan_enable(void)
+{
+ static_branch_enable(&kasan_flag_enabled);
+}
+
static inline bool kasan_hw_tags_enabled(void)
{
return kasan_enabled();
}
+#else
-#else /* CONFIG_KASAN_HW_TAGS */
-
+/* Controls whether KASAN is enabled at all (compile-time check). */
static inline bool kasan_enabled(void)
{
return IS_ENABLED(CONFIG_KASAN);
}
+static inline void kasan_enable(void) { }
+
static inline bool kasan_hw_tags_enabled(void)
{
return false;
}
-#endif /* CONFIG_KASAN_HW_TAGS */
+#endif /* CONFIG_ARCH_DEFER_KASAN || CONFIG_KASAN_HW_TAGS */
#endif /* LINUX_KASAN_ENABLED_H */
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 890011071f2..8bf0efbb751 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -543,6 +543,12 @@ void kasan_report_async(void);
#endif /* CONFIG_KASAN_HW_TAGS */
+#ifdef CONFIG_KASAN_GENERIC
+void __init kasan_init_generic(void);
+#else
+static inline void kasan_init_generic(void) { }
+#endif
+
#ifdef CONFIG_KASAN_SW_TAGS
void __init kasan_init_sw_tags(void);
#else
@@ -562,11 +568,29 @@ static inline void kasan_init_hw_tags(void) { }
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
void kasan_populate_early_vm_area_shadow(void *start, unsigned long size);
-int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
-void kasan_release_vmalloc(unsigned long start, unsigned long end,
+
+int __kasan_populate_vmalloc(unsigned long addr, unsigned long size);
+static inline int kasan_populate_vmalloc(unsigned long addr, unsigned
long size)
+{
+ if (!kasan_enabled())
+ return 0;
+ return __kasan_populate_vmalloc(addr, size);
+}
+
+void __kasan_release_vmalloc(unsigned long start, unsigned long end,
unsigned long free_region_start,
unsigned long free_region_end,
unsigned long flags);
+static inline void kasan_release_vmalloc(unsigned long start,
+ unsigned long end,
+ unsigned long free_region_start,
+ unsigned long free_region_end,
+ unsigned long flags)
+{
+ if (kasan_enabled())
+ __kasan_release_vmalloc(start, end, free_region_start,
+ free_region_end, flags);
+}
#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index f82889a830f..4e0508022ce 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -49,6 +49,14 @@ menuconfig KASAN
if KASAN
+config ARCH_DEFER_KASAN
+ bool
+ help
+ Architectures should select this if they need to defer KASAN
+ initialization until shadow memory is properly set up. This
+ enables runtime control via static keys. Otherwise, KASAN uses
+ compile-time constants for better performance.
+
config CC_HAS_KASAN_MEMINTRINSIC_PREFIX
def_bool (CC_IS_CLANG && $(cc-option,-fsanitize=kernel-address -mllvm
-asan-kernel-mem-intrinsic-prefix=1)) || \
(CC_IS_GCC && $(cc-option,-fsanitize=kernel-address --param
asan-kernel-mem-intrinsic-prefix=1))
diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index ed4873e18c7..dff5f7bfad1 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -32,6 +32,15 @@
#include "kasan.h"
#include "../slab.h"
+#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
+/*
+ * Definition of the unified static key declared in kasan-enabled.h.
+ * This provides consistent runtime enable/disable across KASAN modes.
+ */
+DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled);
+EXPORT_SYMBOL(kasan_flag_enabled);
+#endif
+
struct slab *kasan_addr_to_slab(const void *addr)
{
if (virt_addr_valid(addr))
@@ -250,7 +259,7 @@ static inline void poison_slab_object(struct
kmem_cache *cache, void *object,
bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
unsigned long ip)
{
- if (!kasan_arch_is_ready() || is_kfence_address(object))
+ if (is_kfence_address(object))
return false;
return check_slab_allocation(cache, object, ip);
}
@@ -258,7 +267,7 @@ bool __kasan_slab_pre_free(struct kmem_cache
*cache, void *object,
bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
bool still_accessible)
{
- if (!kasan_arch_is_ready() || is_kfence_address(object))
+ if (is_kfence_address(object))
return false;
poison_slab_object(cache, object, init, still_accessible);
@@ -282,9 +291,6 @@ bool __kasan_slab_free(struct kmem_cache *cache,
void *object, bool init,
static inline bool check_page_allocation(void *ptr, unsigned long ip)
{
- if (!kasan_arch_is_ready())
- return false;
-
if (ptr != page_address(virt_to_head_page(ptr))) {
kasan_report_invalid_free(ptr, ip, KASAN_REPORT_INVALID_FREE);
return true;
@@ -511,7 +517,7 @@ bool __kasan_mempool_poison_object(void *ptr,
unsigned long ip)
return true;
}
- if (is_kfence_address(ptr) || !kasan_arch_is_ready())
+ if (is_kfence_address(ptr))
return true;
slab = folio_slab(folio);
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index d54e89f8c3e..8e597cf8fdd 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -36,6 +36,17 @@
#include "kasan.h"
#include "../slab.h"
+/*
+ * Initialize Generic KASAN and enable runtime checks.
+ * This should be called from arch kasan_init() once shadow memory is ready.
+ */
+void __init kasan_init_generic(void)
+{
+ kasan_enable();
+
+ pr_info("KernelAddressSanitizer initialized (generic)\n");
+}
+
/*
* All functions below always inlined so compiler could
* perform better optimizations in each of __asan_loadX/__assn_storeX
@@ -165,7 +176,7 @@ static __always_inline bool
check_region_inline(const void *addr,
size_t size, bool write,
unsigned long ret_ip)
{
- if (!kasan_arch_is_ready())
+ if (!kasan_enabled())
return true;
if (unlikely(size == 0))
@@ -189,13 +200,10 @@ bool kasan_check_range(const void *addr, size_t
size, bool write,
return check_region_inline(addr, size, write, ret_ip);
}
-bool kasan_byte_accessible(const void *addr)
+bool __kasan_byte_accessible(const void *addr)
{
s8 shadow_byte;
- if (!kasan_arch_is_ready())
- return true;
-
shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(addr));
return shadow_byte >= 0 && shadow_byte < KASAN_GRANULE_SIZE;
@@ -495,9 +503,6 @@ static void release_alloc_meta(struct
kasan_alloc_meta *meta)
static void release_free_meta(const void *object, struct kasan_free_meta *meta)
{
- if (!kasan_arch_is_ready())
- return;
-
/* Check if free meta is valid. */
if (*(u8 *)kasan_mem_to_shadow(object) != KASAN_SLAB_FREE_META)
return;
@@ -562,7 +567,7 @@ void kasan_save_alloc_info(struct kmem_cache
*cache, void *object, gfp_t flags)
kasan_save_track(&alloc_meta->alloc_track, flags);
}
-void kasan_save_free_info(struct kmem_cache *cache, void *object)
+void __kasan_save_free_info(struct kmem_cache *cache, void *object)
{
struct kasan_free_meta *free_meta;
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index 9a6927394b5..c8289a3feab 100644
--- a/mm/kasan/hw_tags.c
+++ b/mm/kasan/hw_tags.c
@@ -45,13 +45,6 @@ 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;
-/*
- * Whether KASAN is enabled at all.
- * The value remains false until KASAN is initialized by kasan_init_hw_tags().
- */
-DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled);
-EXPORT_SYMBOL(kasan_flag_enabled);
-
/*
* Whether the selected mode is synchronous, asynchronous, or asymmetric.
* Defaults to KASAN_MODE_SYNC.
@@ -260,7 +253,7 @@ void __init kasan_init_hw_tags(void)
kasan_init_tags();
/* KASAN is now initialized, enable it. */
- static_branch_enable(&kasan_flag_enabled);
+ kasan_enable();
pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s,
vmalloc=%s, stacktrace=%s)\n",
kasan_mode_info(),
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 129178be5e6..ecb0dccfca9 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -398,7 +398,13 @@ depot_stack_handle_t kasan_save_stack(gfp_t
flags, depot_flags_t depot_flags);
void kasan_set_track(struct kasan_track *track, depot_stack_handle_t stack);
void kasan_save_track(struct kasan_track *track, gfp_t flags);
void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags);
-void kasan_save_free_info(struct kmem_cache *cache, void *object);
+
+void __kasan_save_free_info(struct kmem_cache *cache, void *object);
+static inline void kasan_save_free_info(struct kmem_cache *cache, void *object)
+{
+ if (kasan_enabled())
+ __kasan_save_free_info(cache, object);
+}
#ifdef CONFIG_KASAN_GENERIC
bool kasan_quarantine_put(struct kmem_cache *cache, void *object);
@@ -499,6 +505,7 @@ static inline bool kasan_byte_accessible(const void *addr)
#else /* CONFIG_KASAN_HW_TAGS */
+void __kasan_poison(const void *addr, size_t size, u8 value, bool init);
/**
* kasan_poison - mark the memory range as inaccessible
* @addr: range start address, must be aligned to KASAN_GRANULE_SIZE
@@ -506,7 +513,11 @@ static inline bool kasan_byte_accessible(const void *addr)
* @value: value that's written to metadata for the range
* @init: whether to initialize the memory range (only for hardware tag-based)
*/
-void kasan_poison(const void *addr, size_t size, u8 value, bool init);
+static inline void kasan_poison(const void *addr, size_t size, u8
value, bool init)
+{
+ if (kasan_enabled())
+ __kasan_poison(addr, size, value, init);
+}
/**
* kasan_unpoison - mark the memory range as accessible
@@ -521,12 +532,19 @@ void kasan_poison(const void *addr, size_t size,
u8 value, bool init);
*/
void kasan_unpoison(const void *addr, size_t size, bool init);
-bool kasan_byte_accessible(const void *addr);
+bool __kasan_byte_accessible(const void *addr);
+static inline bool kasan_byte_accessible(const void *addr)
+{
+ if (!kasan_enabled())
+ return true;
+ return __kasan_byte_accessible(addr);
+}
#endif /* CONFIG_KASAN_HW_TAGS */
#ifdef CONFIG_KASAN_GENERIC
+void __kasan_poison_last_granule(const void *address, size_t size);
/**
* kasan_poison_last_granule - mark the last granule of the memory range as
* inaccessible
@@ -536,7 +554,11 @@ bool kasan_byte_accessible(const void *addr);
* This function is only available for the generic mode, as it's the only mode
* that has partially poisoned memory granules.
*/
-void kasan_poison_last_granule(const void *address, size_t size);
+static inline void kasan_poison_last_granule(const void *address, size_t size)
+{
+ if (kasan_enabled())
diff --git a/mm/kasan/sw_tags.c b/mm/kasan/sw_tags.c
index b9382b5b6a3..275bcbbf612 100644
--- a/mm/kasan/sw_tags.c
+++ b/mm/kasan/sw_tags.c
@@ -45,6 +45,8 @@ void __init kasan_init_sw_tags(void)
kasan_init_tags();
+ kasan_enable();
+
pr_info("KernelAddressSanitizer initialized (sw-tags, stacktrace=%s)\n",
str_on_off(kasan_stack_collection_enabled()));
}
diff --git a/mm/kasan/tags.c b/mm/kasan/tags.c
index d65d48b85f9..b9f31293622 100644
--- a/mm/kasan/tags.c
+++ b/mm/kasan/tags.c
@@ -142,7 +142,7 @@ void kasan_save_alloc_info(struct kmem_cache
*cache, void *object, gfp_t flags)
save_stack_info(cache, object, flags, false);
}
-void kasan_save_free_info(struct kmem_cache *cache, void *object)
+void __kasan_save_free_info(struct kmem_cache *cache, void *object)
{
save_stack_info(cache, object, 0, true);
}

--
2.34.1

Christophe Leroy

unread,
Aug 4, 2025, 8:20:36 AM8/4/25
to Sabyrzhan Tasbolatov, Andrey Ryabinin, Andrey Konovalov, linuxp...@lists.ozlabs.org, h...@linux.ibm.com, ak...@linux-foundation.org, kasan-dev, Linux Kernel Mailing List, Linux Memory Management List
Hi,

Le 03/08/2025 à 21:27, Sabyrzhan Tasbolatov a écrit :
> On Wed, Jul 23, 2025 at 10:33 PM Andrey Ryabinin <ryabin...@gmail.com> wrote:
>>
>> ...
>>
>> I don't know if it's a real problem or not. I'm just pointing out that we might
>> have tricky use case here and maybe that's a problem, because nobody had such use
>> case in mind. But maybe it's just fine.
>> I think we just need to boot test it, to see if this works.
>> ...
>> powerpc used static key same way before your patches, so powerpc should be fine.
>
> Hello,
>
> Just heads up that I am still working on v4.
> While I can verify the success on compile and booting with my changes
> on x86, arm with SW/HW_TAGS modes, I'm having issues with PowerPC, UML
> arch that selects ARCH_DEFER_KASAN.
>
> Adding Christophe Leroy in TO. Please advise on the powerpc panic issue.

I don't have the problem you report. Is it with your v3 series ?

I started from
https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/log/?h=merge
(commit de12314b471bf)

Took your series with

b4 shazam 20250717142732.2...@gmail.com

built ppc64le_defconfig

And successfully boot it under QEMU:

$ qemu-system-ppc64 -M pseries,x-vof=on -m 1G -nographic -vga none
-kernel vmlinux -initrd qemu/rootfs-el.cpio.gz -append noreboot
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-cfpc=workaround
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-sbbc=workaround
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-ibs=workaround
qemu-system-ppc64: warning: TCG doesn't support requested feature,
cap-ccf-assist=on
[ 0.000000][ T0] random: crng init done
[ 0.000000][ T0] radix-mmu: Page sizes from device-tree:
[ 0.000000][ T0] radix-mmu: Page size shift = 12 AP=0x0
[ 0.000000][ T0] radix-mmu: Page size shift = 16 AP=0x5
[ 0.000000][ T0] radix-mmu: Page size shift = 21 AP=0x1
[ 0.000000][ T0] radix-mmu: Page size shift = 30 AP=0x2
[ 0.000000][ T0] Activating Kernel Userspace Access Prevention
[ 0.000000][ T0] Activating Kernel Userspace Execution Prevention
[ 0.000000][ T0] radix-mmu: Mapped
0x0000000000000000-0x0000000003e00000 with 2.00 MiB pages (exec)
[ 0.000000][ T0] radix-mmu: Mapped
0x0000000003e00000-0x0000000040000000 with 2.00 MiB pages
[ 0.000000][ T0] lpar: Using radix MMU under hypervisor
[ 0.000000][ T0] Linux version 6.16.0-02450-g1c11a8599f68
(chl...@PO20335.IDSI0.si.c-s.fr) (powerpc64-linux-gcc (GCC) 8.5.0, GNU
ld (GNU Binutils) 2.36.1) #1459 SMP Mon Aug 4 12:49:11 CEST 2025
[ 0.000000][ T0] KernelAddressSanitizer initialized (generic)
[ 0.000000][ T0] OF: reserved mem: Reserved memory: No
reserved-memory node in the DT
[ 0.000000][ T0] Found initrd at 0xc000000004cf0000:0xc00000000530d15a
[ 0.000000][ T0] Hardware name: IBM pSeries (emulated by qemu)
POWER9 (architected) 0x4e1202 0xf000005 pSeries
[ 0.000000][ T0] printk: legacy bootconsole [udbg0] enabled
[ 0.000000][ T0] Partition configured for 1 cpus.
[ 0.000000][ T0] CPU maps initialized for 1 thread per core
[ 0.000000][ T0] numa: Partition configured for 1 NUMA nodes.
[ 0.000000][ T0] -----------------------------------------------------
[ 0.000000][ T0] phys_mem_size = 0x40000000
[ 0.000000][ T0] dcache_bsize = 0x80
[ 0.000000][ T0] icache_bsize = 0x80
[ 0.000000][ T0] cpu_features = 0x0001c06b8f4f9187
[ 0.000000][ T0] possible = 0x003ffbfbcf5fb187
[ 0.000000][ T0] always = 0x0000000380008181
[ 0.000000][ T0] cpu_user_features = 0xdc0065c2 0xaef00000
[ 0.000000][ T0] mmu_features = 0x3c007641
[ 0.000000][ T0] firmware_features = 0x00000a85455a445f
[ 0.000000][ T0] vmalloc start = 0xc008000000000000
[ 0.000000][ T0] IO start = 0xc00a000000000000
[ 0.000000][ T0] vmemmap start = 0xc00c000000000000
[ 0.000000][ T0] -----------------------------------------------------
[ 0.000000][ T0] NODE_DATA(0) allocated [mem 0x3fb30800-0x3fb37fff]
[ 0.000000][ T0] ftrace: allocating 47840 entries in 12 pages
[ 0.000000][ T0] ftrace: allocated 12 pages with 2 groups
[ 0.000000][ T0] rcu: Hierarchical RCU implementation.
[ 0.000000][ T0] rcu: RCU event tracing is enabled.
[ 0.000000][ T0] rcu: RCU restricting CPUs from NR_CPUS=2048 to
nr_cpu_ids=1.
[ 0.000000][ T0] Rude variant of Tasks RCU enabled.
[ 0.000000][ T0] Tracing variant of Tasks RCU enabled.
[ 0.000000][ T0] rcu: RCU calculated value of scheduler-enlistment
delay is 100 jiffies.
[ 0.000000][ T0] rcu: Adjusting geometry for rcu_fanout_leaf=16,
nr_cpu_ids=1
[ 0.000000][ T0] RCU Tasks Rude: Setting shift to 0 and lim to 1
rcu_task_cb_adjust=1 rcu_task_cpu_ids=1.
[ 0.000000][ T0] RCU Tasks Trace: Setting shift to 0 and lim to 1
rcu_task_cb_adjust=1 rcu_task_cpu_ids=1.
[ 0.000000][ T0] NR_IRQS: 512, nr_irqs: 512, preallocated irqs: 16
[ 0.000000][ T0] xive: Using IRQ range [0-0]
[ 0.000000][ T0] xive: Interrupt handling initialized with spapr
backend
[ 0.000000][ T0] xive: Using priority 6 for all interrupts
[ 0.000000][ T0] xive: Using 64kB queues
[ 0.000000][ T0] rcu: srcu_init: Setting srcu_struct sizes based
on contention.
[ 0.000290][ T0] time_init: 56 bit decrementer (max: 7fffffffffffff)
[ 0.001690][ T0] clocksource: timebase: mask: 0xffffffffffffffff
max_cycles: 0x761537d007, max_idle_ns: 440795202126 ns
[ 0.003232][ T0] clocksource: timebase mult[1f40000] shift[24]
registered
[ 0.040308][ T0] Console: colour dummy device 80x25
[ 0.042553][ T0] printk: legacy console [hvc0] enabled
[ 0.042553][ T0] printk: legacy console [hvc0] enabled
[ 0.043883][ T0] printk: legacy bootconsole [udbg0] disabled
[ 0.043883][ T0] printk: legacy bootconsole [udbg0] disabled
[ 0.052810][ T0] pid_max: default: 32768 minimum: 301
[ 0.062682][ T0] LSM: initializing
lsm=lockdown,capability,landlock,yama,selinux,bpf,ima
[ 0.075902][ T0] landlock: Up and running.
[ 0.076246][ T0] Yama: becoming mindful.
[ 0.077639][ T0] SELinux: Initializing.
[ 0.157184][ T0] LSM support for eBPF active
[ 0.170713][ T0] Mount-cache hash table entries: 8192 (order: 0,
65536 bytes, linear)
[ 0.171081][ T0] Mountpoint-cache hash table entries: 8192 (order:
0, 65536 bytes, linear)
[ 0.319421][ T1] POWER9 performance monitor hardware support
registered
[ 0.329508][ T1] rcu: Hierarchical SRCU implementation.
[ 0.329940][ T1] rcu: Max phase no-delay instances is 400.
[ 0.382714][ T1] smp: Bringing up secondary CPUs ...
[ 0.385679][ T1] smp: Brought up 1 node, 1 CPU
[ 0.392599][ T1] numa: Node 0 CPUs: 0
[ 0.445093][ T20] node 0 deferred pages initialised in 21ms
[ 0.447420][ T1] Memory: 784000K/1048576K available (25600K kernel
code, 9216K rwdata, 23552K rodata, 13824K init, 2373K bss, 254528K
reserved, 0K cma-reserved)
[ 0.469596][ T20] pgdatinit0 (20) used greatest stack depth: 29936
bytes left
[ 0.504269][ T1] devtmpfs: initialized
[ 0.605561][ T1] PCI host bridge /pci@800000020000000 ranges:
[ 0.607269][ T1] IO 0x0000200000000000..0x000020000000ffff ->
0x0000000000000000
[ 0.607837][ T1] MEM 0x0000200080000000..0x00002000ffffffff ->
0x0000000080000000
[ 0.608141][ T1] MEM 0x0000210000000000..0x000021ffffffffff ->
0x0000210000000000
[ 0.623659][ T1] clocksource: jiffies: mask: 0xffffffff
max_cycles: 0xffffffff, max_idle_ns: 1911260446275000 ns
[ 0.625600][ T1] posixtimers hash table entries: 512 (order: -3,
8192 bytes, linear)
[ 0.627067][ T1] futex hash table entries: 256 (32768 bytes on 1
NUMA nodes, total 32 KiB, linear).
[ 0.692128][ T1] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[ 0.714282][ T1] audit: initializing netlink subsys (disabled)
[ 0.722232][ T25] audit: type=2000 audit(1754308137.543:1):
state=initialized audit_enabled=0 res=1
[ 0.743697][ T1] cpuidle: using governor menu
[ 0.755015][ T1] WARNING: nvram corruption detected: 0-length
partition
[ 0.755541][ T1] nvram: No room to create ibm,rtas-log partition,
deleting any obsolete OS partitions...
[ 0.755794][ T1] nvram: Failed to find or create ibm,rtas-log
partition, err -28
[ 0.756219][ T1] nvram: No room to create lnx,oops-log partition,
deleting any obsolete OS partitions...
[ 0.756379][ T1] nvram: Failed to find or create lnx,oops-log
partition, err -28
Linux ppc64le
#1459 SMP Mon Au[ 0.756539][ T1] nvram: Failed to initialize oops
partition!
[ 0.762921][ T1] EEH: pSeries platform initialized
[ 0.808097][ T1] kprobes: kprobe jump-optimization is enabled. All
kprobes are optimized if possible.
[ 1.365147][ T1] HugeTLB: allocation took 0ms with
hugepage_allocation_threads=1
[ 1.366269][ T1] HugeTLB: registered 2.00 MiB page size,
pre-allocated 0 pages
[ 1.366456][ T1] HugeTLB: 0 KiB vmemmap can be freed for a 2.00
MiB page
[ 1.366728][ T1] HugeTLB: registered 1.00 GiB page size,
pre-allocated 0 pages
[ 1.366870][ T1] HugeTLB: 0 KiB vmemmap can be freed for a 1.00
GiB page
[ 1.486251][ T1] iommu: Default domain type: Translated
[ 1.486543][ T1] iommu: DMA domain TLB invalidation policy: strict
mode
[ 1.525452][ T1] SCSI subsystem initialized
[ 1.552424][ T1] usbcore: registered new interface driver usbfs
[ 1.554049][ T1] usbcore: registered new interface driver hub
[ 1.559907][ T1] usbcore: registered new device driver usb
[ 1.562100][ T1] pps_core: LinuxPPS API ver. 1 registered
[ 1.562266][ T1] pps_core: Software ver. 5.3.6 - Copyright
2005-2007 Rodolfo Giometti <giom...@linux.it>
[ 1.566642][ T1] PTP clock support registered
[ 1.577168][ T1] EDAC MC: Ver: 3.0.0
[ 1.672772][ T1] PCI: Probing PCI hardware
[ 1.833395][ T1] PCI host bridge to bus 0000:00
[ 1.834524][ T1] pci_bus 0000:00: root bus resource [io
0x10000-0x1ffff] (bus address [0x0000-0xffff])
[ 1.835316][ T1] pci_bus 0000:00: root bus resource [mem
0x200080000000-0x2000ffffffff] (bus address [0x80000000-0xffffffff])
[ 1.835750][ T1] pci_bus 0000:00: root bus resource [mem
0x210000000000-0x21ffffffffff 64bit]
[ 1.836268][ T1] pci_bus 0000:00: root bus resource [bus 00-ff]
[ 1.852192][ T1] IOMMU table initialized, virtual merging enabled
[ 1.864698][ T1] pci_bus 0000:00: resource 4 [io 0x10000-0x1ffff]
[ 1.864967][ T1] pci_bus 0000:00: resource 5 [mem
0x200080000000-0x2000ffffffff]
[ 1.865136][ T1] pci_bus 0000:00: resource 6 [mem
0x210000000000-0x21ffffffffff 64bit]
[ 1.865619][ T1] EEH: No capable adapters found: recovery disabled.
[ 1.895816][ T1] vgaarb: loaded
[ 1.906597][ T1] clocksource: Switched to clocksource timebase
[ 7.645823][ T1] NET: Registered PF_INET protocol family
[ 7.650783][ T1] IP idents hash table entries: 16384 (order: 1,
131072 bytes, linear)
[ 7.684628][ T1] tcp_listen_portaddr_hash hash table entries: 4096
(order: 0, 65536 bytes, linear)
[ 7.686311][ T1] Table-perturb hash table entries: 65536 (order:
2, 262144 bytes, linear)
[ 7.687827][ T1] TCP established hash table entries: 8192 (order:
0, 65536 bytes, linear)
[ 7.690430][ T1] TCP bind hash table entries: 8192 (order: 2,
262144 bytes, linear)
[ 7.694736][ T1] TCP: Hash tables configured (established 8192
bind 8192)
[ 7.697887][ T1] UDP hash table entries: 1024 (order: 0, 65536
bytes, linear)
[ 7.700717][ T1] UDP-Lite hash table entries: 1024 (order: 0,
65536 bytes, linear)
[ 7.709437][ T1] NET: Registered PF_UNIX/PF_LOCAL protocol family
[ 7.728670][ T1] RPC: Registered named UNIX socket transport module.
[ 7.729411][ T1] RPC: Registered udp transport module.
[ 7.729661][ T1] RPC: Registered tcp transport module.
[ 7.729825][ T1] RPC: Registered tcp-with-tls transport module.
[ 7.731574][ T1] RPC: Registered tcp NFSv4.1 backchannel transport
module.
[ 7.732309][ T1] PCI: CLS 0 bytes, default 128
[ 7.769612][ T12] Trying to unpack rootfs image as initramfs...
[ 7.937617][ T1] Initialise system trusted keyrings
[ 7.942505][ T1] Key type blacklist registered
[ 7.964894][ T1] workingset: timestamp_bits=38 max_order=14
bucket_order=0
[ 8.127738][ T1] NFS: Registering the id_resolver key type
[ 8.129779][ T1] Key type id_resolver registered
[ 8.130646][ T1] Key type id_legacy registered
[ 8.150761][ T1] SGI XFS with ACLs, security attributes, no debug
enabled
[ 8.343926][ T1] integrity: Platform Keyring initialized
[ 8.348809][ T1] Key type asymmetric registered
[ 8.354232][ T1] Asymmetric key parser 'x509' registered
[ 8.372851][ T1] Block layer SCSI generic (bsg) driver version 0.4
loaded (major 247)
[ 8.375844][ T1] io scheduler mq-deadline registered
[ 8.377498][ T1] io scheduler kyber registered
[ 11.082153][ T1] Serial: 8250/16550 driver, 4 ports, IRQ sharing
disabled
[ 11.199822][ T1] Non-volatile memory driver v1.3
[ 11.749528][ T1] brd: module loaded
[ 12.062135][ T1] loop: module loaded
[ 12.078153][ T1] ipr: IBM Power RAID SCSI Device Driver version:
2.6.4 (March 14, 2017)
[ 12.118444][ T1] ibmvscsi 71000003: SRP_VERSION: 16.a
[ 12.134566][ T1] ibmvscsi 71000003: Maximum ID: 64 Maximum LUN: 32
Maximum Channel: 3
[ 12.135207][ T1] scsi host0: IBM POWER Virtual SCSI Adapter 1.5.9
[ 12.188126][ C0] ibmvscsi 71000003: partner initialization complete
[ 12.191555][ C0] ibmvscsi 71000003: host srp version: 16.a, host
partition qemu (0), OS 2, max io 2097152
[ 12.192709][ C0] ibmvscsi 71000003: sent SRP login
[ 12.193698][ C0] ibmvscsi 71000003: SRP_LOGIN succeeded
[ 12.381686][ T1] scsi 0:0:2:0: CD-ROM QEMU QEMU
CD-ROM 2.5+ PQ: 0 ANSI: 5
[ 17.470934][ T12] Freeing initrd memory: 6208K
[ 18.458146][ C0] sr 0:0:2:0: Power-on or device reset occurred
[ 18.468304][ T1] sr 0:0:2:0: [sr0] scsi3-mmc drive: 16x/50x cd/rw
xa/form2 cdda tray
[ 18.469176][ T1] cdrom: Uniform CD-ROM driver Revision: 3.20
[ 18.507507][ T1] sr 0:0:2:0: Attached scsi generic sg0 type 5
[ 18.535454][ T1] e100: Intel(R) PRO/100 Network Driver
[ 18.535682][ T1] e100: Copyright(c) 1999-2006 Intel Corporation
[ 18.536741][ T1] e1000: Intel(R) PRO/1000 Network Driver
[ 18.536894][ T1] e1000: Copyright (c) 1999-2006 Intel Corporation.
[ 18.538093][ T1] e1000e: Intel(R) PRO/1000 Network Driver
[ 18.538241][ T1] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
[ 18.548528][ T1] i2c_dev: i2c /dev entries driver
[ 18.551722][ T1] device-mapper: core: CONFIG_IMA_DISABLE_HTABLE is
disabled. Duplicate IMA measurements will not be recorded in the IMA log.
[ 18.553452][ T1] device-mapper: uevent: version 1.0.3
[ 18.564534][ T1] device-mapper: ioctl: 4.50.0-ioctl (2025-04-28)
initialised: dm-d...@lists.linux.dev
[ 18.591869][ T1] usbcore: registered new interface driver usbhid
[ 18.592226][ T1] usbhid: USB HID core driver
[ 18.594958][ T1] ipip: IPv4 and MPLS over IPv4 tunneling driver
[ 18.611797][ T1] NET: Registered PF_INET6 protocol family
[ 18.651618][ T1] Segment Routing with IPv6
[ 18.653186][ T1] In-situ OAM (IOAM) with IPv6
[ 18.656776][ T1] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[ 18.672737][ T1] NET: Registered PF_PACKET protocol family
[ 18.676757][ T1] Key type dns_resolver registered
[ 18.678951][ T1] secvar-sysfs: Failed to retrieve secvar operations
[ 18.683474][ T1] Running code patching self-tests ...
[ 19.045499][ T1] registered taskstats version 1
[ 19.055453][ T1] Loading compiled-in X.509 certificates
[ 19.106895][ T1] Loaded X.509 cert 'Build time autogenerated
kernel key: 9f01b0d991e9d73077cd296c67edb3efb56e2c47'
[ 19.372917][ T1] Demotion targets for Node 0: null
[ 19.373664][ T1] page_owner is disabled
[ 19.385615][ T1] Secure boot mode disabled
[ 19.387815][ T1] ima: No TPM chip found, activating TPM-bypass!
[ 19.389833][ T1] Loading compiled-in module X.509 certificates
[ 19.406497][ T1] Loaded X.509 cert 'Build time autogenerated
kernel key: 9f01b0d991e9d73077cd296c67edb3efb56e2c47'
[ 19.407680][ T1] ima: Allocated hash algorithm: sha256
[ 19.416714][ T1] Secure boot mode disabled
[ 19.417345][ T1] Trusted boot mode disabled
[ 19.417596][ T1] ima: No architecture policies found
[ 19.426439][ T1] printk: legacy console [netcon0] enabled
[ 19.426791][ T1] netconsole: network logging started
[ 19.610553][ T1] Freeing unused kernel image (initmem) memory: 13824K
[ 19.766345][ T1] Checked W+X mappings: passed, no W+X pages found
[ 19.767739][ T1] rodata_test: all tests were successful
[ 19.768936][ T1] Run /init as init process
[ 20.432221][ T58] mount (58) used greatest stack depth: 26992 bytes
left
[ 20.808923][ T60] mount (60) used greatest stack depth: 26416 bytes
left
mount: mounting devtmpfs on /dev failed: Device or resource busy
Starting syslogd: OK
Starting klogd: OK
Running sysctl: [ 24.879851][ T83] find (83) used greatest stack
depth: 26368 bytes left
[ 24.945794][ T84] xargs (84) used greatest stack depth: 26240 bytes
left
OK
Saving 256 bits of creditable seed for next boot
Starting network: [ 26.530674][ T96] ip (96) used greatest stack
depth: 26048 bytes left
[ 27.018425][ T97] ip (97) used greatest stack depth: 24256 bytes left
Waiting for interface eth0 to appear..... timeout!
run-parts: /etc/network/if-pre-up.d/wait_iface: exit status 1
FAIL
[ 33.902403][ C0] hrtimer: interrupt took 3450588 ns
Found console hvc0

Linux version 6.16.0-02450-g1c11a8599f68
(chl...@PO20335.IDSI0.si.c-s.fr) (powerpc64-linux-gcc (GCC) 8.5.0, GNU
ld (GNU Binutils) 2.36.1) #1459 SMP Mon Aug 4 12:49:11 CEST 2025
Network interface test failed
TPM selftest failed
File system test skipped
Boot successful.
~ #


Christophe

Sabyrzhan Tasbolatov

unread,
Aug 4, 2025, 9:22:18 AM8/4/25
to Christophe Leroy, Andrey Ryabinin, Andrey Konovalov, linuxp...@lists.ozlabs.org, h...@linux.ibm.com, ak...@linux-foundation.org, kasan-dev, Linux Kernel Mailing List, Linux Memory Management List
On Mon, Aug 4, 2025 at 5:04 PM Christophe Leroy
<christop...@csgroup.eu> wrote:
>
> Hi,
>
> Le 03/08/2025 à 21:27, Sabyrzhan Tasbolatov a écrit :
> > On Wed, Jul 23, 2025 at 10:33 PM Andrey Ryabinin <ryabin...@gmail.com> wrote:
> >>
> >> ...
> >>
> >> I don't know if it's a real problem or not. I'm just pointing out that we might
> >> have tricky use case here and maybe that's a problem, because nobody had such use
> >> case in mind. But maybe it's just fine.
> >> I think we just need to boot test it, to see if this works.
> >> ...
> >> powerpc used static key same way before your patches, so powerpc should be fine.
> >
> > Hello,
> >
> > Just heads up that I am still working on v4.
> > While I can verify the success on compile and booting with my changes
> > on x86, arm with SW/HW_TAGS modes, I'm having issues with PowerPC, UML
> > arch that selects ARCH_DEFER_KASAN.
> >
> > Adding Christophe Leroy in TO. Please advise on the powerpc panic issue.
>
> I don't have the problem you report. Is it with your v3 series ?

Hi,
Yes, but I've changed it to address Andrey Ryabinin's comments.
I've found out the issue was in my pending v4 kasan-enabled.h definitions,
So powerpc changes are fine. Your comment about revisiting v3 helped me as
I was going through a rabbit hole. Thanks for the testing!

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:32 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
This patch series addresses the fragmentation in KASAN initialization
across architectures by introducing a unified approach that eliminates
duplicate static keys and arch-specific kasan_arch_is_ready()
implementations.

The core issue is that different architectures have inconsistent approaches
to KASAN readiness tracking:
- PowerPC, LoongArch, and UML arch, each implement own kasan_arch_is_ready()
- Only HW_TAGS mode had a unified static key (kasan_flag_enabled)
- Generic and SW_TAGS modes relied on arch-specific solutions
or always-on behavior

This series implements two-level approach:
1. kasan_enabled() - compile-time check for KASAN configuration
2. kasan_shadow_initialized() - runtime check for shadow memory readiness

Changes in v4:
- Unified patches where ARCH_DEFER_KASAN is introduced and used
in the KASAN code (Andrey Ryabinin)
- Fixed kasan_enable() for HW_TAGS mode (Andrey Ryabinin)
- Replaced !kasan_enabled() with !kasan_shadow_initialized() in
loongarch which selects ARCH_DEFER_KASAN (Andrey Ryabinin)
- Addressed the issue in UML arch, where kasan_init_generic() is
called before jump_label_init() (Andrey Ryabinin)

Adding in TO additional recipients who developed KASAN in LoongArch, UML.

Tested on:
- powerpc - selects ARCH_DEFER_KASAN
Built ppc64_defconfig (PPC_BOOK3S_64) - OK
Booted via qemu-system-ppc64 - OK

- um - selects ARCH_DEFER_KASAN
Built defconfig with KASAN_INLINE - OK
Built defconfig with STATIC_LINK && KASAN_OUTLINE - OK
Booted ./linux - OK

- loongarch - selects ARCH_DEFER_KASAN
Built defconfig with KASAN_GENERIC - OK
Haven't tested the boot. Asking Loongarch developers to verify - N/A
But should be good, since Loongarch does not have specific "kasan_init()"
call like UML does. It selects ARCH_DEFER_KASAN and calls kasan_init()
in the end of setup_arch() after jump_label_init().

- arm64
Built defconfig, kvm_guest.config with HW_TAGS, SW_TAGS, GENERIC - OK
KASAN_KUNIT_TEST - OK
Booted via qemu-system-arm64 - OK

- x86_64
Built defconfig, kvm_guest.config with KASAN_GENERIC - OK
KASAN_KUNIT_TEST - OK
Booted via qemu-system-x86 - OK

- s390, riscv, xtensa, arm
Built defconfig with KASAN_GENERIC - OK

Previous v3 thread: https://lore.kernel.org/all/20250717142732.2...@gmail.com/
Previous v2 thread: https://lore.kernel.org/all/20250626153147.1...@gmail.com/

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049

Sabyrzhan Tasbolatov (9):
kasan: introduce ARCH_DEFER_KASAN and unify static key across modes
kasan/powerpc: select ARCH_DEFER_KASAN and call kasan_init_generic
kasan/arm,arm64: call kasan_init_generic in kasan_init
kasan/xtensa: call kasan_init_generic in kasan_init
kasan/loongarch: select ARCH_DEFER_KASAN and call kasan_init_generic
kasan/um: select ARCH_DEFER_KASAN and call kasan_init_generic
kasan/x86: call kasan_init_generic in kasan_init
kasan/s390: call kasan_init_generic in kasan_init
kasan/riscv: call kasan_init_generic in kasan_init

arch/arm/mm/kasan_init.c | 2 +-
arch/arm64/mm/kasan_init.c | 4 +--
arch/loongarch/Kconfig | 1 +
arch/loongarch/include/asm/kasan.h | 7 -----
arch/loongarch/mm/kasan_init.c | 8 ++---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/kasan.h | 12 --------
arch/powerpc/mm/kasan/init_32.c | 2 +-
arch/powerpc/mm/kasan/init_book3e_64.c | 2 +-
arch/powerpc/mm/kasan/init_book3s_64.c | 6 +---
arch/riscv/mm/kasan_init.c | 1 +
arch/s390/kernel/early.c | 3 +-
arch/um/Kconfig | 1 +
arch/um/include/asm/kasan.h | 5 ---
arch/um/kernel/mem.c | 12 ++++++--
arch/x86/mm/kasan_init_64.c | 2 +-
arch/xtensa/mm/kasan_init.c | 2 +-
include/linux/kasan-enabled.h | 36 +++++++++++++++++-----
include/linux/kasan.h | 42 ++++++++++++++++++++------
lib/Kconfig.kasan | 8 +++++
mm/kasan/common.c | 18 +++++++----
mm/kasan/generic.c | 23 ++++++++------
mm/kasan/hw_tags.c | 9 +-----
mm/kasan/kasan.h | 36 ++++++++++++++++------
mm/kasan/shadow.c | 32 +++++---------------
mm/kasan/sw_tags.c | 4 ++-
mm/kasan/tags.c | 2 +-
27 files changed, 157 insertions(+), 124 deletions(-)

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:34 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Introduce CONFIG_ARCH_DEFER_KASAN to identify architectures that need
to defer KASAN initialization until shadow memory is properly set up,
and unify the static key infrastructure across all KASAN modes.

Some architectures (like PowerPC with radix MMU) need to set up their
shadow memory mappings before KASAN can be safely enabled, while others
(like s390, x86, arm) can enable KASAN much earlier or even from the
beginning.

Historically, the runtime static key kasan_flag_enabled existed only for
CONFIG_KASAN_HW_TAGS mode. Generic and SW_TAGS modes either relied on
architecture-specific kasan_arch_is_ready() implementations or evaluated
KASAN checks unconditionally, leading to code duplication.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v4:
- Fixed HW_TAGS static key functionality (was broken in v3)
- Merged configuration and implementation for atomicity
---
include/linux/kasan-enabled.h | 36 +++++++++++++++++++++++-------
include/linux/kasan.h | 42 +++++++++++++++++++++++++++--------
lib/Kconfig.kasan | 8 +++++++
mm/kasan/common.c | 18 ++++++++++-----
mm/kasan/generic.c | 23 +++++++++++--------
mm/kasan/hw_tags.c | 9 +-------
mm/kasan/kasan.h | 36 +++++++++++++++++++++---------
mm/kasan/shadow.c | 32 ++++++--------------------
mm/kasan/sw_tags.c | 4 +++-
mm/kasan/tags.c | 2 +-
10 files changed, 133 insertions(+), 77 deletions(-)

diff --git a/include/linux/kasan-enabled.h b/include/linux/kasan-enabled.h
index 6f612d69ea0..52a3909f032 100644
--- a/include/linux/kasan-enabled.h
+++ b/include/linux/kasan-enabled.h
@@ -4,32 +4,52 @@

#include <linux/static_key.h>

-#ifdef CONFIG_KASAN_HW_TAGS
+/* Controls whether KASAN is enabled at all (compile-time check). */
+static __always_inline bool kasan_enabled(void)
+{
+ return IS_ENABLED(CONFIG_KASAN);
+}

+#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
+/*
+ * Global runtime flag for KASAN modes that need runtime control.
+ * Used by ARCH_DEFER_KASAN architectures and HW_TAGS mode.
+ */
DECLARE_STATIC_KEY_FALSE(kasan_flag_enabled);

-static __always_inline bool kasan_enabled(void)
+/*
+ * Runtime control for shadow memory initialization or HW_TAGS mode.
+ * Uses static key for architectures that need deferred KASAN or HW_TAGS.
+ */
+static __always_inline bool kasan_shadow_initialized(void)
{
return static_branch_likely(&kasan_flag_enabled);
}

-static inline bool kasan_hw_tags_enabled(void)
+static inline void kasan_enable(void)
+{
+ static_branch_enable(&kasan_flag_enabled);
+}
+#else
+/* For architectures that can enable KASAN early, use compile-time check. */
+static __always_inline bool kasan_shadow_initialized(void)
{
return kasan_enabled();
}

-#else /* CONFIG_KASAN_HW_TAGS */
+static inline void kasan_enable(void) {}
+#endif /* CONFIG_ARCH_DEFER_KASAN || CONFIG_KASAN_HW_TAGS */

-static inline bool kasan_enabled(void)
+#ifdef CONFIG_KASAN_HW_TAGS
+static inline bool kasan_hw_tags_enabled(void)
{
- return IS_ENABLED(CONFIG_KASAN);
+ return kasan_shadow_initialized();
}
-
+#else
static inline bool kasan_hw_tags_enabled(void)
{
return false;
}
-
#endif /* CONFIG_KASAN_HW_TAGS */

#endif /* LINUX_KASAN_ENABLED_H */
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 890011071f2..5bf05aed795 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -194,7 +194,7 @@ bool __kasan_slab_pre_free(struct kmem_cache *s, void *object,
static __always_inline bool kasan_slab_pre_free(struct kmem_cache *s,
void *object)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
return __kasan_slab_pre_free(s, object, _RET_IP_);
return false;
}
@@ -229,7 +229,7 @@ static __always_inline bool kasan_slab_free(struct kmem_cache *s,
void *object, bool init,
bool still_accessible)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
return __kasan_slab_free(s, object, init, still_accessible);
return false;
}
@@ -237,7 +237,7 @@ static __always_inline bool kasan_slab_free(struct kmem_cache *s,
void __kasan_kfree_large(void *ptr, unsigned long ip);
static __always_inline void kasan_kfree_large(void *ptr)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
__kasan_kfree_large(ptr, _RET_IP_);
}

@@ -302,7 +302,7 @@ bool __kasan_mempool_poison_pages(struct page *page, unsigned int order,
static __always_inline bool kasan_mempool_poison_pages(struct page *page,
unsigned int order)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
return __kasan_mempool_poison_pages(page, order, _RET_IP_);
return true;
}
@@ -356,7 +356,7 @@ bool __kasan_mempool_poison_object(void *ptr, unsigned long ip);
*/
static __always_inline bool kasan_mempool_poison_object(void *ptr)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
return __kasan_mempool_poison_object(ptr, _RET_IP_);
return true;
}
@@ -543,6 +543,12 @@ void kasan_report_async(void);

#endif /* CONFIG_KASAN_HW_TAGS */

+#ifdef CONFIG_KASAN_GENERIC
+void __init kasan_init_generic(void);
+#else
+static inline void kasan_init_generic(void) { }
+#endif
+
#ifdef CONFIG_KASAN_SW_TAGS
void __init kasan_init_sw_tags(void);
#else
@@ -562,11 +568,29 @@ static inline void kasan_init_hw_tags(void) { }
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)

void kasan_populate_early_vm_area_shadow(void *start, unsigned long size);
-int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
-void kasan_release_vmalloc(unsigned long start, unsigned long end,
+
+int __kasan_populate_vmalloc(unsigned long addr, unsigned long size);
+static inline int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
+{
+ if (!kasan_shadow_initialized())
+ return 0;
+ return __kasan_populate_vmalloc(addr, size);
+}
+
+void __kasan_release_vmalloc(unsigned long start, unsigned long end,
unsigned long free_region_start,
unsigned long free_region_end,
unsigned long flags);
+static inline void kasan_release_vmalloc(unsigned long start,
+ unsigned long end,
+ unsigned long free_region_start,
+ unsigned long free_region_end,
+ unsigned long flags)
+{
+ if (kasan_shadow_initialized())
+ __kasan_release_vmalloc(start, end, free_region_start,
+ free_region_end, flags);
+}

#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */

@@ -592,7 +616,7 @@ static __always_inline void *kasan_unpoison_vmalloc(const void *start,
unsigned long size,
kasan_vmalloc_flags_t flags)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
return __kasan_unpoison_vmalloc(start, size, flags);
return (void *)start;
}
@@ -601,7 +625,7 @@ void __kasan_poison_vmalloc(const void *start, unsigned long size);
static __always_inline void kasan_poison_vmalloc(const void *start,
unsigned long size)
{
- if (kasan_enabled())
+ if (kasan_shadow_initialized())
__kasan_poison_vmalloc(start, size);
}

diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index f82889a830f..38456560c85 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -19,6 +19,14 @@ config ARCH_DISABLE_KASAN_INLINE
Disables both inline and stack instrumentation. Selected by
architectures that do not support these instrumentation types.

+config ARCH_DEFER_KASAN
+ bool
+ help
+ Architectures should select this if they need to defer KASAN
+ initialization until shadow memory is properly set up. This
+ enables runtime control via static keys. Otherwise, KASAN uses
+ compile-time constants for better performance.
+
config CC_HAS_KASAN_GENERIC
def_bool $(cc-option, -fsanitize=kernel-address)
index d54e89f8c3e..1d20b925b9d 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -36,6 +36,17 @@
#include "kasan.h"
#include "../slab.h"

+/*
+ * Initialize Generic KASAN and enable runtime checks.
+ * This should be called from arch kasan_init() once shadow memory is ready.
+ */
+void __init kasan_init_generic(void)
+{
+ kasan_enable();
+
+ pr_info("KernelAddressSanitizer initialized (generic)\n");
+}
+
/*
* All functions below always inlined so compiler could
* perform better optimizations in each of __asan_loadX/__assn_storeX
@@ -165,7 +176,7 @@ static __always_inline bool check_region_inline(const void *addr,
size_t size, bool write,
unsigned long ret_ip)
{
- if (!kasan_arch_is_ready())
+ if (!kasan_shadow_initialized())
index 129178be5e6..2d67a99898e 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -398,7 +398,13 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags, depot_flags_t depot_flags);
void kasan_set_track(struct kasan_track *track, depot_stack_handle_t stack);
void kasan_save_track(struct kasan_track *track, gfp_t flags);
void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags);
-void kasan_save_free_info(struct kmem_cache *cache, void *object);
+
+void __kasan_save_free_info(struct kmem_cache *cache, void *object);
+static inline void kasan_save_free_info(struct kmem_cache *cache, void *object)
+{
+ if (kasan_shadow_initialized())
+ __kasan_save_free_info(cache, object);
+}

#ifdef CONFIG_KASAN_GENERIC
bool kasan_quarantine_put(struct kmem_cache *cache, void *object);
@@ -499,6 +505,7 @@ static inline bool kasan_byte_accessible(const void *addr)

#else /* CONFIG_KASAN_HW_TAGS */

+void __kasan_poison(const void *addr, size_t size, u8 value, bool init);
/**
* kasan_poison - mark the memory range as inaccessible
* @addr: range start address, must be aligned to KASAN_GRANULE_SIZE
@@ -506,7 +513,11 @@ static inline bool kasan_byte_accessible(const void *addr)
* @value: value that's written to metadata for the range
* @init: whether to initialize the memory range (only for hardware tag-based)
*/
-void kasan_poison(const void *addr, size_t size, u8 value, bool init);
+static inline void kasan_poison(const void *addr, size_t size, u8 value, bool init)
+{
+ if (kasan_shadow_initialized())
+ __kasan_poison(addr, size, value, init);
+}

/**
* kasan_unpoison - mark the memory range as accessible
@@ -521,12 +532,19 @@ void kasan_poison(const void *addr, size_t size, u8 value, bool init);
*/
void kasan_unpoison(const void *addr, size_t size, bool init);

-bool kasan_byte_accessible(const void *addr);
+bool __kasan_byte_accessible(const void *addr);
+static inline bool kasan_byte_accessible(const void *addr)
+{
+ if (!kasan_shadow_initialized())
+ return true;
+ return __kasan_byte_accessible(addr);
+}

#endif /* CONFIG_KASAN_HW_TAGS */

#ifdef CONFIG_KASAN_GENERIC

+void __kasan_poison_last_granule(const void *address, size_t size);
/**
* kasan_poison_last_granule - mark the last granule of the memory range as
* inaccessible
@@ -536,7 +554,11 @@ bool kasan_byte_accessible(const void *addr);
* This function is only available for the generic mode, as it's the only mode
* that has partially poisoned memory granules.
*/
-void kasan_poison_last_granule(const void *address, size_t size);
+static inline void kasan_poison_last_granule(const void *address, size_t size)
+{
+ if (kasan_shadow_initialized())
index b9382b5b6a3..51a376940ea 100644
--- a/mm/kasan/sw_tags.c
+++ b/mm/kasan/sw_tags.c
@@ -45,6 +45,8 @@ void __init kasan_init_sw_tags(void)

kasan_init_tags();

+ kasan_enable();
+
pr_info("KernelAddressSanitizer initialized (sw-tags, stacktrace=%s)\n",
str_on_off(kasan_stack_collection_enabled()));
}
@@ -120,7 +122,7 @@ bool kasan_check_range(const void *addr, size_t size, bool write,
return true;
}

-bool kasan_byte_accessible(const void *addr)
+bool __kasan_byte_accessible(const void *addr)
{
u8 tag = get_tag(addr);
void *untagged_addr = kasan_reset_tag(addr);

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:37 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
PowerPC with radix MMU is the primary architecture that needs deferred
KASAN initialization, as it requires complex shadow memory setup before
KASAN can be safely enabled.

Select ARCH_DEFER_KASAN for PPC_RADIX_MMU to enable the static key
mechanism for runtime KASAN control. Other PowerPC configurations
(like book3e and 32-bit) can enable KASAN early and will use
compile-time constants instead.

Remove the PowerPC-specific static key and kasan_arch_is_ready()
implementation in favor of the unified interface.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Fixes: 55d77bae7342 ("kasan: fix Oops due to missing calls to kasan_arch_is_ready()")
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/kasan.h | 12 ------------
arch/powerpc/mm/kasan/init_32.c | 2 +-
arch/powerpc/mm/kasan/init_book3e_64.c | 2 +-
arch/powerpc/mm/kasan/init_book3s_64.c | 6 +-----
5 files changed, 4 insertions(+), 19 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 93402a1d9c9..11c8ef2d88e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -122,6 +122,7 @@ config PPC
# Please keep this list sorted alphabetically.
#
select ARCH_32BIT_OFF_T if PPC32
+ select ARCH_DEFER_KASAN if PPC_RADIX_MMU
select ARCH_DISABLE_KASAN_INLINE if PPC_RADIX_MMU
select ARCH_DMA_DEFAULT_COHERENT if !NOT_COHERENT_CACHE
select ARCH_ENABLE_MEMORY_HOTPLUG
diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h
index b5bbb94c51f..957a57c1db5 100644
--- a/arch/powerpc/include/asm/kasan.h
+++ b/arch/powerpc/include/asm/kasan.h
@@ -53,18 +53,6 @@
#endif

#ifdef CONFIG_KASAN
-#ifdef CONFIG_PPC_BOOK3S_64
-DECLARE_STATIC_KEY_FALSE(powerpc_kasan_enabled_key);
-
-static __always_inline bool kasan_arch_is_ready(void)
-{
- if (static_branch_likely(&powerpc_kasan_enabled_key))
- return true;
- return false;
-}
-
-#define kasan_arch_is_ready kasan_arch_is_ready
-#endif

void kasan_early_init(void);
void kasan_mmu_init(void);
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:39 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization.
Since arm64 doesn't select ARCH_DEFER_KASAN, this will be a no-op for
the runtime flag but will print the initialization banner.

For SW_TAGS and HW_TAGS modes, their respective init functions will
handle the flag enabling.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/arm/mm/kasan_init.c | 2 +-
arch/arm64/mm/kasan_init.c | 4 +---
2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mm/kasan_init.c b/arch/arm/mm/kasan_init.c
index 111d4f70313..c6625e808bf 100644
--- a/arch/arm/mm/kasan_init.c
+++ b/arch/arm/mm/kasan_init.c
@@ -300,6 +300,6 @@ void __init kasan_init(void)
local_flush_tlb_all();

memset(kasan_early_shadow_page, 0, PAGE_SIZE);
- pr_info("Kernel address sanitizer initialized\n");
init_task.kasan_depth = 0;
+ kasan_init_generic();
}
diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c
index d541ce45dae..abeb81bf6eb 100644
--- a/arch/arm64/mm/kasan_init.c
+++ b/arch/arm64/mm/kasan_init.c
@@ -399,14 +399,12 @@ void __init kasan_init(void)
{
kasan_init_shadow();
kasan_init_depth();
-#if defined(CONFIG_KASAN_GENERIC)
+ kasan_init_generic();
/*
* Generic KASAN is now fully initialized.
* Software and Hardware Tag-Based modes still require
* kasan_init_sw_tags() and kasan_init_hw_tags() correspondingly.
*/
- pr_info("KernelAddressSanitizer initialized (generic)\n");
-#endif
}

#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:42 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since xtensa doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op.

Note that arch/xtensa still uses "current" instead of "init_task" pointer
in `current->kasan_depth = 0;` to enable error messages. This is left
unchanged as it cannot be tested.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/xtensa/mm/kasan_init.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/xtensa/mm/kasan_init.c b/arch/xtensa/mm/kasan_init.c
index f39c4d83173..0524b9ed5e6 100644
--- a/arch/xtensa/mm/kasan_init.c
+++ b/arch/xtensa/mm/kasan_init.c
@@ -94,5 +94,5 @@ void __init kasan_init(void)

/* At this point kasan is fully initialized. Enable error messages. */
current->kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized\n");
+ kasan_init_generic();
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:43 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
LoongArch needs deferred KASAN initialization as it has a custom
kasan_arch_is_ready() implementation that tracks shadow memory
readiness via the kasan_early_stage flag.

Select ARCH_DEFER_KASAN to enable the unified static key mechanism
for runtime KASAN control. Call kasan_init_generic() which handles
Generic KASAN initialization and enables the static key.

Replace kasan_arch_is_ready() with kasan_enabled() and delete the
flag kasan_early_stage in favor of the unified kasan_enabled()
interface.

Note that init_task.kasan_depth = 0 is called after kasan_init_generic(),
which is different than in other arch kasan_init(). This is left
unchanged as it cannot be tested.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v4:
- Replaced !kasan_enabled() with !kasan_shadow_initialized() in
loongarch which selects ARCH_DEFER_KASAN (Andrey Ryabinin)
---
arch/loongarch/Kconfig | 1 +
arch/loongarch/include/asm/kasan.h | 7 -------
arch/loongarch/mm/kasan_init.c | 8 ++------
3 files changed, 3 insertions(+), 13 deletions(-)

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index f0abc38c40a..f6304c073ec 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -9,6 +9,7 @@ config LOONGARCH
select ACPI_PPTT if ACPI
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
select ARCH_BINFMT_ELF_STATE
+ select ARCH_DEFER_KASAN
select ARCH_DISABLE_KASAN_INLINE
select ARCH_ENABLE_MEMORY_HOTPLUG
select ARCH_ENABLE_MEMORY_HOTREMOVE
diff --git a/arch/loongarch/include/asm/kasan.h b/arch/loongarch/include/asm/kasan.h
index 62f139a9c87..0e50e5b5e05 100644
--- a/arch/loongarch/include/asm/kasan.h
+++ b/arch/loongarch/include/asm/kasan.h
@@ -66,7 +66,6 @@
#define XKPRANGE_WC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_WC_KASAN_OFFSET)
#define XKVRANGE_VC_SHADOW_OFFSET (KASAN_SHADOW_START + XKVRANGE_VC_KASAN_OFFSET)

-extern bool kasan_early_stage;
extern unsigned char kasan_early_shadow_page[PAGE_SIZE];

#define kasan_mem_to_shadow kasan_mem_to_shadow
@@ -75,12 +74,6 @@ void *kasan_mem_to_shadow(const void *addr);
#define kasan_shadow_to_mem kasan_shadow_to_mem
const void *kasan_shadow_to_mem(const void *shadow_addr);

-#define kasan_arch_is_ready kasan_arch_is_ready
-static __always_inline bool kasan_arch_is_ready(void)
-{
- return !kasan_early_stage;
-}
-
#define addr_has_metadata addr_has_metadata
static __always_inline bool addr_has_metadata(const void *addr)
{
diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c
index d2681272d8f..57fb6e98376 100644
--- a/arch/loongarch/mm/kasan_init.c
+++ b/arch/loongarch/mm/kasan_init.c
@@ -40,11 +40,9 @@ static pgd_t kasan_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);
#define __pte_none(early, pte) (early ? pte_none(pte) : \
((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page)))

-bool kasan_early_stage = true;
-
void *kasan_mem_to_shadow(const void *addr)
{
- if (!kasan_arch_is_ready()) {
+ if (!kasan_shadow_initialized()) {
return (void *)(kasan_early_shadow_page);
} else {
unsigned long maddr = (unsigned long)addr;
@@ -298,8 +296,6 @@ void __init kasan_init(void)
kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START),
kasan_mem_to_shadow((void *)KFENCE_AREA_END));

- kasan_early_stage = false;
-
/* Populate the linear mapping */
for_each_mem_range(i, &pa_start, &pa_end) {
void *start = (void *)phys_to_virt(pa_start);
@@ -329,5 +325,5 @@ void __init kasan_init(void)

/* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized.\n");
+ kasan_init_generic();
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:46 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
UserMode Linux needs deferred KASAN initialization as it has a custom
kasan_arch_is_ready() implementation that tracks shadow memory readiness
via the kasan_um_is_ready flag.

As it's explained in commit 5b301409e8bc("UML: add support for KASAN
under x86_64"), if CONFIG_STATIC_LINK=y, then it works only with
CONFIG_KASAN_OUTLINE instrumentation.

Calling kasan_init_generic() in the end of kasan_init() like in other
arch does not work for UML as kasan_init() is called way before
main()->linux_main(). It produces the SEGFAULT in:
kasan_init()
kasan_init_generic
kasan_enable
static_key_enable
STATIC_KEY_CHECK_USE
...
<kasan_init+173> movabs r9, kasan_flag_enabled
<kasan_init+183> movabs r8, __func__.2
<kasan_init+193> movabs rcx, 0x60a04540
<kasan_init+203> movabs rdi, 0x60a045a0
<kasan_init+213> movabs r10, warn_slowpath_fmt
WARN_ON_ONCE("static key '%pS' used before call to jump_label_init()")
<kasan_init+226> movabs r12, kasan_flag_enabled

That's why we need to call kasan_init_generic() which enables the
static flag after jump_label_init(). The earliest available place
is arch_mm_preinit().

kasan_init()
main()
start_kernel
setup_arch
jump_label_init
...
mm_core_init
arch_mm_preinit
kasan_init_generic()

PowerPC, for example, has kasan_late_init() in arch_mm_preinit().
Though there is no static key enabling there, but it should be the best
place to enable KASAN "fully".

Verified with defconfig, enabling KASAN.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
Changes in v4:
- Addressed the issue in UML arch, where kasan_init_generic() is
called before jump_label_init() (Andrey Ryabinin)
---
arch/um/Kconfig | 1 +
arch/um/include/asm/kasan.h | 5 -----
arch/um/kernel/mem.c | 12 +++++++++---
3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 9083bfdb773..8d14c8fc2cd 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -5,6 +5,7 @@ menu "UML-specific options"
config UML
bool
default y
+ select ARCH_DEFER_KASAN
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
select ARCH_HAS_CACHE_LINE_SIZE
select ARCH_HAS_CPU_FINALIZE_INIT
diff --git a/arch/um/include/asm/kasan.h b/arch/um/include/asm/kasan.h
index f97bb1f7b85..81bcdc0f962 100644
--- a/arch/um/include/asm/kasan.h
+++ b/arch/um/include/asm/kasan.h
@@ -24,11 +24,6 @@

#ifdef CONFIG_KASAN
void kasan_init(void);
-extern int kasan_um_is_ready;
-
-#ifdef CONFIG_STATIC_LINK
-#define kasan_arch_is_ready() (kasan_um_is_ready)
-#endif
#else
static inline void kasan_init(void) { }
#endif /* CONFIG_KASAN */
diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c
index 76bec7de81b..704a26211ed 100644
--- a/arch/um/kernel/mem.c
+++ b/arch/um/kernel/mem.c
@@ -21,10 +21,10 @@
#include <os.h>
#include <um_malloc.h>
#include <linux/sched/task.h>
+#include <linux/kasan.h>

#ifdef CONFIG_KASAN
-int kasan_um_is_ready;
-void kasan_init(void)
+void __init kasan_init(void)
{
/*
* kasan_map_memory will map all of the required address space and
@@ -32,7 +32,10 @@ void kasan_init(void)
*/
kasan_map_memory((void *)KASAN_SHADOW_START, KASAN_SHADOW_SIZE);
init_task.kasan_depth = 0;
- kasan_um_is_ready = true;
+ /* Since kasan_init() is called before main(),
+ * KASAN is initialized but the enablement is deferred after
+ * jump_label_init(). See arch_mm_preinit().
+ */
}

static void (*kasan_init_ptr)(void)
@@ -58,6 +61,9 @@ static unsigned long brk_end;

void __init arch_mm_preinit(void)
{
+ /* Safe to call after jump_label_init(). Enables KASAN. */
+ kasan_init_generic();
+
/* clear the zero-page */
memset(empty_zero_page, 0, PAGE_SIZE);

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:48 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since x86 doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, and kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/x86/mm/kasan_init_64.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index 0539efd0d21..998b6010d6d 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -451,5 +451,5 @@ void __init kasan_init(void)
__flush_tlb_all();

init_task.kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized\n");
+ kasan_init_generic();
}
--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:51 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since s390 doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, and kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

s390 sets up KASAN mappings in the decompressor and can run with KASAN
enabled from very early, so it doesn't need runtime control.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/s390/kernel/early.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 9adfbdd377d..544e5403dd9 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <asm/asm-extable.h>
#include <linux/memblock.h>
+#include <linux/kasan.h>
#include <asm/access-regs.h>
#include <asm/asm-offsets.h>
#include <asm/machine.h>
@@ -65,7 +66,7 @@ static void __init kasan_early_init(void)
{
#ifdef CONFIG_KASAN
init_task.kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized\n");
+ kasan_init_generic();
#endif
}

--
2.34.1

Sabyrzhan Tasbolatov

unread,
Aug 5, 2025, 10:26:53 AM8/5/25
to ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org, snov...@gmail.com
Call kasan_init_generic() which handles Generic KASAN initialization
and prints the banner. Since riscv doesn't select ARCH_DEFER_KASAN,
kasan_enable() will be a no-op, and kasan_enabled() will return
IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
---
arch/riscv/mm/kasan_init.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c
index 41c635d6aca..ba2709b1eec 100644
--- a/arch/riscv/mm/kasan_init.c
+++ b/arch/riscv/mm/kasan_init.c
@@ -530,6 +530,7 @@ void __init kasan_init(void)

memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
init_task.kasan_depth = 0;
+ kasan_init_generic();

csr_write(CSR_SATP, PFN_DOWN(__pa(swapper_pg_dir)) | satp_mode);
local_flush_tlb_all();
--
2.34.1

Alexandre Ghiti

unread,
Aug 5, 2025, 12:06:49 PM8/5/25
to Sabyrzhan Tasbolatov, ryabin...@gmail.com, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
Hi Sabyrzhan,

On 8/5/25 16:26, Sabyrzhan Tasbolatov wrote:
> Call kasan_init_generic() which handles Generic KASAN initialization
> and prints the banner. Since riscv doesn't select ARCH_DEFER_KASAN,
> kasan_enable() will be a no-op, and kasan_enabled() will return
> IS_ENABLED(CONFIG_KASAN) for optimal compile-time behavior.
>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
> Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
> ---
> arch/riscv/mm/kasan_init.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c
> index 41c635d6aca..ba2709b1eec 100644
> --- a/arch/riscv/mm/kasan_init.c
> +++ b/arch/riscv/mm/kasan_init.c
> @@ -530,6 +530,7 @@ void __init kasan_init(void)
>
> memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
> init_task.kasan_depth = 0;
> + kasan_init_generic();


This is right before actually setting the new mapping to the mmu (which
is done below by setting a register called SATP). It does not seem to be
a problem though, just wanted to let you know.

It boots fine with defconfig + kasan inline so:

Tested-by: Alexandre Ghiti <alex...@rivosinc.com>

Thanks,

Alex


>
> csr_write(CSR_SATP, PFN_DOWN(__pa(swapper_pg_dir)) | satp_mode);
> local_flush_tlb_all();

Andrey Ryabinin

unread,
Aug 5, 2025, 1:18:16 PM8/5/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
There is a reason for this line to be here.
Your patch will change the result of the follow up kasan_mem_to_shadow() call and
feed the wrong address to kasan_map_populate()

Andrey Ryabinin

unread,
Aug 5, 2025, 1:20:00 PM8/5/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 8/5/25 4:26 PM, Sabyrzhan Tasbolatov wrote:
>
> diff --git a/arch/um/Kconfig b/arch/um/Kconfig
> index 9083bfdb773..8d14c8fc2cd 100644
> --- a/arch/um/Kconfig
> +++ b/arch/um/Kconfig
> @@ -5,6 +5,7 @@ menu "UML-specific options"
> config UML
> bool
> default y
> + select ARCH_DEFER_KASAN

select ARCH_DEFER_KASAN if STATIC_LINK

Sabyrzhan Tasbolatov

unread,
Aug 6, 2025, 12:35:59 AM8/6/25
to Andrey Ryabinin, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
On Tue, Aug 5, 2025 at 10:19 PM Andrey Ryabinin <ryabin...@gmail.com> wrote:
>
>
>
> On 8/5/25 4:26 PM, Sabyrzhan Tasbolatov wrote:
> >
> > diff --git a/arch/um/Kconfig b/arch/um/Kconfig
> > index 9083bfdb773..8d14c8fc2cd 100644
> > --- a/arch/um/Kconfig
> > +++ b/arch/um/Kconfig
> > @@ -5,6 +5,7 @@ menu "UML-specific options"
> > config UML
> > bool
> > default y
> > + select ARCH_DEFER_KASAN
>
> select ARCH_DEFER_KASAN if STATIC_LINK

As pointed out in commit 5b301409e8bc("UML: add support for KASAN
under x86_64"),

: Also note that, while UML supports both KASAN in inline mode
(CONFIG_KASAN_INLINE)
: and static linking (CONFIG_STATIC_LINK), it does not support both at
the same time.

I've tested that for UML,
ARCH_DEFER_KASAN works if STATIC_LINK && KASAN_OUTLINE
ARCH_DEFER_KASAN works if KASAN_INLINE && !STATIC_LINK

ARCH_DEFER_KASAN if STATIC_LINK, and KASAN_INLINE=y by default from defconfig
crashes with SEGFAULT here (I didn't understand what it is, I think
the main() constructors
is not prepared in UML):

► 0 0x609d6f87 strlen+43
1 0x60a20db0 _dl_new_object+48
2 0x60a24627 _dl_non_dynamic_init+103
3 0x60a25f9a __libc_init_first+42
4 0x609eb6b2 __libc_start_main_impl+2434
5 0x6004a025 _start+37

Since this is the case only for UML, AFAIU, I don't think we want to change
conditions in lib/Kconfig.kasan. Shall I leave UML Kconfig as it is? e.g.

select ARCH_DEFER_KASAN

Sabyrzhan Tasbolatov

unread,
Aug 6, 2025, 12:37:58 AM8/6/25
to Andrey Ryabinin, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
Thanks, I've missed it. Here the upcoming v5 for this:

diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c
index d2681272d8f..0e6622b57ce 100644
--- a/arch/loongarch/mm/kasan_init.c
+++ b/arch/loongarch/mm/kasan_init.c
@@ -40,11 +40,9 @@ static pgd_t kasan_pg_dir[PTRS_PER_PGD] __initdata
__aligned(PAGE_SIZE);
#define __pte_none(early, pte) (early ? pte_none(pte) : \
((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page)))
-bool kasan_early_stage = true;
-
void *kasan_mem_to_shadow(const void *addr)
{
- if (!kasan_arch_is_ready()) {
+ if (!kasan_shadow_initialized()) {
return (void *)(kasan_early_shadow_page);
} else {
unsigned long maddr = (unsigned long)addr;
@@ -298,7 +296,10 @@ void __init kasan_init(void)
kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START),
kasan_mem_to_shadow((void *)KFENCE_AREA_END));
- kasan_early_stage = false;
+ /* Enable KASAN here before kasan_mem_to_shadow() which checks
+ * if kasan_shadow_initialized().
+ */
+ kasan_init_generic();
/* Populate the linear mapping */
for_each_mem_range(i, &pa_start, &pa_end) {
@@ -329,5 +330,4 @@ void __init kasan_init(void)
/* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0;
- pr_info("KernelAddressSanitizer initialized.\n");
}
--
2.34.1

Andrey Ryabinin

unread,
Aug 6, 2025, 9:35:08 AM8/6/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org


On 8/5/25 4:26 PM, Sabyrzhan Tasbolatov wrote:
> Introduce CONFIG_ARCH_DEFER_KASAN to identify architectures that need
> to defer KASAN initialization until shadow memory is properly set up,
> and unify the static key infrastructure across all KASAN modes.
>
> Some architectures (like PowerPC with radix MMU) need to set up their
> shadow memory mappings before KASAN can be safely enabled, while others
> (like s390, x86, arm) can enable KASAN much earlier or even from the
> beginning.
>
> Historically, the runtime static key kasan_flag_enabled existed only for
> CONFIG_KASAN_HW_TAGS mode. Generic and SW_TAGS modes either relied on
> architecture-specific kasan_arch_is_ready() implementations or evaluated
> KASAN checks unconditionally, leading to code duplication.
>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217049
> Signed-off-by: Sabyrzhan Tasbolatov <snov...@gmail.com>
> ---
> Changes in v4:
> - Fixed HW_TAGS static key functionality (was broken in v3)

I don't think it fixed. Before you patch kasan_enabled() esentially
worked like this:

if (IS_ENABLED(CONFIG_KASAN_HW_TAGS))
return static_branch_likely(&kasan_flag_enabled);
else
return IS_ENABLED(CONFIG_KASAN);

Now it's just IS_ENABLED(CONFIG_KASAN);

And there are bunch of kasan_enabled() calls left whose behavior changed for
no reason.
Don't rename it, just leave as is - kasan_enabled().
It's better name, shorter and you don't need to convert call sites, so
there is less chance of mistakes due to unchanged kasan_enabled() -> kasan_shadow_initialized().


> {
> return static_branch_likely(&kasan_flag_enabled);
> }
>
> -static inline bool kasan_hw_tags_enabled(void)
> +static inline void kasan_enable(void)
> +{
> + static_branch_enable(&kasan_flag_enabled);
> +}
> +#else
> +/* For architectures that can enable KASAN early, use compile-time check. */
> +static __always_inline bool kasan_shadow_initialized(void)
> {
> return kasan_enabled();
> }
>

...

>
> void kasan_populate_early_vm_area_shadow(void *start, unsigned long size);
> -int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
> -void kasan_release_vmalloc(unsigned long start, unsigned long end,
> +
> +int __kasan_populate_vmalloc(unsigned long addr, unsigned long size);
> +static inline int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
> +{
> + if (!kasan_shadow_initialized())
> + return 0;


What's the point of moving these checks to header?
Leave it in C, it's easier to grep and navigate code this way.


> + return __kasan_populate_vmalloc(addr, size);
> +}
> +
> +void __kasan_release_vmalloc(unsigned long start, unsigned long end,
> unsigned long free_region_start,
> unsigned long free_region_end,
> unsigned long flags);
> +static inline void kasan_release_vmalloc(unsigned long start,
> + unsigned long end,
> + unsigned long free_region_start,
> + unsigned long free_region_end,
> + unsigned long flags)
> +{
> + if (kasan_shadow_initialized())
> + __kasan_release_vmalloc(start, end, free_region_start,
> + free_region_end, flags);
> +}
>

...> @@ -250,7 +259,7 @@ static inline void poison_slab_object(struct kmem_cache *cache, void *object,
> bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
> unsigned long ip)
> {
> - if (!kasan_arch_is_ready() || is_kfence_address(object))
> + if (is_kfence_address(object))
> return false;
> return check_slab_allocation(cache, object, ip);
> }
> @@ -258,7 +267,7 @@ bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
> bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
> bool still_accessible)
> {
> - if (!kasan_arch_is_ready() || is_kfence_address(object))
> + if (is_kfence_address(object))
> return false;
>
> poison_slab_object(cache, object, init, still_accessible);
> @@ -282,9 +291,6 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
>
> static inline bool check_page_allocation(void *ptr, unsigned long ip)
> {
> - if (!kasan_arch_is_ready())
> - return false;
> -


Well, you can't do this yet, because no arch using ARCH_DEFER_KASAN yet, so this breaks
bisectability.
Leave it, and remove with separate patch only when there are no users left.

Andrey Ryabinin

unread,
Aug 6, 2025, 9:50:14 AM8/6/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
No surprise here, kasan_arch_is_ready() or ARCH_DEFER_KASAN doesn't work with KASAN_INLINE=y
This configuration combination (STATIC_LINK + KASAN_INLINE) wasn't possible before:

#ifndef kasan_arch_is_ready
static inline bool kasan_arch_is_ready(void) { return true; }
#elif !defined(CONFIG_KASAN_GENERIC) || !defined(CONFIG_KASAN_OUTLINE)
#error kasan_arch_is_ready only works in KASAN generic outline mode!
#endif



> Since this is the case only for UML, AFAIU, I don't think we want to change
> conditions in lib/Kconfig.kasan. Shall I leave UML Kconfig as it is? e.g.
>
> select ARCH_DEFER_KASAN
>

No, this should have if STATIC_LINK

Sabyrzhan Tasbolatov

unread,
Aug 6, 2025, 10:15:22 AM8/6/25
to Andrey Ryabinin, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
In v4 it is:

#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
static __always_inline bool kasan_shadow_initialized(void)
{
return static_branch_likely(&kasan_flag_enabled);
}
#else
static __always_inline bool kasan_shadow_initialized(void)
{
return kasan_enabled(); // which is IS_ENABLED(CONFIG_KASAN);
}
#endif

So for HW_TAGS, KASAN is enabled in kasan_init_hw_tags().

>
> And there are bunch of kasan_enabled() calls left whose behavior changed for
> no reason.

By having in v5 the only check kasan_enabled() and used in current mainline code
should be right. I've addressed this comment below. Thanks!
I actually had the only check "kasan_enabled()" in v2, but went to
double check approach in v3
after this comment:
https://lore.kernel.org/all/CA+fCnZcGyTECP15VMSPh+duLmxNe=ApHfOnbAY3...@mail.gmail.com/

Ok, we will have the **only** check kasan_enabled() then in
kasan-enabled.h which

#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
static __always_inline bool kasan_enabled(void)
{
return static_branch_likely(&kasan_flag_enabled);
}
#else
static inline bool kasan_enabled(void)
{
return IS_ENABLED(CONFIG_KASAN);
}

And will remove kasan_arch_is_ready (current kasan_shadow_initialized in v4).

So it is the single place to check if KASAN is enabled for all arch
and internal KASAN code.
Same behavior is in the current mainline code but only for HW_TAGS.

Is this correct?

>
>
> > {
> > return static_branch_likely(&kasan_flag_enabled);
> > }
> >
> > -static inline bool kasan_hw_tags_enabled(void)
> > +static inline void kasan_enable(void)
> > +{
> > + static_branch_enable(&kasan_flag_enabled);
> > +}
> > +#else
> > +/* For architectures that can enable KASAN early, use compile-time check. */
> > +static __always_inline bool kasan_shadow_initialized(void)
> > {
> > return kasan_enabled();
> > }
> >
>
> ...
>
> >
> > void kasan_populate_early_vm_area_shadow(void *start, unsigned long size);
> > -int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
> > -void kasan_release_vmalloc(unsigned long start, unsigned long end,
> > +
> > +int __kasan_populate_vmalloc(unsigned long addr, unsigned long size);
> > +static inline int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
> > +{
> > + if (!kasan_shadow_initialized())
> > + return 0;
>
>
> What's the point of moving these checks to header?
> Leave it in C, it's easier to grep and navigate code this way.

Andrey Konovalov had comments [1] to avoid checks in C
by moving them to headers under __wrappers.

: 1. Avoid spraying kasan_arch_is_ready() throughout the KASAN
: implementation and move these checks into include/linux/kasan.h (and
: add __wrappers when required).

[1] https://lore.kernel.org/all/CA+fCnZcGyTECP15VMSPh+duLmxNe=ApHfOnbAY3...@mail.gmail.com/
Will do in v5 at the end of patch series.

Andrey Ryabinin

unread,
Aug 6, 2025, 3:52:01 PM8/6/25
to Sabyrzhan Tasbolatov, h...@linux.ibm.com, christop...@csgroup.eu, andre...@gmail.com, agor...@linux.ibm.com, ak...@linux-foundation.org, zhan...@loongson.cn, chenh...@loongson.cn, trisha...@google.com, davi...@google.com, gli...@google.com, dvy...@google.com, kasa...@googlegroups.com, linux-...@vger.kernel.org, loon...@lists.linux.dev, linuxp...@lists.ozlabs.org, linux...@lists.infradead.org, linux...@vger.kernel.org, linu...@lists.infradead.org, linu...@kvack.org
You are referring to kasan_shadow_initialized(), but I was talking about kasan_enabled() specifically.
E.g. your patch changes behavior for kasan_init_slab_obj() which doesn't use kasan_shadow_initialized()
(in the case of HW_TAGS=y && kasan_flag_enabled = false) :

static __always_inline void * __must_check kasan_init_slab_obj(
struct kmem_cache *cache, const void *object)
{
if (kasan_enabled())
return __kasan_init_slab_obj(cache, object);
return (void *)object;
}



>>> +#if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
>>> +/*
>>> + * Global runtime flag for KASAN modes that need runtime control.
>>> + * Used by ARCH_DEFER_KASAN architectures and HW_TAGS mode.
>>> + */
>>> DECLARE_STATIC_KEY_FALSE(kasan_flag_enabled);
>>>
>>> -static __always_inline bool kasan_enabled(void)
>>> +/*
>>> + * Runtime control for shadow memory initialization or HW_TAGS mode.
>>> + * Uses static key for architectures that need deferred KASAN or HW_TAGS.
>>> + */
>>> +static __always_inline bool kasan_shadow_initialized(void)
>>
>> Don't rename it, just leave as is - kasan_enabled().
>> It's better name, shorter and you don't need to convert call sites, so
>> there is less chance of mistakes due to unchanged kasan_enabled() -> kasan_shadow_initialized().
>
> I actually had the only check "kasan_enabled()" in v2, but went to
> double check approach in v3
> after this comment:
> https://lore.kernel.org/all/CA+fCnZcGyTECP15VMSPh+duLmxNe=ApHfOnbAY3...@mail.gmail.com/

AFAIU the comment suggest that we need two checks/flags, one in kasan_enabled() which checks
whether kasan was enabled via cmdline (currently only for HW_TAGS)
and one in kasan_arch_is_ready()(or kasan_shadow_initialized()) which checks if arch initialized KASAN.
And this not what v3/v4 does. v4 basically have one check, just under different name.

Separate checks might be needed if we have code paths that need 'kasan_arch_is_ready() && !kasan_enabled()'
and vise versa '!kasan_arch_is_ready() && kasan_enabled()'.

From the top of my head, I can't say if we have such cases.

>
> Ok, we will have the **only** check kasan_enabled() then in
> kasan-enabled.h which
>
> #if defined(CONFIG_ARCH_DEFER_KASAN) || defined(CONFIG_KASAN_HW_TAGS)
> static __always_inline bool kasan_enabled(void)
> {
> return static_branch_likely(&kasan_flag_enabled);
> }
> #else
> static inline bool kasan_enabled(void)
> {
> return IS_ENABLED(CONFIG_KASAN);
> }
>
> And will remove kasan_arch_is_ready (current kasan_shadow_initialized in v4).
>
> So it is the single place to check if KASAN is enabled for all arch
> and internal KASAN code.
> Same behavior is in the current mainline code but only for HW_TAGS.
>
> Is this correct?
>

Yep, that's what I meant.
I think Andrey K. meant cases when we have multiple implementations of one function for each mode.
In such case it makes sense to merge multiple kasan_arch_is_ready() checks into one in the header.
But in case like with kasan_populate_vmalloc() we have only one implementation so I don't see any
value in adding wrapper/moving to header.
Reply all
Reply to author
Forward
0 new messages