* [PATCH v9 01/13] kasan: sw_tags: Use arithmetic shift for shadow computation
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 02/13] kasan: arm64: x86: Make special tags arch specific Maciej Wieczor-Retman
` (12 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Jonathan Corbet, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Andrew Morton, Jan Kiszka, Kieran Bingham,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: m.wieczorretman, Samuel Holland, Maciej Wieczor-Retman,
linux-arm-kernel, linux-doc, linux-kernel, kasan-dev, linux-mm,
llvm
From: Samuel Holland <samuel.holland@sifive.com>
Currently, kasan_mem_to_shadow() uses a logical right shift, which turns
canonical kernel addresses into non-canonical addresses by clearing the
high KASAN_SHADOW_SCALE_SHIFT bits. The value of KASAN_SHADOW_OFFSET is
then chosen so that the addition results in a canonical address for the
shadow memory.
For KASAN_GENERIC, this shift/add combination is ABI with the compiler,
because KASAN_SHADOW_OFFSET is used in compiler-generated inline tag
checks[1], which must only attempt to dereference canonical addresses.
However, for KASAN_SW_TAGS there is some freedom to change the algorithm
without breaking the ABI. Because TBI is enabled for kernel addresses,
the top bits of shadow memory addresses computed during tag checks are
irrelevant, and so likewise are the top bits of KASAN_SHADOW_OFFSET.
This is demonstrated by the fact that LLVM uses a logical right shift in
the tag check fast path[2] but a sbfx (signed bitfield extract)
instruction in the slow path[3] without causing any issues.
Use an arithmetic shift in kasan_mem_to_shadow() as it provides a number
of benefits:
1) The memory layout doesn't change but is easier to understand.
KASAN_SHADOW_OFFSET becomes a canonical memory address, and the shifted
pointer becomes a negative offset, so KASAN_SHADOW_OFFSET ==
KASAN_SHADOW_END regardless of the shift amount or the size of the
virtual address space.
2) KASAN_SHADOW_OFFSET becomes a simpler constant, requiring only one
instruction to load instead of two. Since it must be loaded in each
function with a tag check, this decreases kernel text size by 0.5%.
3) This shift and the sign extension from kasan_reset_tag() can be
combined into a single sbfx instruction. When this same algorithm change
is applied to the compiler, it removes an instruction from each inline
tag check, further reducing kernel text size by an additional 4.6%.
These benefits extend to other architectures as well. On RISC-V, where
the baseline ISA does not shifted addition or have an equivalent to the
sbfx instruction, loading KASAN_SHADOW_OFFSET is reduced from 3 to 2
instructions, and kasan_mem_to_shadow(kasan_reset_tag(addr)) similarly
combines two consecutive right shifts.
Add the arch_kasan_non_canonical_hook() to group the arch specific code
in the relevant arch directories.
Link: https://github.com/llvm/llvm-project/blob/llvmorg-20-init/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp#L1316 [1]
Link: https://github.com/llvm/llvm-project/blob/llvmorg-20-init/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp#L895 [2]
Link: https://github.com/llvm/llvm-project/blob/llvmorg-20-init/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp#L669 [3]
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Co-developed-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v9: (Maciej)
- Take out the arm64 related code from mm/kasan/report.c and put it in
the arch specific directory in a new file so the kasan_mem_to_shadow()
function can be included.
- Reset addr tag bits in arm64's arch_kasan_non_canonical_hook() so the
inline mode can also work with that function (Andrey Ryabinin).
- Fix incorrect number of zeros in a comment in mm/kasan/report.c.
- Remove Catalin's acked-by since changes were made.
Changelog v7: (Maciej)
- Change UL to ULL in report.c to fix some compilation warnings.
Changelog v6: (Maciej)
- Add Catalin's acked-by.
- Move x86 gdb snippet here from the last patch.
Changelog v5: (Maciej)
- (u64) -> (unsigned long) in report.c
Changelog v4: (Maciej)
- Revert x86 to signed mem_to_shadow mapping.
- Remove last two paragraphs since they were just poorer duplication of
the comments in kasan_non_canonical_hook().
Changelog v3: (Maciej)
- Fix scripts/gdb/linux/kasan.py so the new signed mem_to_shadow() is
reflected there.
- Fix Documentation/arch/arm64/kasan-offsets.sh to take new offsets into
account.
- Made changes to the kasan_non_canonical_hook() according to upstream
discussion. Settled on overflow on both ranges and separate checks for
x86 and arm.
Changelog v2: (Maciej)
- Correct address range that's checked in kasan_non_canonical_hook().
Adjust the comment inside.
- Remove part of comment from arch/arm64/include/asm/memory.h.
- Append patch message paragraph about the overflow in
kasan_non_canonical_hook().
Documentation/arch/arm64/kasan-offsets.sh | 8 ++++--
MAINTAINERS | 2 +-
arch/arm64/Kconfig | 10 +++----
arch/arm64/include/asm/kasan.h | 5 ++++
arch/arm64/include/asm/memory.h | 14 ++++++++-
arch/arm64/mm/Makefile | 2 ++
arch/arm64/mm/kasan_init.c | 7 +++--
arch/arm64/mm/kasan_sw_tags.c | 35 +++++++++++++++++++++++
include/linux/kasan.h | 10 +++++--
mm/kasan/kasan.h | 7 +++++
mm/kasan/report.c | 15 ++++++++--
scripts/gdb/linux/kasan.py | 5 +++-
scripts/gdb/linux/mm.py | 5 ++--
13 files changed, 106 insertions(+), 19 deletions(-)
create mode 100644 arch/arm64/mm/kasan_sw_tags.c
diff --git a/Documentation/arch/arm64/kasan-offsets.sh b/Documentation/arch/arm64/kasan-offsets.sh
index 2dc5f9e18039..ce777c7c7804 100644
--- a/Documentation/arch/arm64/kasan-offsets.sh
+++ b/Documentation/arch/arm64/kasan-offsets.sh
@@ -5,8 +5,12 @@
print_kasan_offset () {
printf "%02d\t" $1
- printf "0x%08x00000000\n" $(( (0xffffffff & (-1 << ($1 - 1 - 32))) \
- - (1 << (64 - 32 - $2)) ))
+ if [[ $2 -ne 4 ]] then
+ printf "0x%08x00000000\n" $(( (0xffffffff & (-1 << ($1 - 1 - 32))) \
+ - (1 << (64 - 32 - $2)) ))
+ else
+ printf "0x%08x00000000\n" $(( (0xffffffff & (-1 << ($1 - 1 - 32))) ))
+ fi
}
echo KASAN_SHADOW_SCALE_SHIFT = 3
diff --git a/MAINTAINERS b/MAINTAINERS
index da9dbc1a4019..8e0e776b66a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13585,7 +13585,7 @@ S: Maintained
B: https://bugzilla.kernel.org/buglist.cgi?component=Sanitizers&product=Memory%20Management
F: Documentation/dev-tools/kasan.rst
F: arch/*/include/asm/*kasan.h
-F: arch/*/mm/kasan_init*
+F: arch/*/mm/kasan*
F: include/linux/kasan*.h
F: lib/Kconfig.kasan
F: mm/kasan/
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 93173f0a09c7..c1b7261cdb96 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -434,11 +434,11 @@ config KASAN_SHADOW_OFFSET
default 0xdffffe0000000000 if ARM64_VA_BITS_42 && !KASAN_SW_TAGS
default 0xdfffffc000000000 if ARM64_VA_BITS_39 && !KASAN_SW_TAGS
default 0xdffffff800000000 if ARM64_VA_BITS_36 && !KASAN_SW_TAGS
- default 0xefff800000000000 if (ARM64_VA_BITS_48 || (ARM64_VA_BITS_52 && !ARM64_16K_PAGES)) && KASAN_SW_TAGS
- default 0xefffc00000000000 if (ARM64_VA_BITS_47 || ARM64_VA_BITS_52) && ARM64_16K_PAGES && KASAN_SW_TAGS
- default 0xeffffe0000000000 if ARM64_VA_BITS_42 && KASAN_SW_TAGS
- default 0xefffffc000000000 if ARM64_VA_BITS_39 && KASAN_SW_TAGS
- default 0xeffffff800000000 if ARM64_VA_BITS_36 && KASAN_SW_TAGS
+ default 0xffff800000000000 if (ARM64_VA_BITS_48 || (ARM64_VA_BITS_52 && !ARM64_16K_PAGES)) && KASAN_SW_TAGS
+ default 0xffffc00000000000 if (ARM64_VA_BITS_47 || ARM64_VA_BITS_52) && ARM64_16K_PAGES && KASAN_SW_TAGS
+ default 0xfffffe0000000000 if ARM64_VA_BITS_42 && KASAN_SW_TAGS
+ default 0xffffffc000000000 if ARM64_VA_BITS_39 && KASAN_SW_TAGS
+ default 0xfffffff800000000 if ARM64_VA_BITS_36 && KASAN_SW_TAGS
default 0xffffffffffffffff
config UNWIND_TABLES
diff --git a/arch/arm64/include/asm/kasan.h b/arch/arm64/include/asm/kasan.h
index b167e9d3da91..42d8e3092835 100644
--- a/arch/arm64/include/asm/kasan.h
+++ b/arch/arm64/include/asm/kasan.h
@@ -22,5 +22,10 @@ void kasan_init(void);
static inline void kasan_init(void) { }
#endif
+#ifdef CONFIG_KASAN_SW_TAGS
+bool __arch_kasan_non_canonical_hook(unsigned long addr);
+#define arch_kasan_non_canonical_hook(addr) __arch_kasan_non_canonical_hook(addr)
+#endif
+
#endif
#endif
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 9d54b2ea49d6..f127fbf691ac 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -89,7 +89,15 @@
*
* KASAN_SHADOW_END is defined first as the shadow address that corresponds to
* the upper bound of possible virtual kernel memory addresses UL(1) << 64
- * according to the mapping formula.
+ * according to the mapping formula. For Generic KASAN, the address in the
+ * mapping formula is treated as unsigned (part of the compiler's ABI), so the
+ * end of the shadow memory region is at a large positive offset from
+ * KASAN_SHADOW_OFFSET. For Software Tag-Based KASAN, the address in the
+ * formula is treated as signed. Since all kernel addresses are negative, they
+ * map to shadow memory below KASAN_SHADOW_OFFSET, making KASAN_SHADOW_OFFSET
+ * itself the end of the shadow memory region. (User pointers are positive and
+ * would map to shadow memory above KASAN_SHADOW_OFFSET, but shadow memory is
+ * not allocated for them.)
*
* KASAN_SHADOW_START is defined second based on KASAN_SHADOW_END. The shadow
* memory start must map to the lowest possible kernel virtual memory address
@@ -100,7 +108,11 @@
*/
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
#define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
+#ifdef CONFIG_KASAN_GENERIC
#define KASAN_SHADOW_END ((UL(1) << (64 - KASAN_SHADOW_SCALE_SHIFT)) + KASAN_SHADOW_OFFSET)
+#else
+#define KASAN_SHADOW_END KASAN_SHADOW_OFFSET
+#endif
#define _KASAN_SHADOW_START(va) (KASAN_SHADOW_END - (UL(1) << ((va) - KASAN_SHADOW_SCALE_SHIFT)))
#define KASAN_SHADOW_START _KASAN_SHADOW_START(vabits_actual)
#define PAGE_END KASAN_SHADOW_START
diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile
index c26489cf96cd..4658d59b7ea6 100644
--- a/arch/arm64/mm/Makefile
+++ b/arch/arm64/mm/Makefile
@@ -15,4 +15,6 @@ obj-$(CONFIG_ARM64_GCS) += gcs.o
KASAN_SANITIZE_physaddr.o += n
obj-$(CONFIG_KASAN) += kasan_init.o
+obj-$(CONFIG_KASAN_SW_TAGS) += kasan_sw_tags.o
KASAN_SANITIZE_kasan_init.o := n
+KASAN_SANITIZE_kasan_sw_tags.o := n
diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c
index abeb81bf6ebd..937f6eb8115b 100644
--- a/arch/arm64/mm/kasan_init.c
+++ b/arch/arm64/mm/kasan_init.c
@@ -198,8 +198,11 @@ static bool __init root_level_aligned(u64 addr)
/* The early shadow maps everything to a single page of zeroes */
asmlinkage void __init kasan_early_init(void)
{
- BUILD_BUG_ON(KASAN_SHADOW_OFFSET !=
- KASAN_SHADOW_END - (1UL << (64 - KASAN_SHADOW_SCALE_SHIFT)));
+ if (IS_ENABLED(CONFIG_KASAN_GENERIC))
+ BUILD_BUG_ON(KASAN_SHADOW_OFFSET !=
+ KASAN_SHADOW_END - (1UL << (64 - KASAN_SHADOW_SCALE_SHIFT)));
+ else
+ BUILD_BUG_ON(KASAN_SHADOW_OFFSET != KASAN_SHADOW_END);
BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS), SHADOW_ALIGN));
BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS_MIN), SHADOW_ALIGN));
BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, SHADOW_ALIGN));
diff --git a/arch/arm64/mm/kasan_sw_tags.c b/arch/arm64/mm/kasan_sw_tags.c
new file mode 100644
index 000000000000..d509db7bdc7e
--- /dev/null
+++ b/arch/arm64/mm/kasan_sw_tags.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file contains ARM64 specific KASAN sw_tags code.
+ */
+
+#include <linux/kasan.h>
+
+bool __arch_kasan_non_canonical_hook(unsigned long addr)
+{
+ /*
+ * For Software Tag-Based KASAN, kasan_mem_to_shadow() uses the
+ * arithmetic shift. Normally, this would make checking for a possible
+ * shadow address complicated, as the shadow address computation
+ * operation would overflow only for some memory addresses. However, due
+ * to the chosen KASAN_SHADOW_OFFSET values and the fact the
+ * kasan_mem_to_shadow() only operates on pointers with the tag reset,
+ * the overflow always happens.
+ *
+ * For arm64, the top byte of the pointer gets reset to 0xFF. Thus, the
+ * possible shadow addresses belong to a region that is the result of
+ * kasan_mem_to_shadow() applied to the memory range
+ * [0xFF00000000000000, 0xFFFFFFFFFFFFFFFF]. Despite the overflow, the
+ * resulting possible shadow region is contiguous, as the overflow
+ * happens for both 0xFF00000000000000 and 0xFFFFFFFFFFFFFFFF.
+ *
+ * Reset the addr's tag bits so the inline mode which still uses
+ * the logical shift can work correctly. Otherwise it would
+ * always return because of the 'smaller than' comparison below.
+ */
+ addr |= (0xFFULL << 56);
+ if (addr < (unsigned long)kasan_mem_to_shadow((void *)(0xFFULL << 56)) ||
+ addr > (unsigned long)kasan_mem_to_shadow((void *)(~0ULL)))
+ return true;
+ return false;
+}
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 9c6ac4b62eb9..0f65e88cc3f6 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -62,8 +62,14 @@ int kasan_populate_early_shadow(const void *shadow_start,
#ifndef kasan_mem_to_shadow
static inline void *kasan_mem_to_shadow(const void *addr)
{
- return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT)
- + KASAN_SHADOW_OFFSET;
+ void *scaled;
+
+ if (IS_ENABLED(CONFIG_KASAN_GENERIC))
+ scaled = (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT);
+ else
+ scaled = (void *)((long)addr >> KASAN_SHADOW_SCALE_SHIFT);
+
+ return KASAN_SHADOW_OFFSET + scaled;
}
#endif
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index fc9169a54766..02574e53d980 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -558,6 +558,13 @@ static inline bool kasan_arch_is_ready(void) { return true; }
#error kasan_arch_is_ready only works in KASAN generic outline mode!
#endif
+#ifndef arch_kasan_non_canonical_hook
+static inline bool arch_kasan_non_canonical_hook(unsigned long addr)
+{
+ return false;
+}
+#endif
+
#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
void kasan_kunit_test_suite_start(void);
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 62c01b4527eb..53152d148deb 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -642,10 +642,19 @@ void kasan_non_canonical_hook(unsigned long addr)
const char *bug_type;
/*
- * All addresses that came as a result of the memory-to-shadow mapping
- * (even for bogus pointers) must be >= KASAN_SHADOW_OFFSET.
+ * For Generic KASAN, kasan_mem_to_shadow() uses the logical right shift
+ * and never overflows with the chosen KASAN_SHADOW_OFFSET values. Thus,
+ * the possible shadow addresses (even for bogus pointers) belong to a
+ * single contiguous region that is the result of kasan_mem_to_shadow()
+ * applied to the whole address space.
*/
- if (addr < KASAN_SHADOW_OFFSET)
+ if (IS_ENABLED(CONFIG_KASAN_GENERIC)) {
+ if (addr < (unsigned long)kasan_mem_to_shadow((void *)(0ULL)) ||
+ addr > (unsigned long)kasan_mem_to_shadow((void *)(~0ULL)))
+ return;
+ }
+
+ if (arch_kasan_non_canonical_hook(addr))
return;
orig_addr = (unsigned long)kasan_shadow_to_mem((void *)addr);
diff --git a/scripts/gdb/linux/kasan.py b/scripts/gdb/linux/kasan.py
index 56730b3fde0b..4b86202b155f 100644
--- a/scripts/gdb/linux/kasan.py
+++ b/scripts/gdb/linux/kasan.py
@@ -7,7 +7,8 @@
#
import gdb
-from linux import constants, mm
+from linux import constants, utils, mm
+from ctypes import c_int64 as s64
def help():
t = """Usage: lx-kasan_mem_to_shadow [Hex memory addr]
@@ -39,6 +40,8 @@ class KasanMemToShadow(gdb.Command):
else:
help()
def kasan_mem_to_shadow(self, addr):
+ if constants.CONFIG_KASAN_SW_TAGS and not utils.is_target_arch('x86'):
+ addr = s64(addr)
return (addr >> self.p_ops.KASAN_SHADOW_SCALE_SHIFT) + self.p_ops.KASAN_SHADOW_OFFSET
KasanMemToShadow()
diff --git a/scripts/gdb/linux/mm.py b/scripts/gdb/linux/mm.py
index 7571aebbe650..2e63f3dedd53 100644
--- a/scripts/gdb/linux/mm.py
+++ b/scripts/gdb/linux/mm.py
@@ -110,12 +110,13 @@ class aarch64_page_ops():
self.KERNEL_END = gdb.parse_and_eval("_end")
if constants.LX_CONFIG_KASAN_GENERIC or constants.LX_CONFIG_KASAN_SW_TAGS:
+ self.KASAN_SHADOW_OFFSET = constants.LX_CONFIG_KASAN_SHADOW_OFFSET
if constants.LX_CONFIG_KASAN_GENERIC:
self.KASAN_SHADOW_SCALE_SHIFT = 3
+ self.KASAN_SHADOW_END = (1 << (64 - self.KASAN_SHADOW_SCALE_SHIFT)) + self.KASAN_SHADOW_OFFSET
else:
self.KASAN_SHADOW_SCALE_SHIFT = 4
- self.KASAN_SHADOW_OFFSET = constants.LX_CONFIG_KASAN_SHADOW_OFFSET
- self.KASAN_SHADOW_END = (1 << (64 - self.KASAN_SHADOW_SCALE_SHIFT)) + self.KASAN_SHADOW_OFFSET
+ self.KASAN_SHADOW_END = self.KASAN_SHADOW_OFFSET
self.PAGE_END = self.KASAN_SHADOW_END - (1 << (self.vabits_actual - self.KASAN_SHADOW_SCALE_SHIFT))
else:
self.PAGE_END = self._PAGE_END(self.VA_BITS_MIN)
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 02/13] kasan: arm64: x86: Make special tags arch specific
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 01/13] kasan: sw_tags: Use arithmetic shift for shadow computation Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 03/13] kasan: Fix inline mode for x86 tag-based mode Maciej Wieczor-Retman
` (11 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko
Cc: m.wieczorretman, Samuel Holland, Maciej Wieczor-Retman,
linux-kernel, linux-arm-kernel, kasan-dev, linux-mm
From: Samuel Holland <samuel.holland@sifive.com>
KASAN's tag-based mode defines multiple special tag values. They're
reserved for:
- Native kernel value. On arm64 it's 0xFF and it causes an early return
in the tag checking function.
- Invalid value. 0xFE marks an area as freed / unallocated. It's also
the value that is used to initialize regions of shadow memory.
- Min and max values. 0xFD is the highest value that can be randomly
generated for a new tag. 0 is the minimal value with the exception of
arm64's hardware mode where it is equal to 0xF0.
Metadata macro is also defined:
- Tag width equal to 8.
Tag-based mode on x86 is going to use 4 bit wide tags so all the above
values need to be changed accordingly.
Make tag width and native kernel tag arch specific for x86 and arm64.
Base the invalid tag value and the max value on the native kernel tag
since they follow the same pattern on both mentioned architectures.
Also generalize KASAN_SHADOW_INIT and 0xff used in various
page_kasan_tag* helpers.
Give KASAN_TAG_MIN the default value of zero, and move the special value
for hw_tags arm64 to its arch specific kasan-tags.h.
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Co-developed-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Acked-by: Will Deacon <will@kernel.org> (for the arm part)
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
Reviewed-by: Andrey Ryabinin <ryabinin.a.a@gmail.com>
---
Changelog v9:
- Add Andrey Ryabinin's Reviewed-by tag.
- Add Andrey Konovalov's Reviewed-by tag.
Changelog v8:
- Add Will's Acked-by tag.
Changelog v7:
- Reorder defines of arm64 tag width to prevent redefinition warnings.
- Remove KASAN_TAG_MASK so it's only defined in mmzone.h (Andrey
Konovalov)
- Merge the 'support tag widths less than 8 bits' with this patch since
they do similar things and overwrite each other. (Alexander)
Changelog v6:
- Add hardware tags KASAN_TAG_WIDTH value to the arm64 arch file.
- Keep KASAN_TAG_MASK in the mmzone.h.
- Remove ifndef from KASAN_SHADOW_INIT.
Changelog v5:
- Move KASAN_TAG_MIN to the arm64 kasan-tags.h for the hardware KASAN
mode case.
Changelog v4:
- Move KASAN_TAG_MASK to kasan-tags.h.
Changelog v2:
- Remove risc-v from the patch.
MAINTAINERS | 2 +-
arch/arm64/include/asm/kasan-tags.h | 14 ++++++++++++++
arch/arm64/include/asm/kasan.h | 2 --
arch/arm64/include/asm/uaccess.h | 1 +
arch/x86/include/asm/kasan-tags.h | 9 +++++++++
include/linux/kasan-tags.h | 19 ++++++++++++++-----
include/linux/kasan.h | 3 +--
include/linux/mm.h | 6 +++---
include/linux/page-flags-layout.h | 9 +--------
9 files changed, 44 insertions(+), 21 deletions(-)
create mode 100644 arch/arm64/include/asm/kasan-tags.h
create mode 100644 arch/x86/include/asm/kasan-tags.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 8e0e776b66a7..87c0f0e44f47 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13584,7 +13584,7 @@ L: kasan-dev@googlegroups.com
S: Maintained
B: https://bugzilla.kernel.org/buglist.cgi?component=Sanitizers&product=Memory%20Management
F: Documentation/dev-tools/kasan.rst
-F: arch/*/include/asm/*kasan.h
+F: arch/*/include/asm/*kasan*.h
F: arch/*/mm/kasan*
F: include/linux/kasan*.h
F: lib/Kconfig.kasan
diff --git a/arch/arm64/include/asm/kasan-tags.h b/arch/arm64/include/asm/kasan-tags.h
new file mode 100644
index 000000000000..259952677443
--- /dev/null
+++ b/arch/arm64/include/asm/kasan-tags.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_KASAN_TAGS_H
+#define __ASM_KASAN_TAGS_H
+
+#define KASAN_TAG_KERNEL 0xFF /* native kernel pointers tag */
+
+#ifdef CONFIG_KASAN_HW_TAGS
+#define KASAN_TAG_MIN 0xF0 /* minimum value for random tags */
+#define KASAN_TAG_WIDTH 4
+#else
+#define KASAN_TAG_WIDTH 8
+#endif
+
+#endif /* ASM_KASAN_TAGS_H */
diff --git a/arch/arm64/include/asm/kasan.h b/arch/arm64/include/asm/kasan.h
index 42d8e3092835..ad10931ddae7 100644
--- a/arch/arm64/include/asm/kasan.h
+++ b/arch/arm64/include/asm/kasan.h
@@ -6,8 +6,6 @@
#include <linux/linkage.h>
#include <asm/memory.h>
-#include <asm/mte-kasan.h>
-#include <asm/pgtable-types.h>
#define arch_kasan_set_tag(addr, tag) __tag_set(addr, tag)
#define arch_kasan_reset_tag(addr) __tag_reset(addr)
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 6490930deef8..ccd41a39e3a1 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -22,6 +22,7 @@
#include <asm/cpufeature.h>
#include <asm/mmu.h>
#include <asm/mte.h>
+#include <asm/mte-kasan.h>
#include <asm/ptrace.h>
#include <asm/memory.h>
#include <asm/extable.h>
diff --git a/arch/x86/include/asm/kasan-tags.h b/arch/x86/include/asm/kasan-tags.h
new file mode 100644
index 000000000000..68ba385bc75c
--- /dev/null
+++ b/arch/x86/include/asm/kasan-tags.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_KASAN_TAGS_H
+#define __ASM_KASAN_TAGS_H
+
+#define KASAN_TAG_KERNEL 0xF /* native kernel pointers tag */
+
+#define KASAN_TAG_WIDTH 4
+
+#endif /* ASM_KASAN_TAGS_H */
diff --git a/include/linux/kasan-tags.h b/include/linux/kasan-tags.h
index 4f85f562512c..ad5c11950233 100644
--- a/include/linux/kasan-tags.h
+++ b/include/linux/kasan-tags.h
@@ -2,13 +2,22 @@
#ifndef _LINUX_KASAN_TAGS_H
#define _LINUX_KASAN_TAGS_H
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
+#include <asm/kasan-tags.h>
+#endif
+
+#ifndef KASAN_TAG_WIDTH
+#define KASAN_TAG_WIDTH 0
+#endif
+
+#ifndef KASAN_TAG_KERNEL
#define KASAN_TAG_KERNEL 0xFF /* native kernel pointers tag */
-#define KASAN_TAG_INVALID 0xFE /* inaccessible memory tag */
-#define KASAN_TAG_MAX 0xFD /* maximum value for random tags */
+#endif
+
+#define KASAN_TAG_INVALID (KASAN_TAG_KERNEL - 1) /* inaccessible memory tag */
+#define KASAN_TAG_MAX (KASAN_TAG_KERNEL - 2) /* maximum value for random tags */
-#ifdef CONFIG_KASAN_HW_TAGS
-#define KASAN_TAG_MIN 0xF0 /* minimum value for random tags */
-#else
+#ifndef KASAN_TAG_MIN
#define KASAN_TAG_MIN 0x00 /* minimum value for random tags */
#endif
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 0f65e88cc3f6..1c7acdb5f297 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -40,8 +40,7 @@ typedef unsigned int __bitwise kasan_vmalloc_flags_t;
/* Software KASAN implementations use shadow memory. */
#ifdef CONFIG_KASAN_SW_TAGS
-/* This matches KASAN_TAG_INVALID. */
-#define KASAN_SHADOW_INIT 0xFE
+#define KASAN_SHADOW_INIT KASAN_TAG_INVALID
#else
#define KASAN_SHADOW_INIT 0
#endif
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6f959d8ca4b4..8ba91f38a794 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1949,7 +1949,7 @@ static inline u8 page_kasan_tag(const struct page *page)
if (kasan_enabled()) {
tag = (page->flags.f >> KASAN_TAG_PGSHIFT) & KASAN_TAG_MASK;
- tag ^= 0xff;
+ tag ^= KASAN_TAG_KERNEL;
}
return tag;
@@ -1962,7 +1962,7 @@ static inline void page_kasan_tag_set(struct page *page, u8 tag)
if (!kasan_enabled())
return;
- tag ^= 0xff;
+ tag ^= KASAN_TAG_KERNEL;
old_flags = READ_ONCE(page->flags.f);
do {
flags = old_flags;
@@ -1981,7 +1981,7 @@ static inline void page_kasan_tag_reset(struct page *page)
static inline u8 page_kasan_tag(const struct page *page)
{
- return 0xff;
+ return KASAN_TAG_KERNEL;
}
static inline void page_kasan_tag_set(struct page *page, u8 tag) { }
diff --git a/include/linux/page-flags-layout.h b/include/linux/page-flags-layout.h
index 760006b1c480..b2cc4cb870e0 100644
--- a/include/linux/page-flags-layout.h
+++ b/include/linux/page-flags-layout.h
@@ -3,6 +3,7 @@
#define PAGE_FLAGS_LAYOUT_H
#include <linux/numa.h>
+#include <linux/kasan-tags.h>
#include <generated/bounds.h>
/*
@@ -72,14 +73,6 @@
#define NODE_NOT_IN_PAGE_FLAGS 1
#endif
-#if defined(CONFIG_KASAN_SW_TAGS)
-#define KASAN_TAG_WIDTH 8
-#elif defined(CONFIG_KASAN_HW_TAGS)
-#define KASAN_TAG_WIDTH 4
-#else
-#define KASAN_TAG_WIDTH 0
-#endif
-
#ifdef CONFIG_NUMA_BALANCING
#define LAST__PID_SHIFT 8
#define LAST__PID_MASK ((1 << LAST__PID_SHIFT)-1)
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 03/13] kasan: Fix inline mode for x86 tag-based mode
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 01/13] kasan: sw_tags: Use arithmetic shift for shadow computation Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 02/13] kasan: arm64: x86: Make special tags arch specific Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 04/13] x86/kasan: Add arch specific kasan functions Maciej Wieczor-Retman
` (10 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Nathan Chancellor, Nicolas Schier, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: m.wieczorretman, Maciej Wieczor-Retman, linux-kbuild, kasan-dev,
linux-kernel, llvm
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
The LLVM compiler uses hwasan-instrument-with-calls parameter to setup
inline or outline mode in tag-based KASAN. If zeroed, it means the
instrumentation implementation will be pasted into each relevant
location along with KASAN related constants during compilation. If set
to one all function instrumentation will be done with function calls
instead.
The default hwasan-instrument-with-calls value for the x86 architecture
in the compiler is "1", which is not true for other architectures.
Because of this, enabling inline mode in software tag-based KASAN
doesn't work on x86 as the kernel script doesn't zero out the parameter
and always sets up the outline mode.
Explicitly zero out hwasan-instrument-with-calls when enabling inline
mode in tag-based KASAN.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
Reviewed-by: Alexander Potapenko <glider@google.com>
Reviewed-by: Andrey Ryabinin <ryabinin.a.a@gmail.com>
---
Changelog v9:
- Add Andrey Ryabinin's Reviewed-by tag.
Changelog v7:
- Add Alexander's Reviewed-by tag.
Changelog v6:
- Add Andrey's Reviewed-by tag.
Changelog v3:
- Add this patch to the series.
scripts/Makefile.kasan | 3 +++
1 file changed, 3 insertions(+)
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 0ba2aac3b8dc..e485814df3e9 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -76,8 +76,11 @@ CFLAGS_KASAN := -fsanitize=kernel-hwaddress
RUSTFLAGS_KASAN := -Zsanitizer=kernel-hwaddress \
-Zsanitizer-recover=kernel-hwaddress
+# LLVM sets hwasan-instrument-with-calls to 1 on x86 by default. Set it to 0
+# when inline mode is enabled.
ifdef CONFIG_KASAN_INLINE
kasan_params += hwasan-mapping-offset=$(KASAN_SHADOW_OFFSET)
+ kasan_params += hwasan-instrument-with-calls=0
else
kasan_params += hwasan-instrument-with-calls=1
endif
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 04/13] x86/kasan: Add arch specific kasan functions
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (2 preceding siblings ...)
2026-01-20 14:41 ` [PATCH v9 03/13] kasan: Fix inline mode for x86 tag-based mode Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions Maciej Wieczor-Retman
` (9 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov,
Dmitry Vyukov, Vincenzo Frascino, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Andrew Morton,
Axel Rasmussen, Yuanchu Xie, Wei Xu, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: m.wieczorretman, Maciej Wieczor-Retman, kasan-dev, linux-kernel,
linux-mm
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
KASAN's software tag-based mode needs multiple macros/functions to
handle tag and pointer interactions - to set, retrieve and reset tags
from the top bits of a pointer.
Mimic functions currently used by arm64 but change the tag's position to
bits [60:57] in the pointer.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Reviewed-by: Andrey Ryabinin <ryabinin.a.a@gmail.com>
---
Changelog v9:
- Rename KASAN_TAG_BYTE_MASK to KASAN_TAG_BITS_MASK.
- Add Andrey Ryabinin's Reviewed-by tag.
Changelog v7:
- Add KASAN_TAG_BYTE_MASK to avoid circular includes and avoid removing
KASAN_TAG_MASK from mmzone.h.
- Remove Andrey's Acked-by tag.
Changelog v6:
- Remove empty line after ifdef CONFIG_KASAN_SW_TAGS
- Add ifdef 64 bit to avoid problems in vdso32.
- Add Andrey's Acked-by tag.
Changelog v4:
- Rewrite __tag_set() without pointless casts and make it more readable.
Changelog v3:
- Reorder functions so that __tag_*() etc are above the
arch_kasan_*() ones.
- Remove CONFIG_KASAN condition from __tag_set()
arch/x86/include/asm/kasan.h | 42 ++++++++++++++++++++++++++++++++++--
include/linux/kasan-tags.h | 2 ++
include/linux/mmzone.h | 2 +-
3 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/arch/x86/include/asm/kasan.h b/arch/x86/include/asm/kasan.h
index d7e33c7f096b..c868ae734f68 100644
--- a/arch/x86/include/asm/kasan.h
+++ b/arch/x86/include/asm/kasan.h
@@ -3,6 +3,8 @@
#define _ASM_X86_KASAN_H
#include <linux/const.h>
+#include <linux/kasan-tags.h>
+#include <linux/types.h>
#define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
#define KASAN_SHADOW_SCALE_SHIFT 3
@@ -24,8 +26,43 @@
KASAN_SHADOW_SCALE_SHIFT)))
#ifndef __ASSEMBLER__
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#ifdef CONFIG_KASAN_SW_TAGS
+#define __tag_shifted(tag) FIELD_PREP(GENMASK_ULL(60, 57), tag)
+#define __tag_reset(addr) (sign_extend64((u64)(addr), 56))
+#define __tag_get(addr) ((u8)FIELD_GET(GENMASK_ULL(60, 57), (u64)addr))
+#else
+#define __tag_shifted(tag) 0UL
+#define __tag_reset(addr) (addr)
+#define __tag_get(addr) 0
+#endif /* CONFIG_KASAN_SW_TAGS */
+
+#ifdef CONFIG_64BIT
+static inline void *__tag_set(const void *__addr, u8 tag)
+{
+ u64 addr = (u64)__addr;
+
+ addr &= ~__tag_shifted(KASAN_TAG_BITS_MASK);
+ addr |= __tag_shifted(tag & KASAN_TAG_BITS_MASK);
+
+ return (void *)addr;
+}
+#else
+static inline void *__tag_set(void *__addr, u8 tag)
+{
+ return __addr;
+}
+#endif
+
+#define arch_kasan_set_tag(addr, tag) __tag_set(addr, tag)
+#define arch_kasan_reset_tag(addr) __tag_reset(addr)
+#define arch_kasan_get_tag(addr) __tag_get(addr)
#ifdef CONFIG_KASAN
+
void __init kasan_early_init(void);
void __init kasan_init(void);
void __init kasan_populate_shadow_for_vaddr(void *va, size_t size, int nid);
@@ -34,8 +71,9 @@ static inline void kasan_early_init(void) { }
static inline void kasan_init(void) { }
static inline void kasan_populate_shadow_for_vaddr(void *va, size_t size,
int nid) { }
-#endif
-#endif
+#endif /* CONFIG_KASAN */
+
+#endif /* __ASSEMBLER__ */
#endif
diff --git a/include/linux/kasan-tags.h b/include/linux/kasan-tags.h
index ad5c11950233..36cc4f70674a 100644
--- a/include/linux/kasan-tags.h
+++ b/include/linux/kasan-tags.h
@@ -10,6 +10,8 @@
#define KASAN_TAG_WIDTH 0
#endif
+#define KASAN_TAG_BITS_MASK ((1UL << KASAN_TAG_WIDTH) - 1)
+
#ifndef KASAN_TAG_KERNEL
#define KASAN_TAG_KERNEL 0xFF /* native kernel pointers tag */
#endif
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 75ef7c9f9307..631200332c45 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -1177,7 +1177,7 @@ static inline bool zone_is_empty(const struct zone *zone)
#define NODES_MASK ((1UL << NODES_WIDTH) - 1)
#define SECTIONS_MASK ((1UL << SECTIONS_WIDTH) - 1)
#define LAST_CPUPID_MASK ((1UL << LAST_CPUPID_SHIFT) - 1)
-#define KASAN_TAG_MASK ((1UL << KASAN_TAG_WIDTH) - 1)
+#define KASAN_TAG_MASK KASAN_TAG_BITS_MASK
#define ZONEID_MASK ((1UL << ZONEID_SHIFT) - 1)
static inline enum zone_type memdesc_zonenum(memdesc_flags_t flags)
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (3 preceding siblings ...)
2026-01-20 14:41 ` [PATCH v9 04/13] x86/kasan: Add arch specific kasan functions Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-02-23 20:33 ` Dave Hansen
2026-01-20 14:41 ` [PATCH v9 06/13] mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic Maciej Wieczor-Retman
` (8 subsequent siblings)
13 siblings, 1 reply; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Andy Lutomirski, Peter Zijlstra
Cc: m.wieczorretman, Maciej Wieczor-Retman, Alexander Potapenko,
linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Any place where pointer arithmetic is used to convert a virtual address
into a physical one can raise errors if the virtual address is tagged.
Reset the pointer's tag by sign extending the tag bits in macros that do
pointer arithmetic in address conversions. There will be no change in
compiled code with KASAN disabled since the compiler will optimize the
__tag_reset() out.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Acked-by: Alexander Potapenko <glider@google.com>
---
Changelog v7:
- Add Alexander's Acked-by tag.
Changelog v5:
- Move __tag_reset() calls into __phys_addr_nodebug() and
__virt_addr_valid() instead of calling it on the arguments of higher
level functions.
Changelog v4:
- Simplify page_to_virt() by removing pointless casts.
- Remove change in __is_canonical_address() because it's taken care of
in a later patch due to a LAM compatible definition of canonical.
arch/x86/include/asm/page.h | 8 ++++++++
arch/x86/include/asm/page_64.h | 1 +
arch/x86/mm/physaddr.c | 2 ++
3 files changed, 11 insertions(+)
diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h
index 9265f2fca99a..bcf5cad3da36 100644
--- a/arch/x86/include/asm/page.h
+++ b/arch/x86/include/asm/page.h
@@ -7,6 +7,7 @@
#ifdef __KERNEL__
#include <asm/page_types.h>
+#include <asm/kasan.h>
#ifdef CONFIG_X86_64
#include <asm/page_64.h>
@@ -65,6 +66,13 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
* virt_to_page(kaddr) returns a valid pointer if and only if
* virt_addr_valid(kaddr) returns true.
*/
+
+#ifdef CONFIG_KASAN_SW_TAGS
+#define page_to_virt(x) ({ \
+ void *__addr = __va(page_to_pfn((struct page *)x) << PAGE_SHIFT); \
+ __tag_set(__addr, page_kasan_tag(x)); \
+})
+#endif
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
extern bool __virt_addr_valid(unsigned long kaddr);
#define virt_addr_valid(kaddr) __virt_addr_valid((unsigned long) (kaddr))
diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h
index 2f0e47be79a4..01f9e6233bba 100644
--- a/arch/x86/include/asm/page_64.h
+++ b/arch/x86/include/asm/page_64.h
@@ -22,6 +22,7 @@ extern unsigned long direct_map_physmem_end;
static __always_inline unsigned long __phys_addr_nodebug(unsigned long x)
{
+ x = __tag_reset(x);
unsigned long y = x - __START_KERNEL_map;
/* use the carry flag to determine if x was < __START_KERNEL_map */
diff --git a/arch/x86/mm/physaddr.c b/arch/x86/mm/physaddr.c
index 8d31c6b9e184..8f18273be0d2 100644
--- a/arch/x86/mm/physaddr.c
+++ b/arch/x86/mm/physaddr.c
@@ -14,6 +14,7 @@
#ifdef CONFIG_DEBUG_VIRTUAL
unsigned long __phys_addr(unsigned long x)
{
+ x = __tag_reset(x);
unsigned long y = x - __START_KERNEL_map;
/* use the carry flag to determine if x was < __START_KERNEL_map */
@@ -35,6 +36,7 @@ EXPORT_SYMBOL(__phys_addr);
bool __virt_addr_valid(unsigned long x)
{
+ x = __tag_reset(x);
unsigned long y = x - __START_KERNEL_map;
/* use the carry flag to determine if x was < __START_KERNEL_map */
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions
2026-01-20 14:41 ` [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions Maciej Wieczor-Retman
@ 2026-02-23 20:33 ` Dave Hansen
2026-02-25 8:17 ` Maciej Wieczor-Retman
0 siblings, 1 reply; 21+ messages in thread
From: Dave Hansen @ 2026-02-23 20:33 UTC (permalink / raw)
To: Maciej Wieczor-Retman, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Andy Lutomirski, Peter Zijlstra
Cc: Maciej Wieczor-Retman, Alexander Potapenko, linux-kernel
> #ifdef CONFIG_X86_64
> #include <asm/page_64.h>
> @@ -65,6 +66,13 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
> * virt_to_page(kaddr) returns a valid pointer if and only if
> * virt_addr_valid(kaddr) returns true.
> */
> +
> +#ifdef CONFIG_KASAN_SW_TAGS
> +#define page_to_virt(x) ({ \
> + void *__addr = __va(page_to_pfn((struct page *)x) << PAGE_SHIFT); \
> + __tag_set(__addr, page_kasan_tag(x)); \
> +})
> +#endif
Can we pretty please keep this in arch-independent code?
The idea of tags is not x86-specific and I can almost guarantee that x86
won't be the last one needing this.
On to the rest...
> Reset the pointer's tag by sign extending the tag bits in macros that do
> pointer arithmetic in address conversions. There will be no change in
> compiled code with KASAN disabled since the compiler will optimize the
> __tag_reset() out.
Nit: there's no "macro" for the rest. They're functions. I also don't
_care_ how __tag_reset() works here. Don't explain implementation details.
> index 2f0e47be79a4..01f9e6233bba 100644
> --- a/arch/x86/include/asm/page_64.h
> +++ b/arch/x86/include/asm/page_64.h
> @@ -22,6 +22,7 @@ extern unsigned long direct_map_physmem_end;
>
> static __always_inline unsigned long __phys_addr_nodebug(unsigned long x)
> {
> + x = __tag_reset(x);
> unsigned long y = x - __START_KERNEL_map;
>
> /* use the carry flag to determine if x was < __START_KERNEL_map */
> diff --git a/arch/x86/mm/physaddr.c b/arch/x86/mm/physaddr.c
> index 8d31c6b9e184..8f18273be0d2 100644
> --- a/arch/x86/mm/physaddr.c
> +++ b/arch/x86/mm/physaddr.c
> @@ -14,6 +14,7 @@
> #ifdef CONFIG_DEBUG_VIRTUAL
> unsigned long __phys_addr(unsigned long x)
> {
> + x = __tag_reset(x);
> unsigned long y = x - __START_KERNEL_map;
I know all the virt-to/from-phys functions are a mess. But could we
please take a wee peek here at refactoring them so this doesn't need to
be done *twice*?
I also think the changelog here needs to include something about the
idea that arbitrary kernel pointers are assumed to be tagged and that
these functions mostly accept those arbitrary pointers.
> @@ -35,6 +36,7 @@ EXPORT_SYMBOL(__phys_addr);
>
> bool __virt_addr_valid(unsigned long x)
> {
> + x = __tag_reset(x);
> unsigned long y = x - __START_KERNEL_map;
>
> /* use the carry flag to determine if x was < __START_KERNEL_map */
It also occurs to me that having a helper that does:
x - __START_KERNEL_map;
it could do the tag reset, which might consolidate all of these sites.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions
2026-02-23 20:33 ` Dave Hansen
@ 2026-02-25 8:17 ` Maciej Wieczor-Retman
2026-02-25 14:48 ` Dave Hansen
0 siblings, 1 reply; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-02-25 8:17 UTC (permalink / raw)
To: Dave Hansen
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Andy Lutomirski, Peter Zijlstra,
Maciej Wieczor-Retman, Alexander Potapenko, linux-kernel
On 2026-02-23 at 12:33:01 -0800, Dave Hansen wrote:
>> #ifdef CONFIG_X86_64
>> #include <asm/page_64.h>
>> @@ -65,6 +66,13 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
>> * virt_to_page(kaddr) returns a valid pointer if and only if
>> * virt_addr_valid(kaddr) returns true.
>> */
>> +
>> +#ifdef CONFIG_KASAN_SW_TAGS
>> +#define page_to_virt(x) ({ \
>> + void *__addr = __va(page_to_pfn((struct page *)x) << PAGE_SHIFT); \
>> + __tag_set(__addr, page_kasan_tag(x)); \
>> +})
>> +#endif
>
>Can we pretty please keep this in arch-independent code?
>
>The idea of tags is not x86-specific and I can almost guarantee that x86
>won't be the last one needing this.
Do you mean adding a KASAN oriented page_to_virt() that's arch independent? I'd
guess next to the regular one, in include/linux/mm.h?
arm64's pretty similar so I suppose it should work.
>On to the rest...
>
>> Reset the pointer's tag by sign extending the tag bits in macros that do
>> pointer arithmetic in address conversions. There will be no change in
>> compiled code with KASAN disabled since the compiler will optimize the
>> __tag_reset() out.
>
>Nit: there's no "macro" for the rest. They're functions. I also don't
>_care_ how __tag_reset() works here. Don't explain implementation details.
Okay, I'll rewrite it.
>> index 2f0e47be79a4..01f9e6233bba 100644
>> --- a/arch/x86/include/asm/page_64.h
>> +++ b/arch/x86/include/asm/page_64.h
>> @@ -22,6 +22,7 @@ extern unsigned long direct_map_physmem_end;
>>
>> static __always_inline unsigned long __phys_addr_nodebug(unsigned long x)
>> {
>> + x = __tag_reset(x);
>> unsigned long y = x - __START_KERNEL_map;
>>
>> /* use the carry flag to determine if x was < __START_KERNEL_map */
>> diff --git a/arch/x86/mm/physaddr.c b/arch/x86/mm/physaddr.c
>> index 8d31c6b9e184..8f18273be0d2 100644
>> --- a/arch/x86/mm/physaddr.c
>> +++ b/arch/x86/mm/physaddr.c
>> @@ -14,6 +14,7 @@
>> #ifdef CONFIG_DEBUG_VIRTUAL
>> unsigned long __phys_addr(unsigned long x)
>> {
>> + x = __tag_reset(x);
>> unsigned long y = x - __START_KERNEL_map;
>
>I know all the virt-to/from-phys functions are a mess. But could we
>please take a wee peek here at refactoring them so this doesn't need to
>be done *twice*?
>
>I also think the changelog here needs to include something about the
>idea that arbitrary kernel pointers are assumed to be tagged and that
>these functions mostly accept those arbitrary pointers.
>
Okay, I'll add that in.
>
>> @@ -35,6 +36,7 @@ EXPORT_SYMBOL(__phys_addr);
>>
>> bool __virt_addr_valid(unsigned long x)
>> {
>> + x = __tag_reset(x);
>> unsigned long y = x - __START_KERNEL_map;
>>
>> /* use the carry flag to determine if x was < __START_KERNEL_map */
>
>It also occurs to me that having a helper that does:
>
> x - __START_KERNEL_map;
>
>it could do the tag reset, which might consolidate all of these sites.
Thanks! It's a good idea, I'll do that.
--
Kind regards
Maciej Wieczór-Retman
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions
2026-02-25 8:17 ` Maciej Wieczor-Retman
@ 2026-02-25 14:48 ` Dave Hansen
0 siblings, 0 replies; 21+ messages in thread
From: Dave Hansen @ 2026-02-25 14:48 UTC (permalink / raw)
To: Maciej Wieczor-Retman
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Andy Lutomirski, Peter Zijlstra,
Maciej Wieczor-Retman, Alexander Potapenko, linux-kernel
On 2/25/26 00:17, Maciej Wieczor-Retman wrote:
> On 2026-02-23 at 12:33:01 -0800, Dave Hansen wrote:
>>> #ifdef CONFIG_X86_64
>>> #include <asm/page_64.h>
>>> @@ -65,6 +66,13 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
>>> * virt_to_page(kaddr) returns a valid pointer if and only if
>>> * virt_addr_valid(kaddr) returns true.
>>> */
>>> +
>>> +#ifdef CONFIG_KASAN_SW_TAGS
>>> +#define page_to_virt(x) ({ \
>>> + void *__addr = __va(page_to_pfn((struct page *)x) << PAGE_SHIFT); \
>>> + __tag_set(__addr, page_kasan_tag(x)); \
>>> +})
>>> +#endif
>>
>> Can we pretty please keep this in arch-independent code?
>>
>> The idea of tags is not x86-specific and I can almost guarantee that x86
>> won't be the last one needing this.
>
> Do you mean adding a KASAN oriented page_to_virt() that's arch independent? I'd
> guess next to the regular one, in include/linux/mm.h?
Close.
There should be one and one only arch-independent page_to_virt() that
uses __tag_set(). Then, since __tag_set() gets compiled out when KASAN
is off, you get to keep a single page_to_virt() implementation that
compiles down to the original implementation when KASAN is off.
BTW, you can get __tag_set() down to a single implementation too:
static inline void *__tag_set(const void *__addr, u8 tag)
{
u64 addr = (u64)__addr;
if (IS_ENABLED(CONFIG_KASAN_SW_TAGS)) {
addr &= ~__tag_shifted(KASAN_TAG_BITS_MASK);
addr |= __tag_shifted(tag & KASAN_TAG_BITS_MASK);
}
return (void *)addr;
}
That shifts the actual #ifdeffery all the way back to the constants. At
this level it doesn't really matter much though. Just do whatever is
easy to read.
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v9 06/13] mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (4 preceding siblings ...)
2026-01-20 14:41 ` [PATCH v9 05/13] x86/mm: Reset tag for virtual to physical address conversions Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-22 10:32 ` Mike Rapoport
2026-01-20 14:41 ` [PATCH v9 07/13] x86/mm: Use physical address comparisons in fill_p*d/pte Maciej Wieczor-Retman
` (7 subsequent siblings)
13 siblings, 1 reply; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Andrew Morton, Mike Rapoport, Uladzislau Rezki
Cc: m.wieczorretman, Maciej Wieczor-Retman, Alexander Potapenko,
linux-mm, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
ARCH_HAS_EXECMEM_ROX was re-enabled in x86 at Linux 6.14 release.
vm_reset_perms() calculates range's start and end addresses using min()
and max() functions. To do that it compares pointers but, with KASAN
software tags mode enabled, some are tagged - addr variable is, while
start and end variables aren't. This can cause the wrong address to be
chosen and result in various errors in different places.
Reset tags in the address used as function argument in min(), max().
execmem_cache_add() adds tagged pointers to a maple tree structure,
which then are incorrectly compared when walking the tree. That results
in different pointers being returned later and page permission violation
errors panicking the kernel.
Reset tag of the address range inserted into the maple tree inside
execmem_vmalloc() which then gets propagated to execmem_cache_add().
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Acked-by: Alexander Potapenko <glider@google.com>
---
Changelog v7:
- Add Alexander's acked-by tag.
- Add comments on why these tag resets are needed (Alexander)
Changelog v6:
- Move back the tag reset from execmem_cache_add() to execmem_vmalloc()
(Mike Rapoport)
- Rewrite the changelogs to match the code changes from v6 and v5.
Changelog v5:
- Remove the within_range() change.
- arch_kasan_reset_tag -> kasan_reset_tag.
Changelog v4:
- Add patch to the series.
mm/execmem.c | 9 ++++++++-
mm/vmalloc.c | 7 ++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/mm/execmem.c b/mm/execmem.c
index 810a4ba9c924..dc7422222cf7 100644
--- a/mm/execmem.c
+++ b/mm/execmem.c
@@ -59,7 +59,14 @@ static void *execmem_vmalloc(struct execmem_range *range, size_t size,
return NULL;
}
- return p;
+ /*
+ * Resetting the tag here is necessary to avoid the tagged address
+ * ending up in the maple tree structure. There it's linear address
+ * can be incorrectly compared with other addresses which can result in
+ * a wrong address being picked down the line and for example a page
+ * permission violation error panicking the kernel.
+ */
+ return kasan_reset_tag(p);
}
struct vm_struct *execmem_vmap(size_t size)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 628f96e83b11..1479a7b362f4 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3354,7 +3354,12 @@ static void vm_reset_perms(struct vm_struct *area)
* the vm_unmap_aliases() flush includes the direct map.
*/
for (i = 0; i < area->nr_pages; i += 1U << page_order) {
- unsigned long addr = (unsigned long)page_address(area->pages[i]);
+ /*
+ * Addresses' tag needs resetting so it can be properly used in
+ * the min() and max() below. Otherwise the start or end values
+ * might be favoured.
+ */
+ unsigned long addr = (unsigned long)kasan_reset_tag(page_address(area->pages[i]));
if (addr) {
unsigned long page_size;
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v9 06/13] mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic
2026-01-20 14:41 ` [PATCH v9 06/13] mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic Maciej Wieczor-Retman
@ 2026-01-22 10:32 ` Mike Rapoport
2026-01-23 9:37 ` Maciej Wieczor-Retman
0 siblings, 1 reply; 21+ messages in thread
From: Mike Rapoport @ 2026-01-22 10:32 UTC (permalink / raw)
To: Maciej Wieczor-Retman
Cc: Andrew Morton, Uladzislau Rezki, Maciej Wieczor-Retman,
Alexander Potapenko, linux-mm, linux-kernel
On Tue, Jan 20, 2026 at 02:41:43PM +0000, Maciej Wieczor-Retman wrote:
> From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
>
> ARCH_HAS_EXECMEM_ROX was re-enabled in x86 at Linux 6.14 release.
> vm_reset_perms() calculates range's start and end addresses using min()
> and max() functions. To do that it compares pointers but, with KASAN
> software tags mode enabled, some are tagged - addr variable is, while
> start and end variables aren't. This can cause the wrong address to be
> chosen and result in various errors in different places.
>
> Reset tags in the address used as function argument in min(), max().
>
> execmem_cache_add() adds tagged pointers to a maple tree structure,
> which then are incorrectly compared when walking the tree. That results
> in different pointers being returned later and page permission violation
> errors panicking the kernel.
>
> Reset tag of the address range inserted into the maple tree inside
> execmem_vmalloc() which then gets propagated to execmem_cache_add().
>
> Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
> Acked-by: Alexander Potapenko <glider@google.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v9 06/13] mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic
2026-01-22 10:32 ` Mike Rapoport
@ 2026-01-23 9:37 ` Maciej Wieczor-Retman
0 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-23 9:37 UTC (permalink / raw)
To: Mike Rapoport
Cc: Andrew Morton, Uladzislau Rezki, Maciej Wieczor-Retman,
Alexander Potapenko, linux-mm, linux-kernel
Thanks!
Kind regards
Maciej Wieczór-Retman
On 2026-01-22 at 12:32:33 +0200, Mike Rapoport wrote:
>On Tue, Jan 20, 2026 at 02:41:43PM +0000, Maciej Wieczor-Retman wrote:
>> From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
>>
>> ARCH_HAS_EXECMEM_ROX was re-enabled in x86 at Linux 6.14 release.
>> vm_reset_perms() calculates range's start and end addresses using min()
>> and max() functions. To do that it compares pointers but, with KASAN
>> software tags mode enabled, some are tagged - addr variable is, while
>> start and end variables aren't. This can cause the wrong address to be
>> chosen and result in various errors in different places.
>>
>> Reset tags in the address used as function argument in min(), max().
>>
>> execmem_cache_add() adds tagged pointers to a maple tree structure,
>> which then are incorrectly compared when walking the tree. That results
>> in different pointers being returned later and page permission violation
>> errors panicking the kernel.
>>
>> Reset tag of the address range inserted into the maple tree inside
>> execmem_vmalloc() which then gets propagated to execmem_cache_add().
>>
>> Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
>> Acked-by: Alexander Potapenko <glider@google.com>
>
>Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
>
>--
>Sincerely yours,
>Mike.
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v9 07/13] x86/mm: Use physical address comparisons in fill_p*d/pte
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (5 preceding siblings ...)
2026-01-20 14:41 ` [PATCH v9 06/13] mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-20 14:41 ` [PATCH v9 08/13] x86/kasan: Initialize KASAN raw shadow memory Maciej Wieczor-Retman
` (6 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Dave Hansen, Andy Lutomirski, Peter Zijlstra, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin
Cc: m.wieczorretman, Maciej Wieczor-Retman, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Calculating page offset returns a pointer without a tag. When comparing
the calculated offset to a tagged page pointer an error is raised
because they are not equal.
Change pointer comparisons to physical address comparisons as to avoid
issues with tagged pointers that pointer arithmetic would create. Open
code pte_offset_kernel(), pmd_offset(), pud_offset() and p4d_offset().
Because one parameter is always zero and the rest of the function
insides are enclosed inside __va(), removing that layer lowers the
complexity of final assembly.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v9:
- Rename patch title so it fits the tip standards.
Changelog v7:
- Swap ternary operator outcomes in fill_p4d since the order was
incorrect.
Changelog v2:
- Open code *_offset() to avoid it's internal __va().
arch/x86/mm/init_64.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 9983017ecbe0..cee14855acaf 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -269,7 +269,10 @@ static p4d_t *fill_p4d(pgd_t *pgd, unsigned long vaddr)
if (pgd_none(*pgd)) {
p4d_t *p4d = (p4d_t *)spp_getpage();
pgd_populate(&init_mm, pgd, p4d);
- if (p4d != p4d_offset(pgd, 0))
+
+ if (__pa(p4d) != (pgtable_l5_enabled() ?
+ (unsigned long)pgd_val(*pgd) & PTE_PFN_MASK :
+ __pa(pgd)))
printk(KERN_ERR "PAGETABLE BUG #00! %p <-> %p\n",
p4d, p4d_offset(pgd, 0));
}
@@ -281,7 +284,7 @@ static pud_t *fill_pud(p4d_t *p4d, unsigned long vaddr)
if (p4d_none(*p4d)) {
pud_t *pud = (pud_t *)spp_getpage();
p4d_populate(&init_mm, p4d, pud);
- if (pud != pud_offset(p4d, 0))
+ if (__pa(pud) != (p4d_val(*p4d) & p4d_pfn_mask(*p4d)))
printk(KERN_ERR "PAGETABLE BUG #01! %p <-> %p\n",
pud, pud_offset(p4d, 0));
}
@@ -293,7 +296,7 @@ static pmd_t *fill_pmd(pud_t *pud, unsigned long vaddr)
if (pud_none(*pud)) {
pmd_t *pmd = (pmd_t *) spp_getpage();
pud_populate(&init_mm, pud, pmd);
- if (pmd != pmd_offset(pud, 0))
+ if (__pa(pmd) != (pud_val(*pud) & pud_pfn_mask(*pud)))
printk(KERN_ERR "PAGETABLE BUG #02! %p <-> %p\n",
pmd, pmd_offset(pud, 0));
}
@@ -305,7 +308,7 @@ static pte_t *fill_pte(pmd_t *pmd, unsigned long vaddr)
if (pmd_none(*pmd)) {
pte_t *pte = (pte_t *) spp_getpage();
pmd_populate_kernel(&init_mm, pmd, pte);
- if (pte != pte_offset_kernel(pmd, 0))
+ if (__pa(pte) != (pmd_val(*pmd) & pmd_pfn_mask(*pmd)))
printk(KERN_ERR "PAGETABLE BUG #03!\n");
}
return pte_offset_kernel(pmd, vaddr);
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 08/13] x86/kasan: Initialize KASAN raw shadow memory
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (6 preceding siblings ...)
2026-01-20 14:41 ` [PATCH v9 07/13] x86/mm: Use physical address comparisons in fill_p*d/pte Maciej Wieczor-Retman
@ 2026-01-20 14:41 ` Maciej Wieczor-Retman
2026-01-20 14:42 ` [PATCH v9 09/13] x86/mm: Reset tags in a canonical address helper call Maciej Wieczor-Retman
` (5 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:41 UTC (permalink / raw)
To: Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov,
Dmitry Vyukov, Vincenzo Frascino, Dave Hansen, Andy Lutomirski,
Peter Zijlstra, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
x86, H. Peter Anvin
Cc: m.wieczorretman, Maciej Wieczor-Retman, kasan-dev, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
In KASAN's generic mode the default value in shadow memory is zero.
During initialization of shadow memory pages they are allocated and
zeroed.
In KASAN's tag-based mode the default tag for the arm64 architecture is
0xFE which corresponds to any memory that should not be accessed. On x86
(where tags are 4-bit wide instead of 8-bit wide) that tag is 0xE so
during the initializations all the bytes in shadow memory pages should
be filled with it.
Use memblock_alloc_try_nid_raw() instead of memblock_alloc_try_nid() to
avoid zeroing out the memory so it can be set with the KASAN invalid
tag.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Reviewed-by: Alexander Potapenko <glider@google.com>
Reviewed-by: Andrey Ryabinin <ryabinin.a.a@gmail.com>
---
Changelog v9:
- Rename patch title so it fits the tip standards.
- Add Andrey Ryabinin's Reviewed-by tag.
Changelog v7:
- Fix flipped arguments in memset().
- Add Alexander's reviewed-by tag.
Changelog v2:
- Remove dense mode references, use memset() instead of kasan_poison().
arch/x86/mm/kasan_init_64.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index 998b6010d6d3..7f5c11328ec1 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -34,6 +34,18 @@ static __init void *early_alloc(size_t size, int nid, bool should_panic)
return ptr;
}
+static __init void *early_raw_alloc(size_t size, int nid, bool should_panic)
+{
+ void *ptr = memblock_alloc_try_nid_raw(size, size,
+ __pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, nid);
+
+ if (!ptr && should_panic)
+ panic("%pS: Failed to allocate page, nid=%d from=%lx\n",
+ (void *)_RET_IP_, nid, __pa(MAX_DMA_ADDRESS));
+
+ return ptr;
+}
+
static void __init kasan_populate_pmd(pmd_t *pmd, unsigned long addr,
unsigned long end, int nid)
{
@@ -63,8 +75,9 @@ static void __init kasan_populate_pmd(pmd_t *pmd, unsigned long addr,
if (!pte_none(*pte))
continue;
- p = early_alloc(PAGE_SIZE, nid, true);
- entry = pfn_pte(PFN_DOWN(__pa(p)), PAGE_KERNEL);
+ p = early_raw_alloc(PAGE_SIZE, nid, true);
+ memset(p, KASAN_SHADOW_INIT, PAGE_SIZE);
+ entry = pfn_pte(PFN_DOWN(__pa_nodebug(p)), PAGE_KERNEL);
set_pte_at(&init_mm, addr, pte, entry);
} while (pte++, addr += PAGE_SIZE, addr != end);
}
@@ -436,7 +449,7 @@ void __init kasan_init(void)
* it may contain some garbage. Now we can clear and write protect it,
* since after the TLB flush no one should write to it.
*/
- memset(kasan_early_shadow_page, 0, PAGE_SIZE);
+ memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
for (i = 0; i < PTRS_PER_PTE; i++) {
pte_t pte;
pgprot_t prot;
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 09/13] x86/mm: Reset tags in a canonical address helper call
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (7 preceding siblings ...)
2026-01-20 14:41 ` [PATCH v9 08/13] x86/kasan: Initialize KASAN raw shadow memory Maciej Wieczor-Retman
@ 2026-01-20 14:42 ` Maciej Wieczor-Retman
2026-01-20 14:42 ` [PATCH v9 10/13] x86/mm: Initialize LAM_SUP Maciej Wieczor-Retman
` (4 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:42 UTC (permalink / raw)
To: Dave Hansen, Andy Lutomirski, Peter Zijlstra, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin
Cc: m.wieczorretman, Maciej Wieczor-Retman, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
For an address to be canonical it has to have its top bits equal to each
other. The number of bits depends on the paging level and whether
they're supposed to be ones or zeroes depends on whether the address
points to kernel or user space.
With Linear Address Masking (LAM) enabled, the definition of linear
address canonicality is modified. Not all of the previously required
bits need to be equal, only the first and last from the previously equal
bitmask. So for example a 5-level paging kernel address needs to have
bits [63] and [56] set.
__is_canonical_address() helper can cause issues when met with tagged
pointers that expect the loosened canonicality checks. The function
itself shouldn't be made LAM-aware since for example in KVM, where it's
used extensively - it's not practical to deal with differences between
host and guest which might want a different LAM state. Also by the time
__is_canonical_address() is invoked KVM has already done any necessary
LAM unmasking.
Reset tags in the address argument of __is_canonical_address() located
in copy_from_kernel_nofault_allowed() which is the only function causing
problems.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v9:
- Redo the patch to not break KVM.
- Remove Alexander's acked-by tag.
Changelog v7:
- Add Alexander's acked-by tag.
- Add parentheses around vaddr_bits as suggested by checkpatch.
- Apply the bitmasks to the __canonical_address() function which is used
in kvm code.
Changelog v6:
- Use bitmasks to check both kernel and userspace addresses in the
__is_canonical_address() (Dave Hansen and Samuel Holland).
Changelog v4:
- Add patch to the series.
arch/x86/mm/maccess.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/mm/maccess.c b/arch/x86/mm/maccess.c
index 42115ac079cf..dbaf70bdc383 100644
--- a/arch/x86/mm/maccess.c
+++ b/arch/x86/mm/maccess.c
@@ -33,7 +33,7 @@ bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
if (!boot_cpu_data.x86_virt_bits)
return true;
- return __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
+ return __is_canonical_address(__tag_reset(vaddr), boot_cpu_data.x86_virt_bits);
}
#else
bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 10/13] x86/mm: Initialize LAM_SUP
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (8 preceding siblings ...)
2026-01-20 14:42 ` [PATCH v9 09/13] x86/mm: Reset tags in a canonical address helper call Maciej Wieczor-Retman
@ 2026-01-20 14:42 ` Maciej Wieczor-Retman
2026-01-20 14:42 ` [PATCH v9 11/13] x86: Increase minimal SLAB alignment for KASAN Maciej Wieczor-Retman
` (3 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:42 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Andy Lutomirski, Peter Zijlstra
Cc: m.wieczorretman, Maciej Wieczor-Retman, Alexander Potapenko,
linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
To make use of KASAN's tag based mode on x86, Linear Address Masking
(LAM) needs to be enabled. To do that the 28th bit in CR4 has to be set.
Set the bit in early memory initialization.
When launching secondary CPUs the LAM bit gets lost. To avoid this add
it in a mask in head_64.S. The bitmask permits some bits of CR4 to pass
from the primary CPU to the secondary CPUs without being cleared.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Acked-by: Alexander Potapenko <glider@google.com>
---
Changelog v9:
- Rename patch title so it fits the tip standards.
Changelog v7:
- Add Alexander's acked-by tag.
Changelog v6:
- boot_cpu_has() -> cpu_feature_enabled()
arch/x86/kernel/head_64.S | 3 +++
arch/x86/mm/init.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index 21816b48537c..c5a0bfbe280d 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -209,6 +209,9 @@ SYM_INNER_LABEL(common_startup_64, SYM_L_LOCAL)
* there will be no global TLB entries after the execution."
*/
movl $(X86_CR4_PAE | X86_CR4_LA57), %edx
+#ifdef CONFIG_ADDRESS_MASKING
+ orl $X86_CR4_LAM_SUP, %edx
+#endif
#ifdef CONFIG_X86_MCE
/*
* Preserve CR4.MCE if the kernel will enable #MC support.
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 8bf6ad4b9400..a8442b255481 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -764,6 +764,9 @@ void __init init_mem_mapping(void)
probe_page_size_mask();
setup_pcid();
+ if (cpu_feature_enabled(X86_FEATURE_LAM) && IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+ cr4_set_bits_and_update_boot(X86_CR4_LAM_SUP);
+
#ifdef CONFIG_X86_64
end = max_pfn << PAGE_SHIFT;
#else
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 11/13] x86: Increase minimal SLAB alignment for KASAN
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (9 preceding siblings ...)
2026-01-20 14:42 ` [PATCH v9 10/13] x86/mm: Initialize LAM_SUP Maciej Wieczor-Retman
@ 2026-01-20 14:42 ` Maciej Wieczor-Retman
2026-01-20 14:42 ` [PATCH v9 12/13] x86/kasan: Use a logical bit shift for kasan_mem_to_shadow Maciej Wieczor-Retman
` (2 subsequent siblings)
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:42 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin
Cc: m.wieczorretman, Maciej Wieczor-Retman, Andrey Konovalov,
linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
8 byte minimal SLAB alignment interferes with KASAN's granularity of 16
bytes. It causes a lot of out-of-bounds errors for unaligned 8 byte
allocations.
Compared to a kernel with KASAN disabled, the memory footprint increases
because all kmalloc-8 allocations now are realized as kmalloc-16, which
has twice the object size. But more meaningfully, when compared to a
kernel with generic KASAN enabled, there is no difference. Because of
redzones in generic KASAN, kmalloc-8' and kmalloc-16' object size is the
same (48 bytes). So changing the minimal SLAB alignment of the tag-based
mode doesn't have any negative impact when compared to the other
software KASAN mode.
Adjust x86 minimal SLAB alignment to match KASAN granularity size.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
---
Changelog v9:
- Rename patch title so it fits the tip standards.
Changelog v6:
- Add Andrey's Reviewed-by tag.
Changelog v4:
- Extend the patch message with some more context and impact
information.
Changelog v3:
- Fix typo in patch message 4 -> 16.
- Change define location to arch/x86/include/asm/cache.c.
arch/x86/include/asm/cache.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/x86/include/asm/cache.h b/arch/x86/include/asm/cache.h
index 69404eae9983..3232583b5487 100644
--- a/arch/x86/include/asm/cache.h
+++ b/arch/x86/include/asm/cache.h
@@ -21,4 +21,8 @@
#endif
#endif
+#ifdef CONFIG_KASAN_SW_TAGS
+#define ARCH_SLAB_MINALIGN (1ULL << KASAN_SHADOW_SCALE_SHIFT)
+#endif
+
#endif /* _ASM_X86_CACHE_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 12/13] x86/kasan: Use a logical bit shift for kasan_mem_to_shadow
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (10 preceding siblings ...)
2026-01-20 14:42 ` [PATCH v9 11/13] x86: Increase minimal SLAB alignment for KASAN Maciej Wieczor-Retman
@ 2026-01-20 14:42 ` Maciej Wieczor-Retman
2026-01-20 14:42 ` [PATCH v9 13/13] x86/kasan: Make software tag-based kasan available Maciej Wieczor-Retman
2026-01-20 17:54 ` [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Andrey Konovalov
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:42 UTC (permalink / raw)
To: Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov,
Dmitry Vyukov, Vincenzo Frascino, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin
Cc: m.wieczorretman, Maciej Wieczor-Retman, kasan-dev, linux-kernel
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
The tag-based KASAN adopts an arithemitc bit shift to convert a memory
address to a shadow memory address. While it makes a lot of sense on
arm64, it doesn't work well for all cases on x86 - either the
non-canonical hook becomes quite complex for different paging levels, or
the inline mode would need a lot more adjustments. Thus the best working
scheme is the logical bit shift and non-canonical shadow offset that x86
uses for generic KASAN, of course adjusted for the increased granularity
from 8 to 16 bytes.
Add an arch specific implementation of kasan_mem_to_shadow() that uses
the logical bit shift.
The non-canonical hook tries to calculate whether an address came from
kasan_mem_to_shadow(). First it checks whether this address fits into
the legal set of values possible to output from the mem to shadow
function.
Duplicate the generic mode check from kasan_non_canonical_hook() into
the arch specific function as the calculation follows the same logic due
to the same logical bit shift.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v9:
- Rename patch title so it fits the tip standards.
- Take out the x86 part from mm/kasan/report.c and put it in the arch
specific function. Adjust the patch message.
Changelog v7:
- Redo the patch message and add a comment to __kasan_mem_to_shadow() to
provide better explanation on why x86 doesn't work well with the
arithemitc bit shift approach (Marco).
Changelog v4:
- Add this patch to the series.
arch/x86/include/asm/kasan.h | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/arch/x86/include/asm/kasan.h b/arch/x86/include/asm/kasan.h
index c868ae734f68..90c18e30848f 100644
--- a/arch/x86/include/asm/kasan.h
+++ b/arch/x86/include/asm/kasan.h
@@ -31,6 +31,38 @@
#include <linux/bits.h>
#ifdef CONFIG_KASAN_SW_TAGS
+/*
+ * Using the non-arch specific implementation of __kasan_mem_to_shadow() with a
+ * arithmetic bit shift can cause high code complexity in KASAN's non-canonical
+ * hook for x86 or might not work for some paging level and KASAN mode
+ * combinations. The inline mode compiler support could also suffer from higher
+ * complexity for no specific benefit. Therefore the generic mode's logical
+ * shift implementation is used.
+ */
+static inline void *__kasan_mem_to_shadow(const void *addr)
+{
+ return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT)
+ + KASAN_SHADOW_OFFSET;
+}
+#define kasan_mem_to_shadow(addr) __kasan_mem_to_shadow(addr)
+
+static __always_inline bool __arch_kasan_non_canonical_hook(unsigned long addr)
+{
+ /*
+ * For Generic KASAN and Software Tag-Based mode on the x86
+ * architecture, kasan_mem_to_shadow() uses the logical right shift
+ * and never overflows with the chosen KASAN_SHADOW_OFFSET values (on
+ * both x86 and arm64). Thus, the possible shadow addresses (even for
+ * bogus pointers) belong to a single contiguous region that is the
+ * result of kasan_mem_to_shadow() applied to the whole address space.
+ */
+ if (addr < (unsigned long)kasan_mem_to_shadow((void *)(0ULL)) ||
+ addr > (unsigned long)kasan_mem_to_shadow((void *)(~0ULL)))
+ return true;
+ return false;
+}
+#define arch_kasan_non_canonical_hook(addr) __arch_kasan_non_canonical_hook(addr)
+
#define __tag_shifted(tag) FIELD_PREP(GENMASK_ULL(60, 57), tag)
#define __tag_reset(addr) (sign_extend64((u64)(addr), 56))
#define __tag_get(addr) ((u8)FIELD_GET(GENMASK_ULL(60, 57), (u64)addr))
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v9 13/13] x86/kasan: Make software tag-based kasan available
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (11 preceding siblings ...)
2026-01-20 14:42 ` [PATCH v9 12/13] x86/kasan: Use a logical bit shift for kasan_mem_to_shadow Maciej Wieczor-Retman
@ 2026-01-20 14:42 ` Maciej Wieczor-Retman
2026-01-20 17:54 ` [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Andrey Konovalov
13 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 14:42 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Jonathan Corbet, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Andy Lutomirski, Peter Zijlstra, Andrew Morton
Cc: m.wieczorretman, Maciej Wieczor-Retman, linux-kernel, linux-doc,
kasan-dev
From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Make CONFIG_KASAN_SW_TAGS available for x86 machines if they have
ADDRESS_MASKING enabled (LAM) as that works similarly to Top-Byte Ignore
(TBI) that allows the software tag-based mode on arm64 platform.
The value for sw_tags KASAN_SHADOW_OFFSET was calculated by rearranging
the formulas for KASAN_SHADOW_START and KASAN_SHADOW_END from
arch/x86/include/asm/kasan.h - the only prerequisites being
KASAN_SHADOW_SCALE_SHIFT of 4, and KASAN_SHADOW_END equal to the
one from KASAN generic mode.
Set scale macro based on KASAN mode: in software tag-based mode 16 bytes
of memory map to one shadow byte and 8 in generic mode.
Disable CONFIG_KASAN_INLINE and CONFIG_KASAN_STACK when
CONFIG_KASAN_SW_TAGS is enabled on x86 until the appropriate compiler
support is available.
Lock software tag KASAN behind CC_IS_CLANG due to lack of proper support
by gcc resulting in kernel booting issues.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v9:
- Lock HAVE_ARCH_KASAN_HAS_SW_TAGS behind CC_IS_CLANG due to lack of
support from gcc.
- Remove pr_info() from KASAN initialization since it's now done by the
generic init helper.
- Add paragraph to the mm.rst to explain the mutual exclusive nature of
the KASAN address ranges.
- Use cpu_feature_enabled() instead of boot_cpu_has() in
kasan_init_64.c.
Changelog v7:
- Add a paragraph to the patch message explaining how the various
addresses and the KASAN_SHADOW_OFFSET were calculated.
Changelog v6:
- Don't enable KASAN if LAM is not supported.
- Move kasan_init_tags() to kasan_init_64.c to not clutter the setup.c
file.
- Move the #ifdef for the KASAN scale shift here.
- Move the gdb code to patch "Use arithmetic shift for shadow
computation".
- Return "depends on KASAN" line to Kconfig.
- Add the defer kasan config option so KASAN can be disabled on hardware
that doesn't have LAM.
Changelog v4:
- Add x86 specific kasan_mem_to_shadow().
- Revert x86 to the older unsigned KASAN_SHADOW_OFFSET. Do the same to
KASAN_SHADOW_START/END.
- Modify scripts/gdb/linux/kasan.py to keep x86 using unsigned offset.
- Disable inline and stack support when software tags are enabled on
x86.
Changelog v3:
- Remove runtime_const from previous patch and merge the rest here.
- Move scale shift definition back to header file.
- Add new kasan offset for software tag based mode.
- Fix patch message typo 32 -> 16, and 16 -> 8.
- Update lib/Kconfig.kasan with x86 now having software tag-based
support.
Changelog v2:
- Remove KASAN dense code.
Documentation/arch/x86/x86_64/mm.rst | 10 ++++++++--
arch/x86/Kconfig | 4 ++++
arch/x86/boot/compressed/misc.h | 1 +
arch/x86/include/asm/kasan.h | 5 +++++
arch/x86/mm/kasan_init_64.c | 5 +++++
lib/Kconfig.kasan | 3 ++-
6 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/Documentation/arch/x86/x86_64/mm.rst b/Documentation/arch/x86/x86_64/mm.rst
index a6cf05d51bd8..7e2e4c5fa661 100644
--- a/Documentation/arch/x86/x86_64/mm.rst
+++ b/Documentation/arch/x86/x86_64/mm.rst
@@ -60,7 +60,8 @@ Complete virtual memory map with 4-level page tables
ffffe90000000000 | -23 TB | ffffe9ffffffffff | 1 TB | ... unused hole
ffffea0000000000 | -22 TB | ffffeaffffffffff | 1 TB | virtual memory map (vmemmap_base)
ffffeb0000000000 | -21 TB | ffffebffffffffff | 1 TB | ... unused hole
- ffffec0000000000 | -20 TB | fffffbffffffffff | 16 TB | KASAN shadow memory
+ ffffec0000000000 | -20 TB | fffffbffffffffff | 16 TB | KASAN shadow memory (generic mode)
+ fffff40000000000 | -8 TB | fffffbffffffffff | 8 TB | KASAN shadow memory (software tag-based mode)
__________________|____________|__________________|_________|____________________________________________________________
|
| Identical layout to the 56-bit one from here on:
@@ -130,7 +131,8 @@ Complete virtual memory map with 5-level page tables
ffd2000000000000 | -11.5 PB | ffd3ffffffffffff | 0.5 PB | ... unused hole
ffd4000000000000 | -11 PB | ffd5ffffffffffff | 0.5 PB | virtual memory map (vmemmap_base)
ffd6000000000000 | -10.5 PB | ffdeffffffffffff | 2.25 PB | ... unused hole
- ffdf000000000000 | -8.25 PB | fffffbffffffffff | ~8 PB | KASAN shadow memory
+ ffdf000000000000 | -8.25 PB | fffffbffffffffff | ~8 PB | KASAN shadow memory (generic mode)
+ ffeffc0000000000 | -6 PB | fffffbffffffffff | 4 PB | KASAN shadow memory (software tag-based mode)
__________________|____________|__________________|_________|____________________________________________________________
|
| Identical layout to the 47-bit one from here on:
@@ -176,5 +178,9 @@ Be very careful vs. KASLR when changing anything here. The KASLR address
range must not overlap with anything except the KASAN shadow area, which is
correct as KASAN disables KASLR.
+The 'KASAN shadow memory (generic mode)/(software tag-based mode)' ranges are
+mutually exclusive and depend on which KASAN setting is chosen:
+CONFIG_KASAN_GENERIC or CONFIG_KASAN_SW_TAGS.
+
For both 4- and 5-level layouts, the KSTACK_ERASE_POISON value in the last 2MB
hole: ffffffffffff4111
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 80527299f859..877668cd5deb 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -67,6 +67,7 @@ config X86
select ARCH_CLOCKSOURCE_INIT
select ARCH_CONFIGURES_CPU_MITIGATIONS
select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE
+ select ARCH_DISABLE_KASAN_INLINE if X86_64 && KASAN_SW_TAGS
select ARCH_ENABLE_HUGEPAGE_MIGRATION if X86_64 && HUGETLB_PAGE && MIGRATION
select ARCH_ENABLE_MEMORY_HOTPLUG if X86_64
select ARCH_ENABLE_MEMORY_HOTREMOVE if MEMORY_HOTPLUG
@@ -196,6 +197,8 @@ config X86
select HAVE_ARCH_JUMP_LABEL_RELATIVE
select HAVE_ARCH_KASAN if X86_64
select HAVE_ARCH_KASAN_VMALLOC if X86_64
+ select HAVE_ARCH_KASAN_SW_TAGS if ADDRESS_MASKING && CC_IS_CLANG
+ select ARCH_NEEDS_DEFER_KASAN if ADDRESS_MASKING
select HAVE_ARCH_KFENCE
select HAVE_ARCH_KMSAN if X86_64
select HAVE_ARCH_KGDB
@@ -410,6 +413,7 @@ config AUDIT_ARCH
config KASAN_SHADOW_OFFSET
hex
depends on KASAN
+ default 0xeffffc0000000000 if KASAN_SW_TAGS
default 0xdffffc0000000000
config HAVE_INTEL_TXT
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index fd855e32c9b9..ba70036c2abd 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -13,6 +13,7 @@
#undef CONFIG_PARAVIRT_SPINLOCKS
#undef CONFIG_KASAN
#undef CONFIG_KASAN_GENERIC
+#undef CONFIG_KASAN_SW_TAGS
#define __NO_FORTIFY
diff --git a/arch/x86/include/asm/kasan.h b/arch/x86/include/asm/kasan.h
index 90c18e30848f..53ab7de16517 100644
--- a/arch/x86/include/asm/kasan.h
+++ b/arch/x86/include/asm/kasan.h
@@ -6,7 +6,12 @@
#include <linux/kasan-tags.h>
#include <linux/types.h>
#define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
+
+#ifdef CONFIG_KASAN_SW_TAGS
+#define KASAN_SHADOW_SCALE_SHIFT 4
+#else
#define KASAN_SHADOW_SCALE_SHIFT 3
+#endif
/*
* Compiler uses shadow offset assuming that addresses start
diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index 7f5c11328ec1..8cbb8ec32061 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -465,4 +465,9 @@ void __init kasan_init(void)
init_task.kasan_depth = 0;
kasan_init_generic();
+
+ if (cpu_feature_enabled(X86_FEATURE_LAM))
+ kasan_init_sw_tags();
+ else
+ pr_info("KernelAddressSanitizer not initialized (sw-tags): hardware doesn't support LAM\n");
}
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index a4bb610a7a6f..d13ea8da7bfd 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -112,7 +112,8 @@ config KASAN_SW_TAGS
Requires GCC 11+ or Clang.
- Supported only on arm64 CPUs and relies on Top Byte Ignore.
+ Supported on arm64 CPUs that support Top Byte Ignore and on x86 CPUs
+ that support Linear Address Masking.
Consumes about 1/16th of available memory at kernel start and
add an overhead of ~20% for dynamic allocations.
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86
2026-01-20 14:40 [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Maciej Wieczor-Retman
` (12 preceding siblings ...)
2026-01-20 14:42 ` [PATCH v9 13/13] x86/kasan: Make software tag-based kasan available Maciej Wieczor-Retman
@ 2026-01-20 17:54 ` Andrey Konovalov
2026-01-20 19:18 ` Maciej Wieczor-Retman
13 siblings, 1 reply; 21+ messages in thread
From: Andrey Konovalov @ 2026-01-20 17:54 UTC (permalink / raw)
To: Maciej Wieczor-Retman
Cc: chleroy, surenb, justinstitt, nsc, jan.kiszka, trintaeoitogc,
dave.hansen, ryabinin.a.a, kees, maciej.wieczor-retman, urezki,
will, nick.desaulniers+lkml, brgerst, ubizjak, rppt, samitolvanen,
thuth, mhocko, nathan, osandov, thomas.lendacky, yeoreum.yun,
akpm, catalin.marinas, morbo, jackmanb, mingo, jpoimboe, vbabka,
corbet, lorenzo.stoakes, vincenzo.frascino, luto, glider, weixugc,
axelrasmussen, samuel.holland, kbingham, jeremy.linton, kas, tglx,
ardb, peterz, hpa, dvyukov, yuanchu, leitao, david,
anshuman.khandual, bp, Liam.Howlett, kasan-dev, linux-kbuild, x86,
linux-doc, linux-kernel, llvm, linux-arm-kernel, linux-mm
On Tue, Jan 20, 2026 at 3:40 PM Maciej Wieczor-Retman
<m.wieczorretman@pm.me> wrote:
>
> Maciej Wieczor-Retman (11):
> kasan: Fix inline mode for x86 tag-based mode
> x86/kasan: Add arch specific kasan functions
> x86/mm: Reset tag for virtual to physical address conversions
> mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic
> x86/mm: Use physical address comparisons in fill_p*d/pte
> x86/kasan: Initialize KASAN raw shadow memory
> x86/mm: Reset tags in a canonical address helper call
> x86/mm: Initialize LAM_SUP
> x86: Increase minimal SLAB alignment for KASAN
> x86/kasan: Use a logical bit shift for kasan_mem_to_shadow
> x86/kasan: Make software tag-based kasan available
>
> Samuel Holland (2):
> kasan: sw_tags: Use arithmetic shift for shadow computation
> kasan: arm64: x86: Make special tags arch specific
>
> Documentation/arch/arm64/kasan-offsets.sh | 8 ++-
> Documentation/arch/x86/x86_64/mm.rst | 10 ++-
Still missing Documentation/dev-tools/kasan.rst updates. Feel free to
send as a separate patch to avoid resending the whole series.
> MAINTAINERS | 4 +-
> arch/arm64/Kconfig | 10 +--
> arch/arm64/include/asm/kasan-tags.h | 14 ++++
> arch/arm64/include/asm/kasan.h | 7 +-
> arch/arm64/include/asm/memory.h | 14 +++-
> arch/arm64/include/asm/uaccess.h | 1 +
> arch/arm64/mm/Makefile | 2 +
> arch/arm64/mm/kasan_init.c | 7 +-
> arch/arm64/mm/kasan_sw_tags.c | 35 ++++++++++
> arch/x86/Kconfig | 4 ++
> arch/x86/boot/compressed/misc.h | 1 +
> arch/x86/include/asm/cache.h | 4 ++
> arch/x86/include/asm/kasan-tags.h | 9 +++
> arch/x86/include/asm/kasan.h | 79 ++++++++++++++++++++++-
> arch/x86/include/asm/page.h | 8 +++
> arch/x86/include/asm/page_64.h | 1 +
> arch/x86/kernel/head_64.S | 3 +
> arch/x86/mm/init.c | 3 +
> arch/x86/mm/init_64.c | 11 ++--
> arch/x86/mm/kasan_init_64.c | 24 ++++++-
> arch/x86/mm/maccess.c | 2 +-
> arch/x86/mm/physaddr.c | 2 +
> include/linux/kasan-tags.h | 21 ++++--
> include/linux/kasan.h | 13 ++--
> include/linux/mm.h | 6 +-
> include/linux/mmzone.h | 2 +-
> include/linux/page-flags-layout.h | 9 +--
> lib/Kconfig.kasan | 3 +-
> mm/execmem.c | 9 ++-
> mm/kasan/kasan.h | 7 ++
> mm/kasan/report.c | 15 ++++-
> mm/vmalloc.c | 7 +-
> scripts/Makefile.kasan | 3 +
> scripts/gdb/linux/kasan.py | 5 +-
> scripts/gdb/linux/mm.py | 5 +-
> 37 files changed, 312 insertions(+), 56 deletions(-)
> create mode 100644 arch/arm64/include/asm/kasan-tags.h
> create mode 100644 arch/arm64/mm/kasan_sw_tags.c
> create mode 100644 arch/x86/include/asm/kasan-tags.h
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86
2026-01-20 17:54 ` [PATCH v9 00/13] kasan: x86: arm64: KASAN tag-based mode for x86 Andrey Konovalov
@ 2026-01-20 19:18 ` Maciej Wieczor-Retman
0 siblings, 0 replies; 21+ messages in thread
From: Maciej Wieczor-Retman @ 2026-01-20 19:18 UTC (permalink / raw)
To: Andrey Konovalov
Cc: Maciej Wieczor-Retman, chleroy, surenb, justinstitt, nsc,
jan.kiszka, trintaeoitogc, dave.hansen, ryabinin.a.a, kees,
urezki, will, nick.desaulniers+lkml, brgerst, ubizjak, rppt,
samitolvanen, thuth, mhocko, nathan, osandov, thomas.lendacky,
yeoreum.yun, akpm, catalin.marinas, morbo, jackmanb, mingo,
jpoimboe, vbabka, corbet, lorenzo.stoakes, vincenzo.frascino,
luto, glider, weixugc, axelrasmussen, samuel.holland, kbingham,
jeremy.linton, kas, tglx, ardb, peterz, hpa, dvyukov, yuanchu,
leitao, david, anshuman.khandual, bp, Liam.Howlett, kasan-dev,
linux-kbuild, x86, linux-doc, linux-kernel, llvm,
linux-arm-kernel, linux-mm
On 2026-01-20 at 18:54:48 +0100, Andrey Konovalov wrote:
>On Tue, Jan 20, 2026 at 3:40 PM Maciej Wieczor-Retman
><m.wieczorretman@pm.me> wrote:
>>
>> Maciej Wieczor-Retman (11):
>> kasan: Fix inline mode for x86 tag-based mode
>> x86/kasan: Add arch specific kasan functions
>> x86/mm: Reset tag for virtual to physical address conversions
>> mm/execmem: Untag addresses in EXECMEM_ROX related pointer arithmetic
>> x86/mm: Use physical address comparisons in fill_p*d/pte
>> x86/kasan: Initialize KASAN raw shadow memory
>> x86/mm: Reset tags in a canonical address helper call
>> x86/mm: Initialize LAM_SUP
>> x86: Increase minimal SLAB alignment for KASAN
>> x86/kasan: Use a logical bit shift for kasan_mem_to_shadow
>> x86/kasan: Make software tag-based kasan available
>>
>> Samuel Holland (2):
>> kasan: sw_tags: Use arithmetic shift for shadow computation
>> kasan: arm64: x86: Make special tags arch specific
>>
>> Documentation/arch/arm64/kasan-offsets.sh | 8 ++-
>> Documentation/arch/x86/x86_64/mm.rst | 10 ++-
>
>Still missing Documentation/dev-tools/kasan.rst updates. Feel free to
>send as a separate patch to avoid resending the whole series.
Sorry, my bad, of course I'll send it in separately if this iteration gets
merged. Thanks!
--
Kind regards
Maciej Wieczór-Retman
^ permalink raw reply [flat|nested] 21+ messages in thread