Skip to contentSkip to conversation

kasan: add hardware tag-based mode for arm64

PATCH Superseded Andrey Konovalov 5 participants 90 messages Sep 15, 2020 – Sep 24, 2020

Subsystems (16)

v2 of 11 Superseded Latest is v11 →

lore
Share & tools
lore
#0 lore raw

v2 cover kasan: add hardware tag-based mode for arm64

This patchset adds a new hardware tag-based mode to KASAN [1]. The new mode is similar to the existing software tag-based KASAN, but relies on arm64 Memory Tagging Extension (MTE) [2] to perform memory and pointer tagging (instead of shadow memory and compiler instrumentation).

This patchset is co-developed by
Vincenzo Frascino <[email protected]>.

This patchset is available here:

https://github.com/xairy/linux/tree/up-kasan-mte-v2

and has also been uploaded to the Linux kernel Gerrit instance:

https://linux-review.googlesource.com/c/linux/kernel/git/torvalds/linux/+/2700

This patchset is based on the v10 of the user MTE patchset [3].

This patchset essentially consists of four parts:

1. Rework KASAN code to allow easier integration of the hardware tag-based mode.
2. Introduce config option for the new mode.
3. Introduce core in-kernel MTE routines.
4. Combine the previous parts together to implement the new mode.

For testing in QEMU hardware tag-based KASAN requires:

1. QEMU built from master [4] (use "-machine virt,mte=on -cpu max" arguments to run).
2. GCC version 10.

[1] https://www.kernel.org/doc/html/latest/dev-tools/kasan.html
[2] https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety
[3] git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux for-next/mte
[4] https://github.com/qemu/qemu

====== Overview

The underlying ideas of the approach used by hardware tag-based KASAN are:

1. By relying on the Top Byte Ignore (TBI) arm64 CPU feature, pointer tags are stored in the top byte of each kernel pointer.

2. With the Memory Tagging Extension (MTE) arm64 CPU feature, memory tags for kernel memory allocations are stored in a dedicated memory not accessible via normal instuctions.

3. On each memory allocation, a random tag is generated, embedded it into the returned pointer, and the corresponding memory is tagged with the same tag value.

4. With MTE the CPU performs a check on each memory access to make sure that the pointer tag matches the memory tag.

5. On a tag mismatch the CPU generates a tag fault, and a KASAN report is printed.

Same as other KASAN modes, hardware tag-based KASAN is intended as a debugging feature at this point.

====== Rationale

There are two main reasons for this new hardware tag-based mode:

1. Previously implemented software tag-based KASAN is being successfully used on dogfood testing devices due to its low memory overhead (as initially planned). The new hardware mode keeps the same low memory overhead, and is expected to have significantly lower performance impact, due to the tag checks being performed by the hardware. Therefore the new mode can be used as a better alternative in dogfood testing for hardware that supports MTE.

2. The new mode lays the groundwork for the planned in-kernel MTE-based memory corruption mitigation to be used in production.

====== Technical details

From the implementation perspective, hardware tag-based KASAN is almost identical to the software mode. The key difference is using MTE for assigning and checking tags.

Compared to the software mode, the hardware mode uses 4 bits per tag, as dictated by MTE. Pointer tags are stored in bits [56:60), the top 4 bits have the normal value 0xF. Having less distict tags increases the probablity of false negatives (from ~1/256 to ~1/16) in certain cases.

Only synchronous exceptions are set up and used by hardware tag-based KASAN.

====== Benchmarks

Note: all measurements have been performed with software emulation of Memory Tagging Extension, performance numbers for hardware tag-based KASAN on the actual hardware are expected to be better.

Boot time [1]:
* 2.8 sec for clean kernel
* 5.7 sec for hardware tag-based KASAN
* 11.8 sec for software tag-based KASAN
* 11.6 sec for generic KASAN

Slab memory usage after boot [2]:
* 7.0 kb for clean kernel
* 9.7 kb for hardware tag-based KASAN
* 9.7 kb for software tag-based KASAN
* 41.3 kb for generic KASAN

Measurements have been performed with:
* defconfig-based configs
* Manually built QEMU master
* QEMU arguments: -machine virt,mte=on -cpu max
* CONFIG_KASAN_STACK_ENABLE disabled
* CONFIG_KASAN_INLINE enabled
* clang-10 as the compiler and gcc-10 as the assembler

[1] Time before the ext4 driver is initialized.
[2] Measured as cat /proc/meminfo | grep Slab.

====== Notes

The cover letter for software tag-based KASAN patchset can be found here:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0116523cfffa62aeb5aa3b85ce7419f3dae0c1b8

====== History

Changes v1->v2:
- Rebase onto v10 of the user MTE patchset.
- Only enable in-kernel MTE when KASAN_HW_TAGS is enabled.
- Add a layer of arch-level indirection, so KASAN doesn't call MTE helpers directly (this will be useful in case more architectures will add support for HW_TAGS).
- Don't do arm64_skip_faulting_instruction() on MTE fault, disable MTE instead.
- Don't allow software tags with MTE via arch/arm64/Kconfig instead of lib/Kconfig.kasan.
- Rename mm/kasan/tags.c to tags_sw.c and mte.c to tags_hw.c, and do the same for report_*.c files.
- Reword HW_TAGS Kconfig help text to make it less MTE specific.
- Reword and clarify Documentation.
- Drop unnecessary is_el1_mte_sync_tag_check_fault().
- Change report_tag_fault() to only call kasan_report() once HW_TAGS is introduced.
- Rename arch/arm64/include/asm/mte_asm.h to mte-helpers.h and move all MTE-related defines and some helper functions there.
- Change mm/kasan/kasan.h to include mte-def.h instead of mte.h.
- Add WARN_ON() on unaligned size to mte_set_mem_tag_range().
- Implement ldg/irg MTE routines as inline assembly.
- Remove smp_wmb() from mte_set_mem_tag_range().
- Drop __must_check from mte_set_mem_tag_range() as KASAN has no use for the return value.
- Drop zero size check from mte_assign_mem_tag_range().
- Drop unnecessary include <asm/kasan.h> from low-level arm64 code.
- Move enabling TBI1 into __cpu_setup().
- Drop stale comment about callee-saved register from arch/arm64/kernel/entry.S.
- Mark gcr_kernel_excl as __ro_after_init.
- Use GENMASK() in mte_init_tags().

Andrey Konovalov (31):
kasan: KASAN_VMALLOC depends on KASAN_GENERIC
kasan: group vmalloc code
kasan: shadow declarations only for software modes
kasan: rename (un)poison_shadow to (un)poison_memory
kasan: rename KASAN_SHADOW_* to KASAN_GRANULE_*
kasan: only build init.c for software modes
kasan: split out shadow.c from common.c
kasan: rename generic/tags_report.c files
kasan: don't duplicate config dependencies
kasan: hide invalid free check implementation
kasan: decode stack frame only with KASAN_STACK_ENABLE kasan, arm64: only init shadow for software modes kasan, arm64: only use kasan_depth for software modes
kasan: rename addr_has_shadow to addr_has_metadata
kasan: rename print_shadow_for_address to print_memory_metadata
kasan: kasan_non_canonical_hook only for software modes
kasan: rename SHADOW layout macros to META
kasan: separate metadata_fetch_row for each mode
kasan: don't allow SW_TAGS with ARM64_MTE
kasan: rename tags.c to tags_sw.c
kasan: introduce CONFIG_KASAN_HW_TAGS
arm64: kasan: Add arch layer for memory tagging helpers
arm64: kasan: Align allocations for HW_TAGS
kasan: define KASAN_GRANULE_SIZE for HW_TAGS
kasan, x86, s390: update undef CONFIG_KASAN
kasan, arm64: expand CONFIG_KASAN checks
kasan, arm64: implement HW_TAGS runtime
kasan, arm64: print report from tag fault handler kasan, slub: reset tags when accessing metadata kasan, arm64: enable CONFIG_KASAN_HW_TAGS
kasan: add documentation for hardware tag-based mode

Vincenzo Frascino (6):
arm64: mte: Add in-kernel MTE helpers
arm64: mte: Add in-kernel tag fault handler
arm64: kasan: Enable in-kernel MTE
arm64: mte: Convert gcr_user into an exclude mask
arm64: mte: Switch GCR_EL1 in kernel entry and exit
arm64: kasan: Enable TBI EL1

 Documentation/dev-tools/kasan.rst            |  80 ++-
 arch/arm64/Kconfig                           |   5 +-
 arch/arm64/Makefile                          |   2 +-
 arch/arm64/include/asm/assembler.h           |   2 +-
 arch/arm64/include/asm/cache.h               |   3 +
 arch/arm64/include/asm/esr.h                 |   1 +
 arch/arm64/include/asm/kasan.h               |   8 +-
 arch/arm64/include/asm/memory.h              |  14 +-
 arch/arm64/include/asm/mte-helpers.h         |  54 ++
 arch/arm64/include/asm/mte.h                 |  19 +-
 arch/arm64/include/asm/processor.h           |   2 +-
 arch/arm64/include/asm/string.h              |   5 +-
 arch/arm64/kernel/asm-offsets.c              |   3 +
 arch/arm64/kernel/cpufeature.c               |  10 +
 arch/arm64/kernel/entry.S                    |  26 +
 arch/arm64/kernel/head.S                     |   2 +-
 arch/arm64/kernel/image-vars.h               |   2 +-
 arch/arm64/kernel/mte.c                      |  90 ++-
 arch/arm64/kernel/setup.c                    |   1 -
 arch/arm64/lib/mte.S                         |  17 +
 arch/arm64/mm/dump.c                         |   6 +-
 arch/arm64/mm/fault.c                        |  43 +-
 arch/arm64/mm/kasan_init.c                   |  22 +-
 arch/arm64/mm/proc.S                         |  26 +-
 arch/s390/boot/string.c                      |   1 +
 arch/x86/boot/compressed/misc.h              |   1 +
 include/linux/kasan-checks.h                 |   2 +-
 include/linux/kasan.h                        | 110 ++--
 include/linux/mm.h                           |   2 +-
 include/linux/moduleloader.h                 |   3 +-
 include/linux/page-flags-layout.h            |   2 +-
 include/linux/sched.h                        |   2 +-
 include/linux/string.h                       |   2 +-
 init/init_task.c                             |   2 +-
 kernel/fork.c                                |   4 +-
 lib/Kconfig.kasan                            |  69 ++-
 lib/test_kasan.c                             |   2 +-
 mm/kasan/Makefile                            |  25 +-
 mm/kasan/common.c                            | 554 +------------------
 mm/kasan/generic.c                           |  33 +-
 mm/kasan/generic_report.c                    | 165 ------
 mm/kasan/init.c                              |  10 +-
 mm/kasan/kasan.h                             |  64 ++-
 mm/kasan/report.c                            | 254 ++-------
 mm/kasan/report_generic.c                    | 331 +++++++++++
 mm/kasan/report_tags_hw.c                    |  47 ++
 mm/kasan/{tags_report.c => report_tags_sw.c} |   9 +-
 mm/kasan/shadow.c                            | 509 +++++++++++++++++
 mm/kasan/tags_hw.c                           |  78 +++
 mm/kasan/{tags.c => tags_sw.c}               |  16 +-
 mm/page_poison.c                             |   2 +-
 mm/ptdump.c                                  |  13 +-
 mm/slab_common.c                             |   2 +-
 mm/slub.c                                    |  25 +-
 scripts/Makefile.lib                         |   2 +
 55 files changed, 1689 insertions(+), 1095 deletions(-)
 create mode 100644 arch/arm64/include/asm/mte-helpers.h
 delete mode 100644 mm/kasan/generic_report.c
 create mode 100644 mm/kasan/report_generic.c
 create mode 100644 mm/kasan/report_tags_hw.c
 rename mm/kasan/{tags_report.c => report_tags_sw.c} (91%)
 create mode 100644 mm/kasan/shadow.c
 create mode 100644 mm/kasan/tags_hw.c
 rename mm/kasan/{tags.c => tags_sw.c} (93%)
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#1 lore raw

PATCH v2 1/37 kasan: KASAN_VMALLOC depends on KASAN_GENERIC

Currently only generic KASAN mode supports vmalloc, reflect that in the config.

---
Change-Id: I1889e5b3bed28cc5d607802fb6ae43ba461c0dc1

---
 lib/Kconfig.kasan | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
lib/Kconfig.kasan +1 -1
index 047b53dbfd58..e1d55331b618 100644
@@ -156,7 +156,7 @@ config KASAN_SW_TAGS_IDENTIFY
 
 config KASAN_VMALLOC
 	bool "Back mappings in vmalloc space with real shadow memory"
-	depends on HAVE_ARCH_KASAN_VMALLOC
+	depends on KASAN_GENERIC && HAVE_ARCH_KASAN_VMALLOC
 	help
 	  By default, the shadow region for vmalloc space is the read-only
 	  zero page. This means that KASAN cannot detect errors involving
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#2 lore raw

PATCH v2 2/37 kasan: group vmalloc code

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Group all vmalloc-related function declarations in include/linux/kasan.h, and their implementations in mm/kasan/common.c.

No functional changes.

---
Change-Id: Ie20b6c689203cd6de4fd7f2c465ec081c00c5f15

---
 include/linux/kasan.h | 41 +++++++++++++----------
 mm/kasan/common.c     | 78 ++++++++++++++++++++++---------------------
 2 files changed, 63 insertions(+), 56 deletions(-)
include/linux/kasan.h +23 -18
index 087fba34b209..bd5b4965a269 100644
@@ -69,19 +69,6 @@ struct kasan_cache {
 	int free_meta_offset;
 };
 
-/*
- * These functions provide a special case to support backing module
- * allocations with real shadow memory. With KASAN vmalloc, the special
- * case is unnecessary, as the work is handled in the generic case.
- */
-#ifndef CONFIG_KASAN_VMALLOC
-int kasan_module_alloc(void *addr, size_t size);
-void kasan_free_shadow(const struct vm_struct *vm);
-#else
-static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
-static inline void kasan_free_shadow(const struct vm_struct *vm) {}
-#endif
-
 int kasan_add_zero_shadow(void *start, unsigned long size);
 void kasan_remove_zero_shadow(void *start, unsigned long size);
 
@@ -150,9 +137,6 @@ static inline bool kasan_slab_free(struct kmem_cache *s, void *object,
 	return false;
 }
 
-static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
-static inline void kasan_free_shadow(const struct vm_struct *vm) {}
-
 static inline int kasan_add_zero_shadow(void *start, unsigned long size)
 {
 	return 0;
@@ -205,13 +189,16 @@ static inline void *kasan_reset_tag(const void *addr)
 #endif /* CONFIG_KASAN_SW_TAGS */
 
 #ifdef CONFIG_KASAN_VMALLOC
+
 int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
 void kasan_poison_vmalloc(const void *start, unsigned long size);
 void kasan_unpoison_vmalloc(const void *start, unsigned long size);
 void kasan_release_vmalloc(unsigned long start, unsigned long end,
 			   unsigned long free_region_start,
 			   unsigned long free_region_end);
-#else
+
+#else /* CONFIG_KASAN_VMALLOC */
+
 static inline int kasan_populate_vmalloc(unsigned long start,
 					unsigned long size)
 {
@@ -226,7 +213,25 @@ static inline void kasan_release_vmalloc(unsigned long start,
 					 unsigned long end,
 					 unsigned long free_region_start,
 					 unsigned long free_region_end) {}
-#endif
+
+#endif /* CONFIG_KASAN_VMALLOC */
+
+#if defined(CONFIG_KASAN) && !defined(CONFIG_KASAN_VMALLOC)
+
+/*
+ * These functions provide a special case to support backing module
+ * allocations with real shadow memory. With KASAN vmalloc, the special
+ * case is unnecessary, as the work is handled in the generic case.
+ */
+int kasan_module_alloc(void *addr, size_t size);
+void kasan_free_shadow(const struct vm_struct *vm);
+
+#else /* CONFIG_KASAN && !CONFIG_KASAN_VMALLOC */
+
+static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
+static inline void kasan_free_shadow(const struct vm_struct *vm) {}
+
+#endif /* CONFIG_KASAN && !CONFIG_KASAN_VMALLOC */
 
 #ifdef CONFIG_KASAN_INLINE
 void kasan_non_canonical_hook(unsigned long addr);
mm/kasan/common.c +40 -38

94 lines not on this page · raw message

Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#3 lore raw

PATCH v2 3/37 kasan: shadow declarations only for software modes

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Group shadow-related KASAN function declarations and only define them for the two existing software modes.

No functional changes for software modes.

---
Change-Id: I864be75a88b91b443c55e9c2042865e15703e164

---
 include/linux/kasan.h | 44 ++++++++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 17 deletions(-)
include/linux/kasan.h +27 -17
index bd5b4965a269..44a9aae44138 100644
@@ -3,16 +3,24 @@
 #define _LINUX_KASAN_H
 
 #include <linux/types.h>
+#include <asm/kasan.h>
 
 struct kmem_cache;
 struct page;
 struct vm_struct;
 struct task_struct;
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 
 #include <linux/pgtable.h>
-#include <asm/kasan.h>
+
+/* Software KASAN implementations use shadow memory. */
+
+#ifdef CONFIG_KASAN_SW_TAGS
+#define KASAN_SHADOW_INIT 0xFF
+#else
+#define KASAN_SHADOW_INIT 0
+#endif
 
 extern unsigned char kasan_early_shadow_page[PAGE_SIZE];
 extern pte_t kasan_early_shadow_pte[PTRS_PER_PTE];
@@ -29,6 +37,23 @@ static inline void *kasan_mem_to_shadow(const void *addr)
 		+ KASAN_SHADOW_OFFSET;
 }
 
+int kasan_add_zero_shadow(void *start, unsigned long size);
+void kasan_remove_zero_shadow(void *start, unsigned long size);
+
+#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
+static inline int kasan_add_zero_shadow(void *start, unsigned long size)
+{
+	return 0;
+}
+static inline void kasan_remove_zero_shadow(void *start,
+					unsigned long size)
+{}
+
+#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
+#ifdef CONFIG_KASAN
+
 /* Enable reporting bugs after kasan_disable_current() */
 extern void kasan_enable_current(void);
 
@@ -69,9 +94,6 @@ struct kasan_cache {
 	int free_meta_offset;
 };
 
-int kasan_add_zero_shadow(void *start, unsigned long size);
-void kasan_remove_zero_shadow(void *start, unsigned long size);
-
 size_t __ksize(const void *);
 static inline void kasan_unpoison_slab(const void *ptr)
 {
@@ -137,14 +159,6 @@ static inline bool kasan_slab_free(struct kmem_cache *s, void *object,
 	return false;
 }
 
-static inline int kasan_add_zero_shadow(void *start, unsigned long size)
-{
-	return 0;
-}
-static inline void kasan_remove_zero_shadow(void *start,
-					unsigned long size)
-{}
-
 static inline void kasan_unpoison_slab(const void *ptr) { }
 static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
 
@@ -152,8 +166,6 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
 
 #ifdef CONFIG_KASAN_GENERIC
 
-#define KASAN_SHADOW_INIT 0
-
 void kasan_cache_shrink(struct kmem_cache *cache);
 void kasan_cache_shutdown(struct kmem_cache *cache);
 void kasan_record_aux_stack(void *ptr);
@@ -168,8 +180,6 @@ static inline void kasan_record_aux_stack(void *ptr) {}
 
 #ifdef CONFIG_KASAN_SW_TAGS
 
-#define KASAN_SHADOW_INIT 0xFF
-
 void kasan_init_tags(void);
 
 void *kasan_reset_tag(const void *addr);
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#4 lore raw

PATCH v2 4/37 kasan: rename (un)poison_shadow to (un)poison_memory

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

The new mode won't be using shadow memory, but will reuse the same functions. Rename kasan_unpoison_shadow to kasan_unpoison_memory, and kasan_poison_shadow to kasan_poison_memory.

No functional changes.

---
Change-Id: Ia359f32815242c4704e49a5f1639ca2d2f8cba69

---
 include/linux/kasan.h |  6 +++---
 kernel/fork.c         |  4 ++--
 mm/kasan/common.c     | 38 +++++++++++++++++++-------------------
 mm/kasan/generic.c    | 12 ++++++------
 mm/kasan/kasan.h      |  2 +-
 mm/kasan/tags.c       |  2 +-
 mm/slab_common.c      |  2 +-
 7 files changed, 33 insertions(+), 33 deletions(-)
include/linux/kasan.h +3 -3
index 44a9aae44138..18617d5c4cd7 100644
@@ -60,7 +60,7 @@ extern void kasan_enable_current(void);
 /* Disable reporting bugs for current task */
 extern void kasan_disable_current(void);
 
-void kasan_unpoison_shadow(const void *address, size_t size);
+void kasan_unpoison_memory(const void *address, size_t size);
 
 void kasan_unpoison_task_stack(struct task_struct *task);
 
@@ -97,7 +97,7 @@ struct kasan_cache {
 size_t __ksize(const void *);
 static inline void kasan_unpoison_slab(const void *ptr)
 {
-	kasan_unpoison_shadow(ptr, __ksize(ptr));
+	kasan_unpoison_memory(ptr, __ksize(ptr));
 }
 size_t kasan_metadata_size(struct kmem_cache *cache);
 
@@ -106,7 +106,7 @@ void kasan_restore_multi_shot(bool enabled);
 
 #else /* CONFIG_KASAN */
 
-static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
+static inline void kasan_unpoison_memory(const void *address, size_t size) {}
 
 static inline void kasan_unpoison_task_stack(struct task_struct *task) {}
 
kernel/fork.c +2 -2
index 4d32190861bd..b41fecca59d7 100644
@@ -224,8 +224,8 @@ static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node)
 		if (!s)
 			continue;
 
-		/* Clear the KASAN shadow of the stack. */
-		kasan_unpoison_shadow(s->addr, THREAD_SIZE);
+		/* Mark stack accessible for KASAN. */
+		kasan_unpoison_memory(s->addr, THREAD_SIZE);
 
 		/* Clear stale pointers from reused stack. */
 		memset(s->addr, 0, THREAD_SIZE);
mm/kasan/common.c +19 -19
index d1c987f324cd..65933b27df81 100644
@@ -113,7 +113,7 @@ void *memcpy(void *dest, const void *src, size_t len)
  * Poisons the shadow memory for 'size' bytes starting from 'addr'.
  * Memory addresses should be aligned to KASAN_SHADOW_SCALE_SIZE.
  */
-void kasan_poison_shadow(const void *address, size_t size, u8 value)
+void kasan_poison_memory(const void *address, size_t size, u8 value)
 {
 	void *shadow_start, *shadow_end;
 
@@ -130,7 +130,7 @@ void kasan_poison_shadow(const void *address, size_t size, u8 value)
 	__memset(shadow_start, value, shadow_end - shadow_start);
 }
 
-void kasan_unpoison_shadow(const void *address, size_t size)
+void kasan_unpoison_memory(const void *address, size_t size)
 {
 	u8 tag = get_tag(address);
 
@@ -141,7 +141,7 @@ void kasan_unpoison_shadow(const void *address, size_t size)
 	 */
 	address = reset_tag(address);
 
-	kasan_poison_shadow(address, size, tag);
+	kasan_poison_memory(address, size, tag);
 
 	if (size & KASAN_SHADOW_MASK) {
 		u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
@@ -158,7 +158,7 @@ static void __kasan_unpoison_stack(struct task_struct *task, const void *sp)
 	void *base = task_stack_page(task);
 	size_t size = sp - base;
 
-	kasan_unpoison_shadow(base, size);
+	kasan_unpoison_memory(base, size);
 }
 
 /* Unpoison the entire stack for a task. */
@@ -177,7 +177,7 @@ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark)
 	 */
 	void *base = (void *)((unsigned long)watermark & ~(THREAD_SIZE - 1));
 
-	kasan_unpoison_shadow(base, watermark - base);
+	kasan_unpoison_memory(base, watermark - base);
 }
 
 void kasan_alloc_pages(struct page *page, unsigned int order)
@@ -191,13 +191,13 @@ void kasan_alloc_pages(struct page *page, unsigned int order)
 	tag = random_tag();
 	for (i = 0; i < (1 << order); i++)
 		page_kasan_tag_set(page + i, tag);
-	kasan_unpoison_shadow(page_address(page), PAGE_SIZE << order);
+	kasan_unpoison_memory(page_address(page), PAGE_SIZE << order);
 }
 
 void kasan_free_pages(struct page *page, unsigned int order)
 {
 	if (likely(!PageHighMem(page)))
-		kasan_poison_shadow(page_address(page),
+		kasan_poison_memory(page_address(page),
 				PAGE_SIZE << order,
 				KASAN_FREE_PAGE);
 }
@@ -289,18 +289,18 @@ void kasan_poison_slab(struct page *page)
 
 	for (i = 0; i < compound_nr(page); i++)
 		page_kasan_tag_reset(page + i);
-	kasan_poison_shadow(page_address(page), page_size(page),
+	kasan_poison_memory(page_address(page), page_size(page),
 			KASAN_KMALLOC_REDZONE);
 }
 
 void kasan_unpoison_object_data(struct kmem_cache *cache, void *object)
 {
-	kasan_unpoison_shadow(object, cache->object_size);
+	kasan_unpoison_memory(object, cache->object_size);
 }
 
 void kasan_poison_object_data(struct kmem_cache *cache, void *object)
 {
-	kasan_poison_shadow(object,
+	kasan_poison_memory(object,
 			round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE),
 			KASAN_KMALLOC_REDZONE);
 }
@@ -413,7 +413,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
 	}
 
 	rounded_up_size = round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE);
-	kasan_poison_shadow(object, rounded_up_size, KASAN_KMALLOC_FREE);
+	kasan_poison_memory(object, rounded_up_size, KASAN_KMALLOC_FREE);
 
 	if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine) ||
 			unlikely(!(cache->flags & SLAB_KASAN)))
@@ -453,8 +453,8 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
 		tag = assign_tag(cache, object, false, keep_tag);
 
 	/* Tag is ignored in set_tag without CONFIG_KASAN_SW_TAGS */
-	kasan_unpoison_shadow(set_tag(object, tag), size);
-	kasan_poison_shadow((void *)redzone_start, redzone_end - redzone_start,
+	kasan_unpoison_memory(set_tag(object, tag), size);
+	kasan_poison_memory((void *)redzone_start, redzone_end - redzone_start,
 		KASAN_KMALLOC_REDZONE);
 
 	if (cache->flags & SLAB_KASAN)
@@ -494,8 +494,8 @@ void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
 				KASAN_SHADOW_SCALE_SIZE);
 	redzone_end = (unsigned long)ptr + page_size(page);
 
-	kasan_unpoison_shadow(ptr, size);
-	kasan_poison_shadow((void *)redzone_start, redzone_end - redzone_start,
+	kasan_unpoison_memory(ptr, size);
+	kasan_poison_memory((void *)redzone_start, redzone_end - redzone_start,
 		KASAN_PAGE_REDZONE);
 
 	return (void *)ptr;
@@ -528,7 +528,7 @@ void kasan_poison_kfree(void *ptr, unsigned long ip)
 			kasan_report_invalid_free(ptr, ip);
 			return;
 		}
-		kasan_poison_shadow(ptr, page_size(page), KASAN_FREE_PAGE);
+		kasan_poison_memory(ptr, page_size(page), KASAN_FREE_PAGE);
 	} else {
 		__kasan_slab_free(page->slab_cache, ptr, ip, false);
 	}
@@ -714,7 +714,7 @@ int kasan_populate_vmalloc(unsigned long addr, unsigned long size)
 	 * // vmalloc() allocates memory
 	 * // let a = area->addr
 	 * // we reach kasan_populate_vmalloc
-	 * // and call kasan_unpoison_shadow:
+	 * // and call kasan_unpoison_memory:
 	 * STORE shadow(a), unpoison_val
 	 * ...
 	 * STORE shadow(a+99), unpoison_val	x = LOAD p
@@ -749,7 +749,7 @@ void kasan_poison_vmalloc(const void *start, unsigned long size)
 		return;
 
 	size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
-	kasan_poison_shadow(start, size, KASAN_VMALLOC_INVALID);
+	kasan_poison_memory(start, size, KASAN_VMALLOC_INVALID);
 }
 
 void kasan_unpoison_vmalloc(const void *start, unsigned long size)
@@ -757,7 +757,7 @@ void kasan_unpoison_vmalloc(const void *start, unsigned long size)
 	if (!is_vmalloc_or_module_addr(start))
 		return;
 
-	kasan_unpoison_shadow(start, size);
+	kasan_unpoison_memory(start, size);
 }
 
 static int kasan_depopulate_vmalloc_pte(pte_t *ptep, unsigned long addr,
mm/kasan/generic.c +6 -6
index 248264b9cb76..4b5f905198d8 100644
@@ -207,9 +207,9 @@ static void register_global(struct kasan_global *global)
 {
 	size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE);
 
-	kasan_unpoison_shadow(global->beg, global->size);
+	kasan_unpoison_memory(global->beg, global->size);
 
-	kasan_poison_shadow(global->beg + aligned_size,
+	kasan_poison_memory(global->beg + aligned_size,
 		global->size_with_redzone - aligned_size,
 		KASAN_GLOBAL_REDZONE);
 }
@@ -290,11 +290,11 @@ void __asan_alloca_poison(unsigned long addr, size_t size)
 
 	WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
 
-	kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+	kasan_unpoison_memory((const void *)(addr + rounded_down_size),
 			      size - rounded_down_size);
-	kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+	kasan_poison_memory(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
 			KASAN_ALLOCA_LEFT);
-	kasan_poison_shadow(right_redzone,
+	kasan_poison_memory(right_redzone,
 			padding_size + KASAN_ALLOCA_REDZONE_SIZE,
 			KASAN_ALLOCA_RIGHT);
 }
@@ -306,7 +306,7 @@ void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
 	if (unlikely(!stack_top || stack_top > stack_bottom))
 		return;
 
-	kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+	kasan_unpoison_memory(stack_top, stack_bottom - stack_top);
 }
 EXPORT_SYMBOL(__asan_allocas_unpoison);
 
mm/kasan/kasan.h +1 -1
index ac499456740f..03450d3b31f7 100644
@@ -150,7 +150,7 @@ static inline bool addr_has_shadow(const void *addr)
 	return (addr >= kasan_shadow_to_mem((void *)KASAN_SHADOW_START));
 }
 
-void kasan_poison_shadow(const void *address, size_t size, u8 value);
+void kasan_poison_memory(const void *address, size_t size, u8 value);
 
 /**
  * check_memory_region - Check memory region, and report if invalid access.
mm/kasan/tags.c +1 -1
index e02a36a51f42..4d5a1fe8251f 100644
@@ -158,7 +158,7 @@ EXPORT_SYMBOL(__hwasan_storeN_noabort);
 
 void __hwasan_tag_memory(unsigned long addr, u8 tag, unsigned long size)
 {
-	kasan_poison_shadow((void *)addr, size, tag);
+	kasan_poison_memory((void *)addr, size, tag);
 }
 EXPORT_SYMBOL(__hwasan_tag_memory);
 
mm/slab_common.c +1 -1
index f9ccd5dc13f3..53d0f8bb57ea 100644
@@ -1176,7 +1176,7 @@ size_t ksize(const void *objp)
 	 * We assume that ksize callers could use whole allocated area,
 	 * so we need to unpoison this area.
 	 */
-	kasan_unpoison_shadow(objp, size);
+	kasan_unpoison_memory(objp, size);
 	return size;
 }
 EXPORT_SYMBOL(ksize);
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#5 lore raw

PATCH v2 5/37 kasan: rename KASAN_SHADOW_* to KASAN_GRANULE_*

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

The new mode won't be using shadow memory, but will still use the concept of memory granules. Rename KASAN_SHADOW_SCALE_SIZE to KASAN_GRANULE_SIZE, and KASAN_SHADOW_MASK to KASAN_GRANULE_MASK.

Also use MASK when used as a mask, otherwise use SIZE.

No functional changes.

---
Change-Id: Iac733e2248aa9d29f6fc425d8946ba07cca73ecf

---
 Documentation/dev-tools/kasan.rst |  2 +-
 lib/test_kasan.c                  |  2 +-
 mm/kasan/common.c                 | 39 ++++++++++++++++---------------
 mm/kasan/generic.c                | 14 +++++------
 mm/kasan/generic_report.c         |  8 +++----
 mm/kasan/init.c                   |  8 +++----
 mm/kasan/kasan.h                  |  4 ++--
 mm/kasan/report.c                 | 10 ++++----
 mm/kasan/tags_report.c            |  2 +-
 9 files changed, 45 insertions(+), 44 deletions(-)
Documentation/dev-tools/kasan.rst +1 -1
index 38fd5681fade..a3030fc6afe5 100644
@@ -264,7 +264,7 @@ Most mappings in vmalloc space are small, requiring less than a full
 page of shadow space. Allocating a full shadow page per mapping would
 therefore be wasteful. Furthermore, to ensure that different mappings
 use different shadow pages, mappings would have to be aligned to
-``KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE``.
+``KASAN_GRANULE_SIZE * PAGE_SIZE``.
 
 Instead, we share backing space across multiple mappings. We allocate
 a backing page when a mapping in vmalloc space uses a particular page
lib/test_kasan.c +1 -1
index 53e953bb1d1d..ddd0b80f24a1 100644
@@ -25,7 +25,7 @@
 
 #include "../mm/kasan/kasan.h"
 
-#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_SHADOW_SCALE_SIZE)
+#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_GRANULE_SIZE)
 
 /*
  * We assign some test results to these globals to make sure the tests
mm/kasan/common.c +20 -19
index 65933b27df81..c9daf2c33651 100644
@@ -111,7 +111,7 @@ void *memcpy(void *dest, const void *src, size_t len)
 
 /*
  * Poisons the shadow memory for 'size' bytes starting from 'addr'.
- * Memory addresses should be aligned to KASAN_SHADOW_SCALE_SIZE.
+ * Memory addresses should be aligned to KASAN_GRANULE_SIZE.
  */
 void kasan_poison_memory(const void *address, size_t size, u8 value)
 {
@@ -143,13 +143,13 @@ void kasan_unpoison_memory(const void *address, size_t size)
 
 	kasan_poison_memory(address, size, tag);
 
-	if (size & KASAN_SHADOW_MASK) {
+	if (size & KASAN_GRANULE_MASK) {
 		u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
 
 		if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
 			*shadow = tag;
 		else
-			*shadow = size & KASAN_SHADOW_MASK;
+			*shadow = size & KASAN_GRANULE_MASK;
 	}
 }
 
@@ -301,7 +301,7 @@ void kasan_unpoison_object_data(struct kmem_cache *cache, void *object)
 void kasan_poison_object_data(struct kmem_cache *cache, void *object)
 {
 	kasan_poison_memory(object,
-			round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE),
+			round_up(cache->object_size, KASAN_GRANULE_SIZE),
 			KASAN_KMALLOC_REDZONE);
 }
 
@@ -373,7 +373,7 @@ static inline bool shadow_invalid(u8 tag, s8 shadow_byte)
 {
 	if (IS_ENABLED(CONFIG_KASAN_GENERIC))
 		return shadow_byte < 0 ||
-			shadow_byte >= KASAN_SHADOW_SCALE_SIZE;
+			shadow_byte >= KASAN_GRANULE_SIZE;
 
 	/* else CONFIG_KASAN_SW_TAGS: */
 	if ((u8)shadow_byte == KASAN_TAG_INVALID)
@@ -412,7 +412,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
 		return true;
 	}
 
-	rounded_up_size = round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE);
+	rounded_up_size = round_up(cache->object_size, KASAN_GRANULE_SIZE);
 	kasan_poison_memory(object, rounded_up_size, KASAN_KMALLOC_FREE);
 
 	if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine) ||
@@ -445,9 +445,9 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
 		return NULL;
 
 	redzone_start = round_up((unsigned long)(object + size),
-				KASAN_SHADOW_SCALE_SIZE);
+				KASAN_GRANULE_SIZE);
 	redzone_end = round_up((unsigned long)object + cache->object_size,
-				KASAN_SHADOW_SCALE_SIZE);
+				KASAN_GRANULE_SIZE);
 
 	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
 		tag = assign_tag(cache, object, false, keep_tag);
@@ -491,7 +491,7 @@ void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
 
 	page = virt_to_page(ptr);
 	redzone_start = round_up((unsigned long)(ptr + size),
-				KASAN_SHADOW_SCALE_SIZE);
+				KASAN_GRANULE_SIZE);
 	redzone_end = (unsigned long)ptr + page_size(page);
 
 	kasan_unpoison_memory(ptr, size);
@@ -589,8 +589,8 @@ static int __meminit kasan_mem_notifier(struct notifier_block *nb,
 	shadow_size = nr_shadow_pages << PAGE_SHIFT;
 	shadow_end = shadow_start + shadow_size;
 
-	if (WARN_ON(mem_data->nr_pages % KASAN_SHADOW_SCALE_SIZE) ||
-		WARN_ON(start_kaddr % (KASAN_SHADOW_SCALE_SIZE << PAGE_SHIFT)))
+	if (WARN_ON(mem_data->nr_pages % KASAN_GRANULE_SIZE) ||
+		WARN_ON(start_kaddr % (KASAN_GRANULE_SIZE << PAGE_SHIFT)))
 		return NOTIFY_BAD;
 
 	switch (action) {
@@ -748,7 +748,7 @@ void kasan_poison_vmalloc(const void *start, unsigned long size)
 	if (!is_vmalloc_or_module_addr(start))
 		return;
 
-	size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+	size = round_up(size, KASAN_GRANULE_SIZE);
 	kasan_poison_memory(start, size, KASAN_VMALLOC_INVALID);
 }
 
@@ -861,22 +861,22 @@ void kasan_release_vmalloc(unsigned long start, unsigned long end,
 	unsigned long region_start, region_end;
 	unsigned long size;
 
-	region_start = ALIGN(start, PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
-	region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
+	region_start = ALIGN(start, PAGE_SIZE * KASAN_GRANULE_SIZE);
+	region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_GRANULE_SIZE);
 
 	free_region_start = ALIGN(free_region_start,
-				  PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
+				  PAGE_SIZE * KASAN_GRANULE_SIZE);
 
 	if (start != region_start &&
 	    free_region_start < region_start)
-		region_start -= PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE;
+		region_start -= PAGE_SIZE * KASAN_GRANULE_SIZE;
 
 	free_region_end = ALIGN_DOWN(free_region_end,
-				     PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
+				     PAGE_SIZE * KASAN_GRANULE_SIZE);
 
 	if (end != region_end &&
 	    free_region_end > region_end)
-		region_end += PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE;
+		region_end += PAGE_SIZE * KASAN_GRANULE_SIZE;
 
 	shadow_start = kasan_mem_to_shadow((void *)region_start);
 	shadow_end = kasan_mem_to_shadow((void *)region_end);
@@ -902,7 +902,8 @@ int kasan_module_alloc(void *addr, size_t size)
 	unsigned long shadow_start;
 
 	shadow_start = (unsigned long)kasan_mem_to_shadow(addr);
-	scaled_size = (size + KASAN_SHADOW_MASK) >> KASAN_SHADOW_SCALE_SHIFT;
+	scaled_size = (size + KASAN_GRANULE_SIZE - 1) >>
+				KASAN_SHADOW_SCALE_SHIFT;
 	shadow_size = round_up(scaled_size, PAGE_SIZE);
 
 	if (WARN_ON(!PAGE_ALIGNED(shadow_start)))
mm/kasan/generic.c +7 -7
index 4b5f905198d8..f6d68aa9872f 100644
@@ -51,7 +51,7 @@ static __always_inline bool memory_is_poisoned_1(unsigned long addr)
 	s8 shadow_value = *(s8 *)kasan_mem_to_shadow((void *)addr);
 
 	if (unlikely(shadow_value)) {
-		s8 last_accessible_byte = addr & KASAN_SHADOW_MASK;
+		s8 last_accessible_byte = addr & KASAN_GRANULE_MASK;
 		return unlikely(last_accessible_byte >= shadow_value);
 	}
 
@@ -67,7 +67,7 @@ static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr,
 	 * Access crosses 8(shadow size)-byte boundary. Such access maps
 	 * into 2 shadow bytes, so we need to check them both.
 	 */
-	if (unlikely(((addr + size - 1) & KASAN_SHADOW_MASK) < size - 1))
+	if (unlikely(((addr + size - 1) & KASAN_GRANULE_MASK) < size - 1))
 		return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
 
 	return memory_is_poisoned_1(addr + size - 1);
@@ -78,7 +78,7 @@ static __always_inline bool memory_is_poisoned_16(unsigned long addr)
 	u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
 
 	/* Unaligned 16-bytes access maps into 3 shadow bytes. */
-	if (unlikely(!IS_ALIGNED(addr, KASAN_SHADOW_SCALE_SIZE)))
+	if (unlikely(!IS_ALIGNED(addr, KASAN_GRANULE_SIZE)))
 		return *shadow_addr || memory_is_poisoned_1(addr + 15);
 
 	return *shadow_addr;
@@ -139,7 +139,7 @@ static __always_inline bool memory_is_poisoned_n(unsigned long addr,
 		s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte);
 
 		if (unlikely(ret != (unsigned long)last_shadow ||
-			((long)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow)))
+			((long)(last_byte & KASAN_GRANULE_MASK) >= *last_shadow)))
 			return true;
 	}
 	return false;
@@ -205,7 +205,7 @@ void kasan_cache_shutdown(struct kmem_cache *cache)
 
 static void register_global(struct kasan_global *global)
 {
-	size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE);
+	size_t aligned_size = round_up(global->size, KASAN_GRANULE_SIZE);
 
 	kasan_unpoison_memory(global->beg, global->size);
 
@@ -279,10 +279,10 @@ EXPORT_SYMBOL(__asan_handle_no_return);
 /* Emitted by compiler to poison alloca()ed objects. */
 void __asan_alloca_poison(unsigned long addr, size_t size)
 {
-	size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+	size_t rounded_up_size = round_up(size, KASAN_GRANULE_SIZE);
 	size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
 			rounded_up_size;
-	size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+	size_t rounded_down_size = round_down(size, KASAN_GRANULE_SIZE);
 
 	const void *left_redzone = (const void *)(addr -
 			KASAN_ALLOCA_REDZONE_SIZE);
mm/kasan/generic_report.c +4 -4
index a38c7a9e192a..4dce1633b082 100644
@@ -39,7 +39,7 @@ void *find_first_bad_addr(void *addr, size_t size)
 	void *p = addr;
 
 	while (p < addr + size && !(*(u8 *)kasan_mem_to_shadow(p)))
-		p += KASAN_SHADOW_SCALE_SIZE;
+		p += KASAN_GRANULE_SIZE;
 	return p;
 }
 
@@ -51,14 +51,14 @@ static const char *get_shadow_bug_type(struct kasan_access_info *info)
 	shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr);
 
 	/*
-	 * If shadow byte value is in [0, KASAN_SHADOW_SCALE_SIZE) we can look
+	 * If shadow byte value is in [0, KASAN_GRANULE_SIZE) we can look
 	 * at the next shadow byte to determine the type of the bad access.
 	 */
-	if (*shadow_addr > 0 && *shadow_addr <= KASAN_SHADOW_SCALE_SIZE - 1)
+	if (*shadow_addr > 0 && *shadow_addr <= KASAN_GRANULE_SIZE - 1)
 		shadow_addr++;
 
 	switch (*shadow_addr) {
-	case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
+	case 0 ... KASAN_GRANULE_SIZE - 1:
 		/*
 		 * In theory it's still possible to see these shadow values
 		 * due to a data race in the kernel code.
mm/kasan/init.c +4 -4
index fe6be0be1f76..754b641c83c7 100644
@@ -447,8 +447,8 @@ void kasan_remove_zero_shadow(void *start, unsigned long size)
 	end = addr + (size >> KASAN_SHADOW_SCALE_SHIFT);
 
 	if (WARN_ON((unsigned long)start %
-			(KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) ||
-	    WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)))
+			(KASAN_GRANULE_SIZE * PAGE_SIZE)) ||
+	    WARN_ON(size % (KASAN_GRANULE_SIZE * PAGE_SIZE)))
 		return;
 
 	for (; addr < end; addr = next) {
@@ -482,8 +482,8 @@ int kasan_add_zero_shadow(void *start, unsigned long size)
 	shadow_end = shadow_start + (size >> KASAN_SHADOW_SCALE_SHIFT);
 
 	if (WARN_ON((unsigned long)start %
-			(KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) ||
-	    WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)))
+			(KASAN_GRANULE_SIZE * PAGE_SIZE)) ||
+	    WARN_ON(size % (KASAN_GRANULE_SIZE * PAGE_SIZE)))
 		return -EINVAL;
 
 	ret = kasan_populate_early_shadow(shadow_start, shadow_end);
mm/kasan/kasan.h +2 -2
index 03450d3b31f7..c31e2c739301 100644
@@ -5,8 +5,8 @@
 #include <linux/kasan.h>
 #include <linux/stackdepot.h>
 
-#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
-#define KASAN_SHADOW_MASK       (KASAN_SHADOW_SCALE_SIZE - 1)
+#define KASAN_GRANULE_SIZE	(1UL << KASAN_SHADOW_SCALE_SHIFT)
+#define KASAN_GRANULE_MASK	(KASAN_GRANULE_SIZE - 1)
 
 #define KASAN_TAG_KERNEL	0xFF /* native kernel pointers tag */
 #define KASAN_TAG_INVALID	0xFE /* inaccessible memory tag */
mm/kasan/report.c +5 -5
index 4f49fa6cd1aa..7c025d792e2f 100644
@@ -317,24 +317,24 @@ static bool __must_check get_address_stack_frame_info(const void *addr,
 		return false;
 
 	aligned_addr = round_down((unsigned long)addr, sizeof(long));
-	mem_ptr = round_down(aligned_addr, KASAN_SHADOW_SCALE_SIZE);
+	mem_ptr = round_down(aligned_addr, KASAN_GRANULE_SIZE);
 	shadow_ptr = kasan_mem_to_shadow((void *)aligned_addr);
 	shadow_bottom = kasan_mem_to_shadow(end_of_stack(current));
 
 	while (shadow_ptr >= shadow_bottom && *shadow_ptr != KASAN_STACK_LEFT) {
 		shadow_ptr--;
-		mem_ptr -= KASAN_SHADOW_SCALE_SIZE;
+		mem_ptr -= KASAN_GRANULE_SIZE;
 	}
 
 	while (shadow_ptr >= shadow_bottom && *shadow_ptr == KASAN_STACK_LEFT) {
 		shadow_ptr--;
-		mem_ptr -= KASAN_SHADOW_SCALE_SIZE;
+		mem_ptr -= KASAN_GRANULE_SIZE;
 	}
 
 	if (shadow_ptr < shadow_bottom)
 		return false;
 
-	frame = (const unsigned long *)(mem_ptr + KASAN_SHADOW_SCALE_SIZE);
+	frame = (const unsigned long *)(mem_ptr + KASAN_GRANULE_SIZE);
 	if (frame[0] != KASAN_CURRENT_STACK_FRAME_MAGIC) {
 		pr_err("KASAN internal error: frame info validation failed; invalid marker: %lu\n",
 		       frame[0]);
@@ -572,6 +572,6 @@ void kasan_non_canonical_hook(unsigned long addr)
 	else
 		bug_type = "maybe wild-memory-access";
 	pr_alert("KASAN: %s in range [0x%016lx-0x%016lx]\n", bug_type,
-		 orig_addr, orig_addr + KASAN_SHADOW_MASK);
+		 orig_addr, orig_addr + KASAN_GRANULE_SIZE - 1);
 }
 #endif
mm/kasan/tags_report.c +1 -1
index bee43717d6f0..6ddb55676a7c 100644
@@ -81,7 +81,7 @@ void *find_first_bad_addr(void *addr, size_t size)
 	void *end = p + size;
 
 	while (p < end && tag == *(u8 *)kasan_mem_to_shadow(p))
-		p += KASAN_SHADOW_SCALE_SIZE;
+		p += KASAN_GRANULE_SIZE;
 	return p;
 }
 
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#6 lore raw

Re: [PATCH v2 05/37] kasan: rename KASAN_SHADOW_* to KASAN_GRANULE_*

Quoted 6 lines · Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

The new mode won't be using shadow memory, but will still use the concept of memory granules.

KASAN documentation doesn't seem to explain this concept anywhere (I also checked the "kasan: add documentation for hardware tag-based mode" patch), looks like it's only mentioned in MTE documentation. Could you please elaborate on what we consider a granule in each of KASAN modes?

Quoted 376 lines

Rename KASAN_SHADOW_SCALE_SIZE to KASAN_GRANULE_SIZE, and KASAN_SHADOW_MASK to KASAN_GRANULE_MASK.

Also use MASK when used as a mask, otherwise use SIZE.

No functional changes.

---
Change-Id: Iac733e2248aa9d29f6fc425d8946ba07cca73ecf

---
 Documentation/dev-tools/kasan.rst |  2 +-
 lib/test_kasan.c                  |  2 +-
 mm/kasan/common.c                 | 39 ++++++++++++++++---------------
 mm/kasan/generic.c                | 14 +++++------
 mm/kasan/generic_report.c         |  8 +++----
 mm/kasan/init.c                   |  8 +++----
 mm/kasan/kasan.h                  |  4 ++--
 mm/kasan/report.c                 | 10 ++++----
 mm/kasan/tags_report.c            |  2 +-
 9 files changed, 45 insertions(+), 44 deletions(-)
Documentation/dev-tools/kasan.rst +1 -1
index 38fd5681fade..a3030fc6afe5 100644
@@ -264,7 +264,7 @@ Most mappings in vmalloc space are small, requiring less than a full
 page of shadow space. Allocating a full shadow page per mapping would
 therefore be wasteful. Furthermore, to ensure that different mappings
 use different shadow pages, mappings would have to be aligned to
-``KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE``.
+``KASAN_GRANULE_SIZE * PAGE_SIZE``.
 
 Instead, we share backing space across multiple mappings. We allocate
 a backing page when a mapping in vmalloc space uses a particular page
lib/test_kasan.c +1 -1
index 53e953bb1d1d..ddd0b80f24a1 100644
@@ -25,7 +25,7 @@
 
 #include "../mm/kasan/kasan.h"
 
-#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_SHADOW_SCALE_SIZE)
+#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_GRANULE_SIZE)
 
 /*
  * We assign some test results to these globals to make sure the tests
mm/kasan/common.c +20 -19
index 65933b27df81..c9daf2c33651 100644
@@ -111,7 +111,7 @@ void *memcpy(void *dest, const void *src, size_t len)
 
 /*
  * Poisons the shadow memory for 'size' bytes starting from 'addr'.
- * Memory addresses should be aligned to KASAN_SHADOW_SCALE_SIZE.
+ * Memory addresses should be aligned to KASAN_GRANULE_SIZE.
  */
 void kasan_poison_memory(const void *address, size_t size, u8 value)
 {
@@ -143,13 +143,13 @@ void kasan_unpoison_memory(const void *address, size_t size)
 
        kasan_poison_memory(address, size, tag);
 
-       if (size & KASAN_SHADOW_MASK) {
+       if (size & KASAN_GRANULE_MASK) {
                u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
 
                if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
                        *shadow = tag;
                else
-                       *shadow = size & KASAN_SHADOW_MASK;
+                       *shadow = size & KASAN_GRANULE_MASK;
        }
 }
 
@@ -301,7 +301,7 @@ void kasan_unpoison_object_data(struct kmem_cache *cache, void *object)
 void kasan_poison_object_data(struct kmem_cache *cache, void *object)
 {
        kasan_poison_memory(object,
-                       round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE),
+                       round_up(cache->object_size, KASAN_GRANULE_SIZE),
                        KASAN_KMALLOC_REDZONE);
 }
 
@@ -373,7 +373,7 @@ static inline bool shadow_invalid(u8 tag, s8 shadow_byte)
 {
        if (IS_ENABLED(CONFIG_KASAN_GENERIC))
                return shadow_byte < 0 ||
-                       shadow_byte >= KASAN_SHADOW_SCALE_SIZE;
+                       shadow_byte >= KASAN_GRANULE_SIZE;
 
        /* else CONFIG_KASAN_SW_TAGS: */
        if ((u8)shadow_byte == KASAN_TAG_INVALID)
@@ -412,7 +412,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
                return true;
        }
 
-       rounded_up_size = round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE);
+       rounded_up_size = round_up(cache->object_size, KASAN_GRANULE_SIZE);
        kasan_poison_memory(object, rounded_up_size, KASAN_KMALLOC_FREE);
 
        if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine) ||
@@ -445,9 +445,9 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
                return NULL;
 
        redzone_start = round_up((unsigned long)(object + size),
-                               KASAN_SHADOW_SCALE_SIZE);
+                               KASAN_GRANULE_SIZE);
        redzone_end = round_up((unsigned long)object + cache->object_size,
-                               KASAN_SHADOW_SCALE_SIZE);
+                               KASAN_GRANULE_SIZE);
 
        if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
                tag = assign_tag(cache, object, false, keep_tag);
@@ -491,7 +491,7 @@ void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
 
        page = virt_to_page(ptr);
        redzone_start = round_up((unsigned long)(ptr + size),
-                               KASAN_SHADOW_SCALE_SIZE);
+                               KASAN_GRANULE_SIZE);
        redzone_end = (unsigned long)ptr + page_size(page);
 
        kasan_unpoison_memory(ptr, size);
@@ -589,8 +589,8 @@ static int __meminit kasan_mem_notifier(struct notifier_block *nb,
        shadow_size = nr_shadow_pages << PAGE_SHIFT;
        shadow_end = shadow_start + shadow_size;
 
-       if (WARN_ON(mem_data->nr_pages % KASAN_SHADOW_SCALE_SIZE) ||
-               WARN_ON(start_kaddr % (KASAN_SHADOW_SCALE_SIZE << PAGE_SHIFT)))
+       if (WARN_ON(mem_data->nr_pages % KASAN_GRANULE_SIZE) ||
+               WARN_ON(start_kaddr % (KASAN_GRANULE_SIZE << PAGE_SHIFT)))
                return NOTIFY_BAD;
 
        switch (action) {
@@ -748,7 +748,7 @@ void kasan_poison_vmalloc(const void *start, unsigned long size)
        if (!is_vmalloc_or_module_addr(start))
                return;
 
-       size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+       size = round_up(size, KASAN_GRANULE_SIZE);
        kasan_poison_memory(start, size, KASAN_VMALLOC_INVALID);
 }
 
@@ -861,22 +861,22 @@ void kasan_release_vmalloc(unsigned long start, unsigned long end,
        unsigned long region_start, region_end;
        unsigned long size;
 
-       region_start = ALIGN(start, PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
-       region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
+       region_start = ALIGN(start, PAGE_SIZE * KASAN_GRANULE_SIZE);
+       region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_GRANULE_SIZE);
 
        free_region_start = ALIGN(free_region_start,
-                                 PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
+                                 PAGE_SIZE * KASAN_GRANULE_SIZE);
 
        if (start != region_start &&
            free_region_start < region_start)
-               region_start -= PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE;
+               region_start -= PAGE_SIZE * KASAN_GRANULE_SIZE;
 
        free_region_end = ALIGN_DOWN(free_region_end,
-                                    PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE);
+                                    PAGE_SIZE * KASAN_GRANULE_SIZE);
 
        if (end != region_end &&
            free_region_end > region_end)
-               region_end += PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE;
+               region_end += PAGE_SIZE * KASAN_GRANULE_SIZE;
 
        shadow_start = kasan_mem_to_shadow((void *)region_start);
        shadow_end = kasan_mem_to_shadow((void *)region_end);
@@ -902,7 +902,8 @@ int kasan_module_alloc(void *addr, size_t size)
        unsigned long shadow_start;
 
        shadow_start = (unsigned long)kasan_mem_to_shadow(addr);
-       scaled_size = (size + KASAN_SHADOW_MASK) >> KASAN_SHADOW_SCALE_SHIFT;
+       scaled_size = (size + KASAN_GRANULE_SIZE - 1) >>
+                               KASAN_SHADOW_SCALE_SHIFT;
        shadow_size = round_up(scaled_size, PAGE_SIZE);
 
        if (WARN_ON(!PAGE_ALIGNED(shadow_start)))
mm/kasan/generic.c +7 -7
index 4b5f905198d8..f6d68aa9872f 100644
@@ -51,7 +51,7 @@ static __always_inline bool memory_is_poisoned_1(unsigned long addr)
        s8 shadow_value = *(s8 *)kasan_mem_to_shadow((void *)addr);
 
        if (unlikely(shadow_value)) {
-               s8 last_accessible_byte = addr & KASAN_SHADOW_MASK;
+               s8 last_accessible_byte = addr & KASAN_GRANULE_MASK;
                return unlikely(last_accessible_byte >= shadow_value);
        }
 
@@ -67,7 +67,7 @@ static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr,
         * Access crosses 8(shadow size)-byte boundary. Such access maps
         * into 2 shadow bytes, so we need to check them both.
         */
-       if (unlikely(((addr + size - 1) & KASAN_SHADOW_MASK) < size - 1))
+       if (unlikely(((addr + size - 1) & KASAN_GRANULE_MASK) < size - 1))
                return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
 
        return memory_is_poisoned_1(addr + size - 1);
@@ -78,7 +78,7 @@ static __always_inline bool memory_is_poisoned_16(unsigned long addr)
        u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
 
        /* Unaligned 16-bytes access maps into 3 shadow bytes. */
-       if (unlikely(!IS_ALIGNED(addr, KASAN_SHADOW_SCALE_SIZE)))
+       if (unlikely(!IS_ALIGNED(addr, KASAN_GRANULE_SIZE)))
                return *shadow_addr || memory_is_poisoned_1(addr + 15);
 
        return *shadow_addr;
@@ -139,7 +139,7 @@ static __always_inline bool memory_is_poisoned_n(unsigned long addr,
                s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte);
 
                if (unlikely(ret != (unsigned long)last_shadow ||
-                       ((long)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow)))
+                       ((long)(last_byte & KASAN_GRANULE_MASK) >= *last_shadow)))
                        return true;
        }
        return false;
@@ -205,7 +205,7 @@ void kasan_cache_shutdown(struct kmem_cache *cache)
 
 static void register_global(struct kasan_global *global)
 {
-       size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE);
+       size_t aligned_size = round_up(global->size, KASAN_GRANULE_SIZE);
 
        kasan_unpoison_memory(global->beg, global->size);
 
@@ -279,10 +279,10 @@ EXPORT_SYMBOL(__asan_handle_no_return);
 /* Emitted by compiler to poison alloca()ed objects. */
 void __asan_alloca_poison(unsigned long addr, size_t size)
 {
-       size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+       size_t rounded_up_size = round_up(size, KASAN_GRANULE_SIZE);
        size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
                        rounded_up_size;
-       size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+       size_t rounded_down_size = round_down(size, KASAN_GRANULE_SIZE);
 
        const void *left_redzone = (const void *)(addr -
                        KASAN_ALLOCA_REDZONE_SIZE);
mm/kasan/generic_report.c +4 -4
index a38c7a9e192a..4dce1633b082 100644
@@ -39,7 +39,7 @@ void *find_first_bad_addr(void *addr, size_t size)
        void *p = addr;
 
        while (p < addr + size && !(*(u8 *)kasan_mem_to_shadow(p)))
-               p += KASAN_SHADOW_SCALE_SIZE;
+               p += KASAN_GRANULE_SIZE;
        return p;
 }
 
@@ -51,14 +51,14 @@ static const char *get_shadow_bug_type(struct kasan_access_info *info)
        shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr);
 
        /*
-        * If shadow byte value is in [0, KASAN_SHADOW_SCALE_SIZE) we can look
+        * If shadow byte value is in [0, KASAN_GRANULE_SIZE) we can look
         * at the next shadow byte to determine the type of the bad access.
         */
-       if (*shadow_addr > 0 && *shadow_addr <= KASAN_SHADOW_SCALE_SIZE - 1)
+       if (*shadow_addr > 0 && *shadow_addr <= KASAN_GRANULE_SIZE - 1)
                shadow_addr++;
 
        switch (*shadow_addr) {
-       case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
+       case 0 ... KASAN_GRANULE_SIZE - 1:
                /*
                 * In theory it's still possible to see these shadow values
                 * due to a data race in the kernel code.
mm/kasan/init.c +4 -4
index fe6be0be1f76..754b641c83c7 100644
@@ -447,8 +447,8 @@ void kasan_remove_zero_shadow(void *start, unsigned long size)
        end = addr + (size >> KASAN_SHADOW_SCALE_SHIFT);
 
        if (WARN_ON((unsigned long)start %
-                       (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) ||
-           WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)))
+                       (KASAN_GRANULE_SIZE * PAGE_SIZE)) ||
+           WARN_ON(size % (KASAN_GRANULE_SIZE * PAGE_SIZE)))
                return;
 
        for (; addr < end; addr = next) {
@@ -482,8 +482,8 @@ int kasan_add_zero_shadow(void *start, unsigned long size)
        shadow_end = shadow_start + (size >> KASAN_SHADOW_SCALE_SHIFT);
 
        if (WARN_ON((unsigned long)start %
-                       (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)) ||
-           WARN_ON(size % (KASAN_SHADOW_SCALE_SIZE * PAGE_SIZE)))
+                       (KASAN_GRANULE_SIZE * PAGE_SIZE)) ||
+           WARN_ON(size % (KASAN_GRANULE_SIZE * PAGE_SIZE)))
                return -EINVAL;
 
        ret = kasan_populate_early_shadow(shadow_start, shadow_end);
mm/kasan/kasan.h +2 -2
index 03450d3b31f7..c31e2c739301 100644
@@ -5,8 +5,8 @@
 #include <linux/kasan.h>
 #include <linux/stackdepot.h>
 
-#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
-#define KASAN_SHADOW_MASK       (KASAN_SHADOW_SCALE_SIZE - 1)
+#define KASAN_GRANULE_SIZE     (1UL << KASAN_SHADOW_SCALE_SHIFT)
+#define KASAN_GRANULE_MASK     (KASAN_GRANULE_SIZE - 1)
 
 #define KASAN_TAG_KERNEL       0xFF /* native kernel pointers tag */
 #define KASAN_TAG_INVALID      0xFE /* inaccessible memory tag */
mm/kasan/report.c +5 -5
index 4f49fa6cd1aa..7c025d792e2f 100644
@@ -317,24 +317,24 @@ static bool __must_check get_address_stack_frame_info(const void *addr,
                return false;
 
        aligned_addr = round_down((unsigned long)addr, sizeof(long));
-       mem_ptr = round_down(aligned_addr, KASAN_SHADOW_SCALE_SIZE);
+       mem_ptr = round_down(aligned_addr, KASAN_GRANULE_SIZE);
        shadow_ptr = kasan_mem_to_shadow((void *)aligned_addr);
        shadow_bottom = kasan_mem_to_shadow(end_of_stack(current));
 
        while (shadow_ptr >= shadow_bottom && *shadow_ptr != KASAN_STACK_LEFT) {
                shadow_ptr--;
-               mem_ptr -= KASAN_SHADOW_SCALE_SIZE;
+               mem_ptr -= KASAN_GRANULE_SIZE;
        }
 
        while (shadow_ptr >= shadow_bottom && *shadow_ptr == KASAN_STACK_LEFT) {
                shadow_ptr--;
-               mem_ptr -= KASAN_SHADOW_SCALE_SIZE;
+               mem_ptr -= KASAN_GRANULE_SIZE;
        }
 
        if (shadow_ptr < shadow_bottom)
                return false;
 
-       frame = (const unsigned long *)(mem_ptr + KASAN_SHADOW_SCALE_SIZE);
+       frame = (const unsigned long *)(mem_ptr + KASAN_GRANULE_SIZE);
        if (frame[0] != KASAN_CURRENT_STACK_FRAME_MAGIC) {
                pr_err("KASAN internal error: frame info validation failed; invalid marker: %lu\n",
                       frame[0]);
@@ -572,6 +572,6 @@ void kasan_non_canonical_hook(unsigned long addr)
        else
                bug_type = "maybe wild-memory-access";
        pr_alert("KASAN: %s in range [0x%016lx-0x%016lx]\n", bug_type,
-                orig_addr, orig_addr + KASAN_SHADOW_MASK);
+                orig_addr, orig_addr + KASAN_GRANULE_SIZE - 1);
 }
 #endif
mm/kasan/tags_report.c +1 -2
index bee43717d6f0..6ddb55676a7c 100644
@@ -81,7 +81,7 @@ void *find_first_bad_addr(void *addr, size_t size)
        void *end = p + size;
 
        while (p < end && tag == *(u8 *)kasan_mem_to_shadow(p))
-               p += KASAN_SHADOW_SCALE_SIZE;
+               p += KASAN_GRANULE_SIZE;
        return p;
 }
 
--

2.28.0.618.gf4bc123cb7-goog

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#7 lore raw

Re: [PATCH v2 05/37] kasan: rename KASAN_SHADOW_* to KASAN_GRANULE_*

Quoted 6 lines · Alexander Potapenko' via kasan-dev
Quoted 4 lines · Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Quoted 7 lines
Quoted 2 lines

The new mode won't be using shadow memory, but will still use the concept of memory granules.

KASAN documentation doesn't seem to explain this concept anywhere (I also checked the "kasan: add documentation for hardware tag-based mode" patch), looks like it's only mentioned in MTE documentation. Could you please elaborate on what we consider a granule in each of KASAN modes?

Sure, will do in v3, thanks!

#8 lore raw

PATCH v2 6/37 kasan: only build init.c for software modes

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

The new mode won't be using shadow memory, so only build init.c that contains shadow initialization code for software modes.

No functional changes for software modes.

---
Change-Id: I8d68c47345afc1dbedadde738f34a874dcae5080

---
 mm/kasan/Makefile | 6 +++---
 mm/kasan/init.c   | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)
mm/kasan/Makefile +3 -3
index 370d970e5ab5..7cf685bb51bd 100644
@@ -29,6 +29,6 @@ CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_tags_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 
-obj-$(CONFIG_KASAN) := common.o init.o report.o
-obj-$(CONFIG_KASAN_GENERIC) += generic.o generic_report.o quarantine.o
-obj-$(CONFIG_KASAN_SW_TAGS) += tags.o tags_report.o
+obj-$(CONFIG_KASAN) := common.o report.o
+obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o generic_report.o quarantine.o
+obj-$(CONFIG_KASAN_SW_TAGS) += init.o tags.o tags_report.o
mm/kasan/init.c +1 -1
index 754b641c83c7..20f5e1ab8d95 100644
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * This file contains some kasan initialization code.
+ * This file contains KASAN shadow initialization code.
  *
  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
  * Author: Andrey Ryabinin <[email protected]>
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#9 lore raw

PATCH v2 7/37 kasan: split out shadow.c from common.c

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

The new mode won't be using shadow memory. Move all shadow-related code to shadow.c, which is only enabled for software KASAN modes that use shadow memory.

No functional changes for software modes.

---
Change-Id: Ic1c32ce72d4649848e9e6a1f2c8dd269c77673f2

---
 mm/kasan/Makefile |   6 +-
 mm/kasan/common.c | 486 +------------------------------------------
 mm/kasan/shadow.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 514 insertions(+), 487 deletions(-)
 create mode 100644 mm/kasan/shadow.c
mm/kasan/Makefile +4 -2
index 7cf685bb51bd..7cc1031e1ef8 100644
@@ -10,6 +10,7 @@ CFLAGS_REMOVE_generic_report.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_quarantine.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_shadow.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_tags.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_tags_report.o = $(CC_FLAGS_FTRACE)
 
@@ -26,9 +27,10 @@ CFLAGS_generic_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_init.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_quarantine.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_shadow.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_tags_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 
 obj-$(CONFIG_KASAN) := common.o report.o
-obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o generic_report.o quarantine.o
-obj-$(CONFIG_KASAN_SW_TAGS) += init.o tags.o tags_report.o
+obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o generic_report.o shadow.o quarantine.o
+obj-$(CONFIG_KASAN_SW_TAGS) += init.o shadow.o tags.o tags_report.o
mm/kasan/common.c +1 -485

514 lines not on this page · raw message

mm/kasan/shadow.c new file +509 -0

509 lines not on this page · raw message

Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#10 lore raw

Re: [PATCH v2 07/37] kasan: split out shadow.c from common.c

mm/kasan/shadow.c line 3

Quoted 9 lines
mm/kasan/shadow.c new file +3 -0
new file mode 100644
index 000000000000..4888084ecdfc
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains KASAN shadow runtime code.

I think it will be nice to mention here which KASAN modes are going to use this file.

mm/kasan/shadow.c line 52

Quoted 9 lines
mm/kasan/shadow.c new file +9 -0
+#undef memset
+void *memset(void *addr, int c, size_t len)
+{
+       if (!check_memory_region((unsigned long)addr, len, true, _RET_IP_))
+               return NULL;
+
+       return __memset(addr, c, len);
+}
+

OOC, don't we need memset and memmove implementations in the hardware-based mode as well?

mm/kasan/shadow.c line 440

Quoted 2 lines
mm/kasan/shadow.c new file +2 -0
+       region_start = ALIGN(start, PAGE_SIZE * KASAN_GRANULE_SIZE);
+       region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_GRANULE_SIZE);

"PAGE_SIZE * KASAN_GRANULE_SIZE" seems to be a common thing, can we give it a name?

#11 lore raw

Re: [PATCH v2 07/37] kasan: split out shadow.c from common.c

Quoted 13 lines · Alexander Potapenko

mm/kasan/shadow.c line 3

Quoted 10 lines
mm/kasan/shadow.c new file +3 -0
new file mode 100644
index 000000000000..4888084ecdfc
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains KASAN shadow runtime code.

I think it will be nice to mention here which KASAN modes are going to use this file.

Will do in v3.

Quoted 4 lines
Quoted 4 lines
mm/kasan/shadow.c new file +4 -0
+#undef memset
+void *memset(void *addr, int c, size_t len)
+{
+       if (!check_memory_region((unsigned long)addr, len, true, _RET_IP_))
Quoted 8 lines

mm/kasan/shadow.c line 52

Quoted 5 lines
mm/kasan/shadow.c new file +5 -0
+               return NULL;
+
+       return __memset(addr, c, len);
+}
+

OOC, don't we need memset and memmove implementations in the hardware-based mode as well?

Hardware mode uses native memset implementation as all memory access instructions are checked by the hardware anyway.

Quoted 5 lines

mm/kasan/shadow.c line 440

Quoted 2 lines
mm/kasan/shadow.c new file +2 -0
+       region_start = ALIGN(start, PAGE_SIZE * KASAN_GRANULE_SIZE);
+       region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_GRANULE_SIZE);

"PAGE_SIZE * KASAN_GRANULE_SIZE" seems to be a common thing, can we give it a name?

This patch just moves the already existing code, but I can fix this in a separate patch.

#12 lore raw

PATCH v2 8/37 kasan: rename generic/tags_report.c files

In reply to Andrey Konovalov

Rename generic_report.c to report_generic.c and tags_report.c to report_tags.c, as their content is more relevant to report.c file, then to generic.c or tags.c.

No functional changes.

---
Change-Id: If77d21f655d52ef3e58c4c37fd6621a07f505f18

---
 mm/kasan/Makefile                               | 12 ++++++------
 mm/kasan/report.c                               |  2 +-
 mm/kasan/{generic_report.c => report_generic.c} |  0
 mm/kasan/{tags_report.c => report_tags.c}       |  0
 4 files changed, 7 insertions(+), 7 deletions(-)
 rename mm/kasan/{generic_report.c => report_generic.c} (100%)
 rename mm/kasan/{tags_report.c => report_tags.c} (100%)
mm/kasan/Makefile +6 -6
index 7cc1031e1ef8..b2596512421a 100644
@@ -6,13 +6,13 @@ KCOV_INSTRUMENT := n
 # Disable ftrace to avoid recursion.
 CFLAGS_REMOVE_common.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_generic.o = $(CC_FLAGS_FTRACE)
-CFLAGS_REMOVE_generic_report.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_quarantine.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_report_generic.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_report_tags.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_shadow.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_tags.o = $(CC_FLAGS_FTRACE)
-CFLAGS_REMOVE_tags_report.o = $(CC_FLAGS_FTRACE)
 
 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
 # see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533
@@ -23,14 +23,14 @@ CC_FLAGS_KASAN_RUNTIME += -DDISABLE_BRANCH_PROFILING
 
 CFLAGS_common.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
-CFLAGS_generic_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_init.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_quarantine.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_shadow.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
-CFLAGS_tags_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 
 obj-$(CONFIG_KASAN) := common.o report.o
-obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o generic_report.o shadow.o quarantine.o
-obj-$(CONFIG_KASAN_SW_TAGS) += init.o shadow.o tags.o tags_report.o
+obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o report_generic.o shadow.o quarantine.o
+obj-$(CONFIG_KASAN_SW_TAGS) += init.o report_tags.o shadow.o tags.o
mm/kasan/report.c +1 -1
index 7c025d792e2f..f16591ba9e2e 100644
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * This file contains common generic and tag-based KASAN error reporting code.
+ * This file contains common KASAN error reporting code.
  *
  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
  * Author: Andrey Ryabinin <[email protected]>
mm/kasan/report_generic.c renamed from mm/kasan/generic_report.c +0 -0
similarity index 100%
rename from mm/kasan/generic_report.c
rename to mm/kasan/report_generic.c
mm/kasan/report_tags.c renamed from mm/kasan/tags_report.c +0 -0
similarity index 100%
rename from mm/kasan/tags_report.c
rename to mm/kasan/report_tags.c
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#13 lore raw

PATCH v2 9/37 kasan: don't duplicate config dependencies

In reply to Andrey Konovalov

Both KASAN_GENERIC and KASAN_SW_TAGS have common dependencies, move those to KASAN.

---
Change-Id: I77e475802e8f1750b9154fe4a6e6da4456054fcd

---
 lib/Kconfig.kasan | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)
lib/Kconfig.kasan +3 -8
index e1d55331b618..b4cf6c519d71 100644
@@ -24,6 +24,9 @@ menuconfig KASAN
 		   (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)
 	depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
 	depends on CC_HAS_WORKING_NOSANITIZE_ADDRESS
+	select SLUB_DEBUG if SLUB
+	select CONSTRUCTORS
+	select STACKDEPOT
 	help
 	  Enables KASAN (KernelAddressSANitizer) - runtime memory debugger,
 	  designed to find out-of-bounds accesses and use-after-free bugs.
@@ -46,10 +49,6 @@ choice
 config KASAN_GENERIC
 	bool "Generic mode"
 	depends on HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC
-	depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
-	select SLUB_DEBUG if SLUB
-	select CONSTRUCTORS
-	select STACKDEPOT
 	help
 	  Enables generic KASAN mode.
 
@@ -70,10 +69,6 @@ config KASAN_GENERIC
 config KASAN_SW_TAGS
 	bool "Software tag-based mode"
 	depends on HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS
-	depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
-	select SLUB_DEBUG if SLUB
-	select CONSTRUCTORS
-	select STACKDEPOT
 	help
 	  Enables software tag-based KASAN mode.
 
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#14 lore raw

PATCH v2 10/37 kasan: hide invalid free check implementation

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

For software KASAN modes the check is based on the value in the shadow memory. Hardware tag-based KASAN won't be using shadow, so hide the implementation of the check in check_invalid_free().

No functional changes for software modes.

---
Change-Id: I5fae9531c9fc948eb4d4e0c589744032fc5a0789

---
 mm/kasan/common.c  | 19 +------------------
 mm/kasan/generic.c |  7 +++++++
 mm/kasan/kasan.h   |  2 ++
 mm/kasan/tags.c    | 12 ++++++++++++
 4 files changed, 22 insertions(+), 18 deletions(-)
mm/kasan/common.c +1 -18
index 43a927e70067..a2321d35390e 100644
@@ -277,25 +277,9 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
 	return (void *)object;
 }
 
-static inline bool shadow_invalid(u8 tag, s8 shadow_byte)
-{
-	if (IS_ENABLED(CONFIG_KASAN_GENERIC))
-		return shadow_byte < 0 ||
-			shadow_byte >= KASAN_GRANULE_SIZE;
-
-	/* else CONFIG_KASAN_SW_TAGS: */
-	if ((u8)shadow_byte == KASAN_TAG_INVALID)
-		return true;
-	if ((tag != KASAN_TAG_KERNEL) && (tag != (u8)shadow_byte))
-		return true;
-
-	return false;
-}
-
 static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
 			      unsigned long ip, bool quarantine)
 {
-	s8 shadow_byte;
 	u8 tag;
 	void *tagged_object;
 	unsigned long rounded_up_size;
@@ -314,8 +298,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
 	if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
 		return false;
 
-	shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object));
-	if (shadow_invalid(tag, shadow_byte)) {
+	if (check_invalid_free(tagged_object)) {
 		kasan_report_invalid_free(tagged_object, ip);
 		return true;
 	}
mm/kasan/generic.c +7 -0
index f6d68aa9872f..73f4d786ad5d 100644
@@ -192,6 +192,13 @@ bool check_memory_region(unsigned long addr, size_t size, bool write,
 	return check_memory_region_inline(addr, size, write, ret_ip);
 }
 
+bool check_invalid_free(void *addr)
+{
+	s8 shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(addr));
+
+	return shadow_byte < 0 || shadow_byte >= KASAN_GRANULE_SIZE;
+}
+
 void kasan_cache_shrink(struct kmem_cache *cache)
 {
 	quarantine_remove_cache(cache);
mm/kasan/kasan.h +2 -0
index c31e2c739301..cf6a135860f2 100644
@@ -163,6 +163,8 @@ void kasan_poison_memory(const void *address, size_t size, u8 value);
 bool check_memory_region(unsigned long addr, size_t size, bool write,
 				unsigned long ret_ip);
 
+bool check_invalid_free(void *addr);
+
 void *find_first_bad_addr(void *addr, size_t size);
 const char *get_bug_type(struct kasan_access_info *info);
 
mm/kasan/tags.c +12 -0
index 4d5a1fe8251f..feb42c1763b8 100644
@@ -126,6 +126,18 @@ bool check_memory_region(unsigned long addr, size_t size, bool write,
 	return true;
 }
 
+bool check_invalid_free(void *addr)
+{
+	u8 tag = get_tag(addr);
+	u8 shadow_byte = READ_ONCE(*(u8 *)kasan_mem_to_shadow(reset_tag(addr)));
+
+	if (shadow_byte == KASAN_TAG_INVALID)
+		return true;
+	if (tag != KASAN_TAG_KERNEL && tag != shadow_byte)
+		return true;
+	return false;
+}
+
 #define DEFINE_HWASAN_LOAD_STORE(size)					\
 	void __hwasan_load##size##_noabort(unsigned long addr)		\
 	{								\
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#15 lore raw

PATCH v2 11/37 kasan: decode stack frame only with KASAN_STACK_ENABLE

In reply to Andrey Konovalov

Decoding routines aren't needed when CONFIG_KASAN_STACK_ENABLE is not enabled. Currently only generic KASAN mode implements stack error reporting.

No functional changes for software modes.

---
Change-Id: I084e3214f2b40dc0bef7c5a9fafdc6f5c42b06a2

---
 mm/kasan/kasan.h          |   6 ++
 mm/kasan/report.c         | 162 --------------------------------------
 mm/kasan/report_generic.c | 161 +++++++++++++++++++++++++++++++++++++
 3 files changed, 167 insertions(+), 162 deletions(-)
mm/kasan/kasan.h +6 -0
index cf6a135860f2..15cf3e0018ae 100644
@@ -168,6 +168,12 @@ bool check_invalid_free(void *addr);
 void *find_first_bad_addr(void *addr, size_t size);
 const char *get_bug_type(struct kasan_access_info *info);
 
+#ifdef CONFIG_KASAN_STACK_ENABLE
+void print_address_stack_frame(const void *addr);
+#else
+static inline void print_address_stack_frame(const void *addr) { }
+#endif
+
 bool kasan_report(unsigned long addr, size_t size,
 		bool is_write, unsigned long ip);
 void kasan_report_invalid_free(void *object, unsigned long ip);
mm/kasan/report.c +0 -162

168 lines not on this page · raw message

mm/kasan/report_generic.c +161 -0

167 lines not on this page · raw message

Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#16 lore raw

PATCH v2 12/37 kasan, arm64: only init shadow for software modes

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't be using shadow memory. Only initialize it when one of the software KASAN modes are enabled.

No functional changes for software modes.

---
Change-Id: I055e0651369b14d3e54cdaa8c48e6329b2e8952d

---
 arch/arm64/include/asm/kasan.h |  8 ++++++--
 arch/arm64/mm/kasan_init.c     | 15 ++++++++++++++-
 2 files changed, 20 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kasan.h +6 -2
index b0dc4abc3589..f7ea70d02cab 100644
@@ -13,6 +13,12 @@
 #define arch_kasan_get_tag(addr)	__tag_get(addr)
 
 #ifdef CONFIG_KASAN
+void kasan_init(void);
+#else
+static inline void kasan_init(void) { }
+#endif
+
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 
 /*
  * KASAN_SHADOW_START: beginning of the kernel virtual addresses.
@@ -33,12 +39,10 @@
 #define _KASAN_SHADOW_START(va)	(KASAN_SHADOW_END - (1UL << ((va) - KASAN_SHADOW_SCALE_SHIFT)))
 #define KASAN_SHADOW_START      _KASAN_SHADOW_START(vabits_actual)
 
-void kasan_init(void);
 void kasan_copy_shadow(pgd_t *pgdir);
 asmlinkage void kasan_early_init(void);
 
 #else
-static inline void kasan_init(void) { }
 static inline void kasan_copy_shadow(pgd_t *pgdir) { }
 #endif
 
arch/arm64/mm/kasan_init.c +14 -1
index 7291b26ce788..4d35eaf3ec97 100644
@@ -21,6 +21,8 @@
 #include <asm/sections.h>
 #include <asm/tlbflush.h>
 
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
+
 static pgd_t tmp_pg_dir[PTRS_PER_PGD] __initdata __aligned(PGD_SIZE);
 
 /*
@@ -208,7 +210,7 @@ static void __init clear_pgds(unsigned long start,
 		set_pgd(pgd_offset_k(start), __pgd(0));
 }
 
-void __init kasan_init(void)
+static void __init kasan_init_shadow(void)
 {
 	u64 kimg_shadow_start, kimg_shadow_end;
 	u64 mod_shadow_start, mod_shadow_end;
@@ -269,6 +271,17 @@ void __init kasan_init(void)
 
 	memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
 	cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
+}
+
+#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS) */
+
+static inline void __init kasan_init_shadow(void) { }
+
+#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
+void __init kasan_init(void)
+{
+	kasan_init_shadow();
 
 	/* At this point kasan is fully initialized. Enable error messages */
 	init_task.kasan_depth = 0;
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Reviewed-by on v2 12/37
#17 lore raw

Re: [PATCH v2 12/37] kasan, arm64: only init shadow for software modes

Quoted 10 lines · Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't be using shadow memory. Only initialize it when one of the software KASAN modes are enabled.

No functional changes for software modes.

#18 lore raw

PATCH v2 13/37 kasan, arm64: only use kasan_depth for software modes

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't use kasan_depth. Only define and use it when one of the software KASAN modes are enabled.

No functional changes for software modes.

---
Change-Id: I6109ea96c8df41ef6d75ad71bf22c1c8fa234a9a

---
 arch/arm64/mm/kasan_init.c | 11 ++++++++---
 include/linux/kasan.h      | 14 ++++++++++----
 include/linux/sched.h      |  2 +-
 init/init_task.c           |  2 +-
 mm/kasan/common.c          |  2 ++
 mm/kasan/report.c          |  2 ++
 6 files changed, 24 insertions(+), 9 deletions(-)
arch/arm64/mm/kasan_init.c +8 -3
index 4d35eaf3ec97..b6b9d55bb72e 100644
@@ -273,17 +273,22 @@ static void __init kasan_init_shadow(void)
 	cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
 }
 
+void __init kasan_init_depth(void)
+{
+	init_task.kasan_depth = 0;
+}
+
 #else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS) */
 
 static inline void __init kasan_init_shadow(void) { }
 
+static inline void __init kasan_init_depth(void) { }
+
 #endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
 void __init kasan_init(void)
 {
 	kasan_init_shadow();
-
-	/* At this point kasan is fully initialized. Enable error messages */
-	init_task.kasan_depth = 0;
+	kasan_init_depth();
 	pr_info("KernelAddressSanitizer initialized\n");
 }
include/linux/kasan.h +10 -4
index 18617d5c4cd7..894f4d9163ee 100644
@@ -52,7 +52,7 @@ static inline void kasan_remove_zero_shadow(void *start,
 
 #endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 
 /* Enable reporting bugs after kasan_disable_current() */
 extern void kasan_enable_current(void);
@@ -60,6 +60,15 @@ extern void kasan_enable_current(void);
 /* Disable reporting bugs for current task */
 extern void kasan_disable_current(void);
 
+#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
+static inline void kasan_enable_current(void) {}
+static inline void kasan_disable_current(void) {}
+
+#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
+#ifdef CONFIG_KASAN
+
 void kasan_unpoison_memory(const void *address, size_t size);
 
 void kasan_unpoison_task_stack(struct task_struct *task);
@@ -110,9 +119,6 @@ static inline void kasan_unpoison_memory(const void *address, size_t size) {}
 
 static inline void kasan_unpoison_task_stack(struct task_struct *task) {}
 
-static inline void kasan_enable_current(void) {}
-static inline void kasan_disable_current(void) {}
-
 static inline void kasan_alloc_pages(struct page *page, unsigned int order) {}
 static inline void kasan_free_pages(struct page *page, unsigned int order) {}
 
include/linux/sched.h +1 -1
index afe01e232935..db38b7ecf46d 100644
@@ -1192,7 +1192,7 @@ struct task_struct {
 	u64				timer_slack_ns;
 	u64				default_timer_slack_ns;
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	unsigned int			kasan_depth;
 #endif
 
init/init_task.c +1 -1
index f6889fce64af..b93078f1708b 100644
@@ -173,7 +173,7 @@ struct task_struct init_task
 	.numa_group	= NULL,
 	.numa_faults	= NULL,
 #endif
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	.kasan_depth	= 1,
 #endif
 #ifdef CONFIG_KCSAN
mm/kasan/common.c +2 -0
index a2321d35390e..41c7f1105eaa 100644
@@ -51,6 +51,7 @@ void kasan_set_track(struct kasan_track *track, gfp_t flags)
 	track->stack = kasan_save_stack(flags);
 }
 
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 void kasan_enable_current(void)
 {
 	current->kasan_depth++;
@@ -60,6 +61,7 @@ void kasan_disable_current(void)
 {
 	current->kasan_depth--;
 }
+#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
 static void __kasan_unpoison_stack(struct task_struct *task, const void *sp)
 {
mm/kasan/report.c +2 -0
index ddaf9d14ca81..8463e35b489f 100644
@@ -295,8 +295,10 @@ static void print_shadow_for_address(const void *addr)
 
 static bool report_enabled(void)
 {
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	if (current->kasan_depth)
 		return false;
+#endif
 	if (test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags))
 		return true;
 	return !test_and_set_bit(KASAN_BIT_REPORTED, &kasan_flags);
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Reviewed-by on v2 13/37
#19 lore raw

Re: [PATCH v2 13/37] kasan, arm64: only use kasan_depth for software modes

Quoted 10 lines · Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't use kasan_depth. Only define and use it when one of the software KASAN modes are enabled.

No functional changes for software modes.

#20 lore raw

PATCH v2 14/37 kasan: rename addr_has_shadow to addr_has_metadata

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't be using shadow memory, but will reuse this function. Rename "shadow" to implementation-neutral "metadata".

No functional changes.

---
Change-Id: I03706fe34b38da7860c39aa0968e00001a7d1873

---
 mm/kasan/kasan.h          | 2 +-
 mm/kasan/report.c         | 6 +++---
 mm/kasan/report_generic.c | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)
mm/kasan/kasan.h +1 -1
index 15cf3e0018ae..38fa4c202e9a 100644
@@ -145,7 +145,7 @@ static inline const void *kasan_shadow_to_mem(const void *shadow_addr)
 		<< KASAN_SHADOW_SCALE_SHIFT);
 }
 
-static inline bool addr_has_shadow(const void *addr)
+static inline bool addr_has_metadata(const void *addr)
 {
 	return (addr >= kasan_shadow_to_mem((void *)KASAN_SHADOW_START));
 }
mm/kasan/report.c +3 -3
index 8463e35b489f..ada3cfb43764 100644
@@ -334,7 +334,7 @@ static void __kasan_report(unsigned long addr, size_t size, bool is_write,
 	untagged_addr = reset_tag(tagged_addr);
 
 	info.access_addr = tagged_addr;
-	if (addr_has_shadow(untagged_addr))
+	if (addr_has_metadata(untagged_addr))
 		info.first_bad_addr = find_first_bad_addr(tagged_addr, size);
 	else
 		info.first_bad_addr = untagged_addr;
@@ -345,11 +345,11 @@ static void __kasan_report(unsigned long addr, size_t size, bool is_write,
 	start_report(&flags);
 
 	print_error_description(&info);
-	if (addr_has_shadow(untagged_addr))
+	if (addr_has_metadata(untagged_addr))
 		print_tags(get_tag(tagged_addr), info.first_bad_addr);
 	pr_err("\n");
 
-	if (addr_has_shadow(untagged_addr)) {
+	if (addr_has_metadata(untagged_addr)) {
 		print_address_description(untagged_addr, get_tag(tagged_addr));
 		pr_err("\n");
 		print_shadow_for_address(info.first_bad_addr);
mm/kasan/report_generic.c +1 -1
index 427f4ac80cca..29d30fae9421 100644
@@ -122,7 +122,7 @@ const char *get_bug_type(struct kasan_access_info *info)
 	if (info->access_addr + info->access_size < info->access_addr)
 		return "out-of-bounds";
 
-	if (addr_has_shadow(info->access_addr))
+	if (addr_has_metadata(info->access_addr))
 		return get_shadow_bug_type(info);
 	return get_wild_bug_type(info);
 }
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#21 lore raw

PATCH v2 15/37 kasan: rename print_shadow_for_address to print_memory_metadata

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't be using shadow memory, but will reuse this function. Rename "shadow" to implementation-neutral "metadata".

No functional changes.

---
Change-Id: I18397dddbed6bc6d365ddcaf063a83948e1150a5

---
 mm/kasan/report.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
mm/kasan/report.c +3 -3
index ada3cfb43764..8ad1ced1607d 100644
@@ -255,7 +255,7 @@ static int shadow_pointer_offset(const void *row, const void *shadow)
 		(shadow - row) / SHADOW_BYTES_PER_BLOCK + 1;
 }
 
-static void print_shadow_for_address(const void *addr)
+static void print_memory_metadata(const void *addr)
 {
 	int i;
 	const void *shadow = kasan_mem_to_shadow(addr);
@@ -316,7 +316,7 @@ void kasan_report_invalid_free(void *object, unsigned long ip)
 	pr_err("\n");
 	print_address_description(object, tag);
 	pr_err("\n");
-	print_shadow_for_address(object);
+	print_memory_metadata(object);
 	end_report(&flags);
 }
 
@@ -352,7 +352,7 @@ static void __kasan_report(unsigned long addr, size_t size, bool is_write,
 	if (addr_has_metadata(untagged_addr)) {
 		print_address_description(untagged_addr, get_tag(tagged_addr));
 		pr_err("\n");
-		print_shadow_for_address(info.first_bad_addr);
+		print_memory_metadata(info.first_bad_addr);
 	} else {
 		dump_stack();
 	}
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#22 lore raw

PATCH v2 16/37 kasan: kasan_non_canonical_hook only for software modes

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

kasan_non_canonical_hook() is only applicable to KASAN modes that use shadow memory, and won't be needed for hardware tag-based KASAN.

No functional changes for software modes.

---
Change-Id: Icc9f5ef100a2e86f3a4214a0c3131a68266181b2

---
 mm/kasan/report.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
mm/kasan/report.c +2 -1
index 8ad1ced1607d..2cce7c9beea3 100644
@@ -376,7 +376,8 @@ bool kasan_report(unsigned long addr, size_t size, bool is_write,
 	return ret;
 }
 
-#ifdef CONFIG_KASAN_INLINE
+#if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \
+	defined(CONFIG_KASAN_INLINE)
 /*
  * With CONFIG_KASAN_INLINE, accesses to bogus pointers (outside the high
  * canonical half of the address space) cause out-of-bounds shadow memory reads
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#23 lore raw

PATCH v2 17/37 kasan: rename SHADOW layout macros to META

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN won't be using shadow memory, but will reuse these macros. Rename "SHADOW" to implementation-neutral "META".

No functional changes.

---
Change-Id: Id2d836bf43b401bce1221cc06e745185f17b1cc

---
 mm/kasan/report.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)
mm/kasan/report.c +15 -15
index 2cce7c9beea3..6306673e7062 100644
@@ -36,11 +36,11 @@
 #include "kasan.h"
 #include "../slab.h"
 
-/* Shadow layout customization. */
-#define SHADOW_BYTES_PER_BLOCK 1
-#define SHADOW_BLOCKS_PER_ROW 16
-#define SHADOW_BYTES_PER_ROW (SHADOW_BLOCKS_PER_ROW * SHADOW_BYTES_PER_BLOCK)
-#define SHADOW_ROWS_AROUND_ADDR 2
+/* Metadata layout customization. */
+#define META_BYTES_PER_BLOCK 1
+#define META_BLOCKS_PER_ROW 16
+#define META_BYTES_PER_ROW (META_BLOCKS_PER_ROW * META_BYTES_PER_BLOCK)
+#define META_ROWS_AROUND_ADDR 2
 
 static unsigned long kasan_flags;
 
@@ -243,7 +243,7 @@ static void print_address_description(void *addr, u8 tag)
 
 static bool row_is_guilty(const void *row, const void *guilty)
 {
-	return (row <= guilty) && (guilty < row + SHADOW_BYTES_PER_ROW);
+	return (row <= guilty) && (guilty < row + META_BYTES_PER_ROW);
 }
 
 static int shadow_pointer_offset(const void *row, const void *shadow)
@@ -252,7 +252,7 @@ static int shadow_pointer_offset(const void *row, const void *shadow)
 	 *    3 + (BITS_PER_LONG/8)*2 chars.
 	 */
 	return 3 + (BITS_PER_LONG/8)*2 + (shadow - row)*2 +
-		(shadow - row) / SHADOW_BYTES_PER_BLOCK + 1;
+		(shadow - row) / META_BYTES_PER_BLOCK + 1;
 }
 
 static void print_memory_metadata(const void *addr)
@@ -262,15 +262,15 @@ static void print_memory_metadata(const void *addr)
 	const void *shadow_row;
 
 	shadow_row = (void *)round_down((unsigned long)shadow,
-					SHADOW_BYTES_PER_ROW)
-		- SHADOW_ROWS_AROUND_ADDR * SHADOW_BYTES_PER_ROW;
+					META_BYTES_PER_ROW)
+		- META_ROWS_AROUND_ADDR * META_BYTES_PER_ROW;
 
 	pr_err("Memory state around the buggy address:\n");
 
-	for (i = -SHADOW_ROWS_AROUND_ADDR; i <= SHADOW_ROWS_AROUND_ADDR; i++) {
+	for (i = -META_ROWS_AROUND_ADDR; i <= META_ROWS_AROUND_ADDR; i++) {
 		const void *kaddr = kasan_shadow_to_mem(shadow_row);
 		char buffer[4 + (BITS_PER_LONG/8)*2];
-		char shadow_buf[SHADOW_BYTES_PER_ROW];
+		char shadow_buf[META_BYTES_PER_ROW];
 
 		snprintf(buffer, sizeof(buffer),
 			(i == 0) ? ">%px: " : " %px: ", kaddr);
@@ -279,17 +279,17 @@ static void print_memory_metadata(const void *addr)
 		 * function, because generic functions may try to
 		 * access kasan mapping for the passed address.
 		 */
-		memcpy(shadow_buf, shadow_row, SHADOW_BYTES_PER_ROW);
+		memcpy(shadow_buf, shadow_row, META_BYTES_PER_ROW);
 		print_hex_dump(KERN_ERR, buffer,
-			DUMP_PREFIX_NONE, SHADOW_BYTES_PER_ROW, 1,
-			shadow_buf, SHADOW_BYTES_PER_ROW, 0);
+			DUMP_PREFIX_NONE, META_BYTES_PER_ROW, 1,
+			shadow_buf, META_BYTES_PER_ROW, 0);
 
 		if (row_is_guilty(shadow_row, shadow))
 			pr_err("%*c\n",
 				shadow_pointer_offset(shadow_row, shadow),
 				'^');
 
-		shadow_row += SHADOW_BYTES_PER_ROW;
+		shadow_row += META_BYTES_PER_ROW;
 	}
 }
 
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#24 lore raw

PATCH v2 18/37 kasan: separate metadata_fetch_row for each mode

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Rework print_memory_metadata() to make it agnostic with regard to the way metadata is stored. Allow providing a separate metadata_fetch_row() implementation for each KASAN mode. Hardware tag-based KASAN will provide its own implementation that doesn't use shadow memory.

No functional changes for software modes.

---
Change-Id: I5b0ed1d079ea776e620beca6a529a861e7dced95

---
 mm/kasan/kasan.h          |  8 ++++++
 mm/kasan/report.c         | 56 +++++++++++++++++++--------------------
 mm/kasan/report_generic.c |  5 ++++
 mm/kasan/report_tags.c    |  5 ++++
 4 files changed, 45 insertions(+), 29 deletions(-)
mm/kasan/kasan.h +8 -0
index 38fa4c202e9a..1d3c7c6ce771 100644
@@ -56,6 +56,13 @@
 #define KASAN_ABI_VERSION 1
 #endif
 
+/* Metadata layout customization. */
+#define META_BYTES_PER_BLOCK 1
+#define META_BLOCKS_PER_ROW 16
+#define META_BYTES_PER_ROW (META_BLOCKS_PER_ROW * META_BYTES_PER_BLOCK)
+#define META_MEM_BYTES_PER_ROW (META_BYTES_PER_ROW * KASAN_GRANULE_SIZE)
+#define META_ROWS_AROUND_ADDR 2
+
 struct kasan_access_info {
 	const void *access_addr;
 	const void *first_bad_addr;
@@ -167,6 +174,7 @@ bool check_invalid_free(void *addr);
 
 void *find_first_bad_addr(void *addr, size_t size);
 const char *get_bug_type(struct kasan_access_info *info);
+void metadata_fetch_row(char *buffer, void *row);
 
 #ifdef CONFIG_KASAN_STACK_ENABLE
 void print_address_stack_frame(const void *addr);
mm/kasan/report.c +27 -29

94 lines not on this page · raw message

mm/kasan/report_generic.c +5 -0
index 29d30fae9421..6524651b5d2e 100644
@@ -127,6 +127,11 @@ const char *get_bug_type(struct kasan_access_info *info)
 	return get_wild_bug_type(info);
 }
 
+void metadata_fetch_row(char *buffer, void *row)
+{
+	memcpy(buffer, kasan_mem_to_shadow(row), META_BYTES_PER_ROW);
+}
+
 #ifdef CONFIG_KASAN_STACK_ENABLE
 static bool __must_check tokenize_frame_descr(const char **frame_descr,
 					      char *token, size_t max_tok_len,
mm/kasan/report_tags.c +5 -0
index 6ddb55676a7c..4060d0503462 100644
@@ -85,6 +85,11 @@ void *find_first_bad_addr(void *addr, size_t size)
 	return p;
 }
 
+void metadata_fetch_row(char *buffer, void *row)
+{
+	memcpy(buffer, kasan_mem_to_shadow(row), META_BYTES_PER_ROW);
+}
+
 void print_tags(u8 addr_tag, const void *addr)
 {
 	u8 *shadow = (u8 *)kasan_mem_to_shadow(addr);
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#25 lore raw

PATCH v2 19/37 kasan: don't allow SW_TAGS with ARM64_MTE

In reply to Andrey Konovalov

Software tag-based KASAN provides its own tag checking machinery that can conflict with MTE. Don't allow enabling software tag-based KASAN when MTE is enabled.

---
Change-Id: Icd29bd0c6b1d3d7a0ee3d50c20490f404d34fc97

---
 arch/arm64/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/Kconfig +1 -1
index e7450fbd0aa7..e875db8e1c86 100644
@@ -131,7 +131,7 @@ config ARM64
 	select HAVE_ARCH_JUMP_LABEL
 	select HAVE_ARCH_JUMP_LABEL_RELATIVE
 	select HAVE_ARCH_KASAN if !(ARM64_16K_PAGES && ARM64_VA_BITS_48)
-	select HAVE_ARCH_KASAN_SW_TAGS if HAVE_ARCH_KASAN
+	select HAVE_ARCH_KASAN_SW_TAGS if (HAVE_ARCH_KASAN && !ARM64_MTE)
 	select HAVE_ARCH_KGDB
 	select HAVE_ARCH_MMAP_RND_BITS
 	select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#27 lore raw

PATCH v2 20/37 kasan: rename tags.c to tags_sw.c

In reply to Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN will also be using tag-based approach, so rename tags.c to tags_sw.c and report_tags.c to report_tags_sw.c to avoid confusion once the new mode is added

---
Change-Id: I922ab246c5903e8ce3dd3766b923aaf250599850

---
 mm/kasan/Makefile                            | 10 +++++-----
 mm/kasan/{report_tags.c => report_tags_sw.c} |  0
 mm/kasan/{tags.c => tags_sw.c}               |  0
 3 files changed, 5 insertions(+), 5 deletions(-)
 rename mm/kasan/{report_tags.c => report_tags_sw.c} (100%)
 rename mm/kasan/{tags.c => tags_sw.c} (100%)
mm/kasan/Makefile +5 -5
index b2596512421a..0789f9023884 100644
@@ -10,9 +10,9 @@ CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_quarantine.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report_generic.o = $(CC_FLAGS_FTRACE)
-CFLAGS_REMOVE_report_tags.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_report_tags_sw.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_shadow.o = $(CC_FLAGS_FTRACE)
-CFLAGS_REMOVE_tags.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_tags_sw.o = $(CC_FLAGS_FTRACE)
 
 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
 # see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533
@@ -27,10 +27,10 @@ CFLAGS_init.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_quarantine.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
-CFLAGS_report_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report_tags_sw.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_shadow.o := $(CC_FLAGS_KASAN_RUNTIME)
-CFLAGS_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_tags_sw.o := $(CC_FLAGS_KASAN_RUNTIME)
 
 obj-$(CONFIG_KASAN) := common.o report.o
 obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o report_generic.o shadow.o quarantine.o
-obj-$(CONFIG_KASAN_SW_TAGS) += init.o report_tags.o shadow.o tags.o
+obj-$(CONFIG_KASAN_SW_TAGS) += init.o report_tags_sw.o shadow.o tags_sw.o
mm/kasan/report_tags_sw.c renamed from mm/kasan/report_tags.c +0 -0
similarity index 100%
rename from mm/kasan/report_tags.c
rename to mm/kasan/report_tags_sw.c
mm/kasan/tags_sw.c renamed from mm/kasan/tags.c +0 -0
similarity index 100%
rename from mm/kasan/tags.c
rename to mm/kasan/tags_sw.c
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#28 lore raw

Re: [PATCH v2 20/37] kasan: rename tags.c to tags_sw.c

Quoted 7 lines · Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Hardware tag-based KASAN will also be using tag-based approach, so rename tags.c to tags_sw.c and report_tags.c to report_tags_sw.c to avoid confusion once the new mode is added

I find it a bit excessive renaming the same file twice in the same patch series (tags_report.c -> report_tags.c -> report_tags_sw.c)

#29 lore raw

Re: [PATCH v2 20/37] kasan: rename tags.c to tags_sw.c

Quoted 6 lines · Alexander Potapenko
Quoted 4 lines · Andrey Konovalov

This is a preparatory commit for the upcoming addition of a new hardware tag-based (MTE-based) KASAN mode.

Quoted 6 lines
Quoted 3 lines

Hardware tag-based KASAN will also be using tag-based approach, so rename tags.c to tags_sw.c and report_tags.c to report_tags_sw.c to avoid confusion once the new mode is added

I find it a bit excessive renaming the same file twice in the same patch series (tags_report.c -> report_tags.c -> report_tags_sw.c)

Also, as we are going to have CONFIG_KASAN_{SW,HW}_TAGS, won't it be better to call the files {report_,}tags_{sw,hw}.c ?

Signature · 10 lines
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
#30 lore raw

Re: [PATCH v2 20/37] kasan: rename tags.c to tags_sw.c

Quoted 2 lines

Also, as we are going to have CONFIG_KASAN_{SW,HW}_TAGS, won't it be better to call the files {report_,}tags_{sw,hw}.c ?

Sorry for the typo, I meant "{report_,}{sw,hw}_tags.c, mirroring the config names.

#31 lore raw

Re: [PATCH v2 20/37] kasan: rename tags.c to tags_sw.c

Quoted 6 lines · Alexander Potapenko
Quoted 3 lines

Also, as we are going to have CONFIG_KASAN_{SW,HW}_TAGS, won't it be better to call the files {report_,}tags_{sw,hw}.c ?

Sorry for the typo, I meant "{report_,}{sw,hw}_tags.c, mirroring the config names.

The idea here was to have common prefixes for similar parts, therefore I put "tags" first, so "tags_sw.c" comes next to "tags_hw.c" when one is running "ls". But I can rename them if you think it makes sense.

#32 lore raw

PATCH v2 21/37 kasan: introduce CONFIG_KASAN_HW_TAGS

In reply to Andrey Konovalov

This patch adds a configuration option for a new KASAN mode called hardware tag-based KASAN. This mode uses the memory tagging approach like the software tag-based mode, but relies on arm64 Memory Tagging Extension feature for tag management and access checking.

---
Change-Id: I246c2def9fffa6563278db1bddfbe742ca7bdefe

---
 lib/Kconfig.kasan | 56 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 17 deletions(-)
lib/Kconfig.kasan +39 -17
index b4cf6c519d71..17c9ecfaecb9 100644
@@ -6,7 +6,10 @@ config HAVE_ARCH_KASAN
 config HAVE_ARCH_KASAN_SW_TAGS
 	bool
 
-config	HAVE_ARCH_KASAN_VMALLOC
+config HAVE_ARCH_KASAN_HW_TAGS
+	bool
+
+config HAVE_ARCH_KASAN_VMALLOC
 	bool
 
 config CC_HAS_KASAN_GENERIC
@@ -20,10 +23,11 @@ config CC_HAS_WORKING_NOSANITIZE_ADDRESS
 
 menuconfig KASAN
 	bool "KASAN: runtime memory debugger"
-	depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
-		   (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)
+	depends on (((HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
+		     (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)) && \
+		    CC_HAS_WORKING_NOSANITIZE_ADDRESS) || \
+		   HAVE_ARCH_KASAN_HW_TAGS
 	depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
-	depends on CC_HAS_WORKING_NOSANITIZE_ADDRESS
 	select SLUB_DEBUG if SLUB
 	select CONSTRUCTORS
 	select STACKDEPOT
@@ -38,13 +42,18 @@ choice
 	prompt "KASAN mode"
 	default KASAN_GENERIC
 	help
-	  KASAN has two modes: generic KASAN (similar to userspace ASan,
-	  x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC) and
-	  software tag-based KASAN (a version based on software memory
-	  tagging, arm64 only, similar to userspace HWASan, enabled with
-	  CONFIG_KASAN_SW_TAGS).
+	  KASAN has three modes:
+	  1. generic KASAN (similar to userspace ASan,
+	     x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC),
+	  2. software tag-based KASAN (arm64 only, based on software
+	     memory tagging (similar to userspace HWASan), enabled with
+	     CONFIG_KASAN_SW_TAGS), and
+	  3. hardware tag-based KASAN (arm64 only, based on hardware
+	     memory tagging, enabled with CONFIG_KASAN_HW_TAGS).
 
-	  Both generic and tag-based KASAN are strictly debugging features.
+	  All KASAN modes are strictly debugging features.
+
+	  For better error detection enable CONFIG_STACKTRACE.
 
 config KASAN_GENERIC
 	bool "Generic mode"
@@ -61,8 +70,6 @@ config KASAN_GENERIC
 	  and introduces an overhead of ~x1.5 for the rest of the allocations.
 	  The performance slowdown is ~x3.
 
-	  For better error detection enable CONFIG_STACKTRACE.
-
 	  Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB
 	  (the resulting kernel does not boot).
 
@@ -72,9 +79,11 @@ config KASAN_SW_TAGS
 	help
 	  Enables software tag-based KASAN mode.
 
-	  This mode requires Top Byte Ignore support by the CPU and therefore
-	  is only supported for arm64. This mode requires Clang version 7.0.0
-	  or later.
+	  This mode require software memory tagging support in the form of
+	  HWASan-like compiler instrumentation.
+
+	  Currently this mode is only implemented for arm64 CPUs and relies on
+	  Top Byte Ignore. This mode requires Clang version 7.0.0 or later.
 
 	  This mode consumes about 1/16th of available memory at kernel start
 	  and introduces an overhead of ~20% for the rest of the allocations.
@@ -82,15 +91,27 @@ config KASAN_SW_TAGS
 	  casting and comparison, as it embeds tags into the top byte of each
 	  pointer.
 
-	  For better error detection enable CONFIG_STACKTRACE.
-
 	  Currently CONFIG_KASAN_SW_TAGS doesn't work with CONFIG_DEBUG_SLAB
 	  (the resulting kernel does not boot).
 
+config KASAN_HW_TAGS
+	bool "Hardware tag-based mode"
+	depends on HAVE_ARCH_KASAN_HW_TAGS
+	depends on SLUB
+	help
+	  Enables hardware tag-based KASAN mode.
+
+	  This mode requires hardware memory tagging support, and can be used
+	  by any architecture that provides it.
+
+	  Currently this mode is only implemented for arm64 CPUs starting from
+	  ARMv8.5 and relies on Memory Tagging Extension and Top Byte Ignore.
+
 endchoice
 
 choice
 	prompt "Instrumentation type"
+	depends on KASAN_GENERIC || KASAN_SW_TAGS
 	default KASAN_OUTLINE
 
 config KASAN_OUTLINE
@@ -114,6 +135,7 @@ endchoice
 
 config KASAN_STACK_ENABLE
 	bool "Enable stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST
+	depends on KASAN_GENERIC || KASAN_SW_TAGS
 	help
 	  The LLVM stack address sanitizer has a know problem that
 	  causes excessive stack usage in a lot of functions, see
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#33 lore raw

Re: [PATCH v2 21/37] kasan: introduce CONFIG_KASAN_HW_TAGS

Quoted 32 lines · Andrey Konovalov

This patch adds a configuration option for a new KASAN mode called hardware tag-based KASAN. This mode uses the memory tagging approach like the software tag-based mode, but relies on arm64 Memory Tagging Extension feature for tag management and access checking.

---
Change-Id: I246c2def9fffa6563278db1bddfbe742ca7bdefe

---
 lib/Kconfig.kasan | 56 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 17 deletions(-)
lib/Kconfig.kasan +4 -1
index b4cf6c519d71..17c9ecfaecb9 100644
@@ -6,7 +6,10 @@ config HAVE_ARCH_KASAN
 config HAVE_ARCH_KASAN_SW_TAGS
 	bool
 
-config	HAVE_ARCH_KASAN_VMALLOC
+config HAVE_ARCH_KASAN_HW_TAGS
+	bool
+
+config HAVE_ARCH_KASAN_VMALLOC
 	bool
 
 config CC_HAS_KASAN_GENERIC
@@ -20,10 +23,11 @@ config CC_HAS_WORKING_NOSANITIZE_ADDRESS
 
 menuconfig KASAN

lib/Kconfig.kasan config CC_HAS_WORKING_NOSANITIZE_ADDRESS · line 31

Quoted 10 lines
lib/Kconfig.kasan +4 -3
 	bool "KASAN: runtime memory debugger"
-	depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
-		   (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)
+	depends on (((HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
+		     (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)) && \
+		    CC_HAS_WORKING_NOSANITIZE_ADDRESS) || \
+		   HAVE_ARCH_KASAN_HW_TAGS
 	depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
-	depends on CC_HAS_WORKING_NOSANITIZE_ADDRESS
 	select SLUB_DEBUG if SLUB

Is SLUB_DEBUG necessary with HW_TAGS?

Quoted 14 lines
lib/Kconfig.kasan +3 -5
 	select CONSTRUCTORS
 	select STACKDEPOT
@@ -38,13 +42,18 @@ choice
 	prompt "KASAN mode"
 	default KASAN_GENERIC
 	help
-	  KASAN has two modes: generic KASAN (similar to userspace ASan,
-	  x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC) and
-	  software tag-based KASAN (a version based on software memory
-	  tagging, arm64 only, similar to userspace HWASan, enabled with
-	  CONFIG_KASAN_SW_TAGS).
+	  KASAN has three modes:
+	  1. generic KASAN (similar to userspace ASan,
+	     x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC),

lib/Kconfig.kasan choice · line 56

Quoted 10 lines
lib/Kconfig.kasan +8 -1
+	  2. software tag-based KASAN (arm64 only, based on software
+	     memory tagging (similar to userspace HWASan), enabled with
+	     CONFIG_KASAN_SW_TAGS), and
+	  3. hardware tag-based KASAN (arm64 only, based on hardware
+	     memory tagging, enabled with CONFIG_KASAN_HW_TAGS).
 
-	  Both generic and tag-based KASAN are strictly debugging features.
+	  All KASAN modes are strictly debugging features.
+
+	  For better error detection enable CONFIG_STACKTRACE.

I don't think CONFIG_STACKTRACE improves error detection, right? It only makes the reports more readable

Quoted 58 lines
lib/Kconfig.kasan +19 -7
 config KASAN_GENERIC
 	bool "Generic mode"
@@ -61,8 +70,6 @@ config KASAN_GENERIC
 	  and introduces an overhead of ~x1.5 for the rest of the allocations.
 	  The performance slowdown is ~x3.
 
-	  For better error detection enable CONFIG_STACKTRACE.
-
 	  Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB
 	  (the resulting kernel does not boot).
 
@@ -72,9 +79,11 @@ config KASAN_SW_TAGS
 	help
 	  Enables software tag-based KASAN mode.
 
-	  This mode requires Top Byte Ignore support by the CPU and therefore
-	  is only supported for arm64. This mode requires Clang version 7.0.0
-	  or later.
+	  This mode require software memory tagging support in the form of
+	  HWASan-like compiler instrumentation.
+
+	  Currently this mode is only implemented for arm64 CPUs and relies on
+	  Top Byte Ignore. This mode requires Clang version 7.0.0 or later.
 
 	  This mode consumes about 1/16th of available memory at kernel start
 	  and introduces an overhead of ~20% for the rest of the allocations.
@@ -82,15 +91,27 @@ config KASAN_SW_TAGS
 	  casting and comparison, as it embeds tags into the top byte of each
 	  pointer.
 
-	  For better error detection enable CONFIG_STACKTRACE.
-
 	  Currently CONFIG_KASAN_SW_TAGS doesn't work with CONFIG_DEBUG_SLAB
 	  (the resulting kernel does not boot).
 
+config KASAN_HW_TAGS
+	bool "Hardware tag-based mode"
+	depends on HAVE_ARCH_KASAN_HW_TAGS
+	depends on SLUB
+	help
+	  Enables hardware tag-based KASAN mode.
+
+	  This mode requires hardware memory tagging support, and can be used
+	  by any architecture that provides it.
+
+	  Currently this mode is only implemented for arm64 CPUs starting from
+	  ARMv8.5 and relies on Memory Tagging Extension and Top Byte Ignore.
+
 endchoice
 
 choice
 	prompt "Instrumentation type"
+	depends on KASAN_GENERIC || KASAN_SW_TAGS
 	default KASAN_OUTLINE
 
 config KASAN_OUTLINE

lib/Kconfig.kasan endchoice · line 141

Quoted 7 lines
lib/Kconfig.kasan +1 -0
@@ -114,6 +135,7 @@ endchoice
 
 config KASAN_STACK_ENABLE
 	bool "Enable stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST
+	depends on KASAN_GENERIC || KASAN_SW_TAGS
 	help
 	  The LLVM stack address sanitizer has a know problem that
 	  causes excessive stack usage in a lot of functions, see

How about something like the below change (introduce KASAN_INSTRUMENTED Kconfig var) to avoid the repeated "KASAN_GENERIC || KASAN_SW_TAGS". This could then also be used in the various .c/.h files (and make some of the code more readable hopefully).

+config KASAN_INSTRUMENTED
+ def_bool KASAN_GENERIC || KASAN_SW_TAGS
+
choice
prompt "Instrumentation type"
- depends on KASAN_GENERIC || KASAN_SW_TAGS
+ depends on KASAN_INSTRUMENTED
default KASAN_OUTLINE

config KASAN_OUTLINE
@@ -135,7 +141,7 @@ endchoice

config KASAN_STACK_ENABLE
bool "Enable stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST
- depends on KASAN_GENERIC || KASAN_SW_TAGS
+ depends on KASAN_INSTRUMENTED
help
The LLVM stack address sanitizer has a know problem that causes excessive stack usage in a lot of functions, see

Thanks,
-- Marco

#34 lore raw

Re: [PATCH v2 21/37] kasan: introduce CONFIG_KASAN_HW_TAGS

Quoted 33 lines · Marco Elver
Quoted 31 lines · Andrey Konovalov

This patch adds a configuration option for a new KASAN mode called hardware tag-based KASAN. This mode uses the memory tagging approach like the software tag-based mode, but relies on arm64 Memory Tagging Extension feature for tag management and access checking.

---
Change-Id: I246c2def9fffa6563278db1bddfbe742ca7bdefe

---
 lib/Kconfig.kasan | 56 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 17 deletions(-)
lib/Kconfig.kasan +4 -1
index b4cf6c519d71..17c9ecfaecb9 100644
@@ -6,7 +6,10 @@ config HAVE_ARCH_KASAN
 config HAVE_ARCH_KASAN_SW_TAGS
      bool
 
-config       HAVE_ARCH_KASAN_VMALLOC
+config HAVE_ARCH_KASAN_HW_TAGS
+     bool
+
+config HAVE_ARCH_KASAN_VMALLOC
      bool
 
 config CC_HAS_KASAN_GENERIC
@@ -20,10 +23,11 @@ config CC_HAS_WORKING_NOSANITIZE_ADDRESS
 
Quoted 13 lines

lib/Kconfig.kasan config CC_HAS_WORKING_NOSANITIZE_ADDRESS · line 31

Quoted 11 lines
lib/Kconfig.kasan +4 -3
 menuconfig KASAN
      bool "KASAN: runtime memory debugger"
-     depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
-                (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)
+     depends on (((HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
+                  (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)) && \
+                 CC_HAS_WORKING_NOSANITIZE_ADDRESS) || \
+                HAVE_ARCH_KASAN_HW_TAGS
      depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
-     depends on CC_HAS_WORKING_NOSANITIZE_ADDRESS
      select SLUB_DEBUG if SLUB

Is SLUB_DEBUG necessary with HW_TAGS?

I'll check and drop it if it's unnecessary.

Quoted 20 lines
Quoted 20 lines
lib/Kconfig.kasan +8 -5
      select CONSTRUCTORS
      select STACKDEPOT
@@ -38,13 +42,18 @@ choice
      prompt "KASAN mode"
      default KASAN_GENERIC
      help
-       KASAN has two modes: generic KASAN (similar to userspace ASan,
-       x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC) and
-       software tag-based KASAN (a version based on software memory
-       tagging, arm64 only, similar to userspace HWASan, enabled with
-       CONFIG_KASAN_SW_TAGS).
+       KASAN has three modes:
+       1. generic KASAN (similar to userspace ASan,
+          x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC),
+       2. software tag-based KASAN (arm64 only, based on software
+          memory tagging (similar to userspace HWASan), enabled with
+          CONFIG_KASAN_SW_TAGS), and
+       3. hardware tag-based KASAN (arm64 only, based on hardware
+          memory tagging, enabled with CONFIG_KASAN_HW_TAGS).
 
Quoted 7 lines

lib/Kconfig.kasan choice · line 56

Quoted 4 lines
lib/Kconfig.kasan +3 -1
-       Both generic and tag-based KASAN are strictly debugging features.
+       All KASAN modes are strictly debugging features.
+
+       For better error detection enable CONFIG_STACKTRACE.

I don't think CONFIG_STACKTRACE improves error detection, right? It only makes the reports more readable

Yes, will fix.

Quoted 59 lines
Quoted 59 lines
lib/Kconfig.kasan +19 -7
 config KASAN_GENERIC
      bool "Generic mode"
@@ -61,8 +70,6 @@ config KASAN_GENERIC
        and introduces an overhead of ~x1.5 for the rest of the allocations.
        The performance slowdown is ~x3.
 
-       For better error detection enable CONFIG_STACKTRACE.
-
        Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB
        (the resulting kernel does not boot).
 
@@ -72,9 +79,11 @@ config KASAN_SW_TAGS
      help
        Enables software tag-based KASAN mode.
 
-       This mode requires Top Byte Ignore support by the CPU and therefore
-       is only supported for arm64. This mode requires Clang version 7.0.0
-       or later.
+       This mode require software memory tagging support in the form of
+       HWASan-like compiler instrumentation.
+
+       Currently this mode is only implemented for arm64 CPUs and relies on
+       Top Byte Ignore. This mode requires Clang version 7.0.0 or later.
 
        This mode consumes about 1/16th of available memory at kernel start
        and introduces an overhead of ~20% for the rest of the allocations.
@@ -82,15 +91,27 @@ config KASAN_SW_TAGS
        casting and comparison, as it embeds tags into the top byte of each
        pointer.
 
-       For better error detection enable CONFIG_STACKTRACE.
-
        Currently CONFIG_KASAN_SW_TAGS doesn't work with CONFIG_DEBUG_SLAB
        (the resulting kernel does not boot).
 
+config KASAN_HW_TAGS
+     bool "Hardware tag-based mode"
+     depends on HAVE_ARCH_KASAN_HW_TAGS
+     depends on SLUB
+     help
+       Enables hardware tag-based KASAN mode.
+
+       This mode requires hardware memory tagging support, and can be used
+       by any architecture that provides it.
+
+       Currently this mode is only implemented for arm64 CPUs starting from
+       ARMv8.5 and relies on Memory Tagging Extension and Top Byte Ignore.
+
 endchoice
 
 choice
      prompt "Instrumentation type"
+     depends on KASAN_GENERIC || KASAN_SW_TAGS
      default KASAN_OUTLINE
 
 config KASAN_OUTLINE
@@ -114,6 +135,7 @@ endchoice
 
Quoted 11 lines

lib/Kconfig.kasan endchoice · line 141

Quoted 6 lines
lib/Kconfig.kasan +1 -0
 config KASAN_STACK_ENABLE
      bool "Enable stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST
+     depends on KASAN_GENERIC || KASAN_SW_TAGS
      help
        The LLVM stack address sanitizer has a know problem that
        causes excessive stack usage in a lot of functions, see

How about something like the below change (introduce KASAN_INSTRUMENTED Kconfig var) to avoid the repeated "KASAN_GENERIC || KASAN_SW_TAGS". This could then also be used in the various .c/.h files (and make some of the code more readable hopefully).

I tried doing that initially, but it didn't really look good. The reason is that we actually have two properties that are currently common for the software modes, but aren't actually tied to each other:
instrumentation and shadow memory. Therefore we will end up with two new configs: KASAN_INSTRUMENTED and KASAN_USES_SHADOW (or something), and things get quite confusing. I think it's better to keep KASAN_GENERIC || KASAN_SW_TAGS everywhere.

#35 lore raw

Re: [PATCH v2 21/37] kasan: introduce CONFIG_KASAN_HW_TAGS

Quoted 152 lines · Andrey Konovalov' via kasan-dev
Quoted 33 lines · Marco Elver
Quoted 31 lines · Andrey Konovalov

This patch adds a configuration option for a new KASAN mode called hardware tag-based KASAN. This mode uses the memory tagging approach like the software tag-based mode, but relies on arm64 Memory Tagging Extension feature for tag management and access checking.

---
Change-Id: I246c2def9fffa6563278db1bddfbe742ca7bdefe

---
 lib/Kconfig.kasan | 56 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 17 deletions(-)
lib/Kconfig.kasan +4 -1
index b4cf6c519d71..17c9ecfaecb9 100644
@@ -6,7 +6,10 @@ config HAVE_ARCH_KASAN
 config HAVE_ARCH_KASAN_SW_TAGS
      bool
 
-config       HAVE_ARCH_KASAN_VMALLOC
+config HAVE_ARCH_KASAN_HW_TAGS
+     bool
+
+config HAVE_ARCH_KASAN_VMALLOC
      bool
 
 config CC_HAS_KASAN_GENERIC
@@ -20,10 +23,11 @@ config CC_HAS_WORKING_NOSANITIZE_ADDRESS
 
Quoted 13 lines

lib/Kconfig.kasan config CC_HAS_WORKING_NOSANITIZE_ADDRESS · line 31

Quoted 11 lines
lib/Kconfig.kasan +4 -3
 menuconfig KASAN
      bool "KASAN: runtime memory debugger"
-     depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
-                (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)
+     depends on (((HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \
+                  (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)) && \
+                 CC_HAS_WORKING_NOSANITIZE_ADDRESS) || \
+                HAVE_ARCH_KASAN_HW_TAGS
      depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB)
-     depends on CC_HAS_WORKING_NOSANITIZE_ADDRESS
      select SLUB_DEBUG if SLUB

Is SLUB_DEBUG necessary with HW_TAGS?

I'll check and drop it if it's unnecessary.

Quoted 20 lines
Quoted 20 lines
lib/Kconfig.kasan +8 -5
      select CONSTRUCTORS
      select STACKDEPOT
@@ -38,13 +42,18 @@ choice
      prompt "KASAN mode"
      default KASAN_GENERIC
      help
-       KASAN has two modes: generic KASAN (similar to userspace ASan,
-       x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC) and
-       software tag-based KASAN (a version based on software memory
-       tagging, arm64 only, similar to userspace HWASan, enabled with
-       CONFIG_KASAN_SW_TAGS).
+       KASAN has three modes:
+       1. generic KASAN (similar to userspace ASan,
+          x86_64/arm64/xtensa, enabled with CONFIG_KASAN_GENERIC),
+       2. software tag-based KASAN (arm64 only, based on software
+          memory tagging (similar to userspace HWASan), enabled with
+          CONFIG_KASAN_SW_TAGS), and
+       3. hardware tag-based KASAN (arm64 only, based on hardware
+          memory tagging, enabled with CONFIG_KASAN_HW_TAGS).
 
Quoted 7 lines

lib/Kconfig.kasan choice · line 56

Quoted 4 lines
lib/Kconfig.kasan +3 -1
-       Both generic and tag-based KASAN are strictly debugging features.
+       All KASAN modes are strictly debugging features.
+
+       For better error detection enable CONFIG_STACKTRACE.

I don't think CONFIG_STACKTRACE improves error detection, right? It only makes the reports more readable

Yes, will fix.

Quoted 59 lines
Quoted 59 lines
lib/Kconfig.kasan +19 -7
 config KASAN_GENERIC
      bool "Generic mode"
@@ -61,8 +70,6 @@ config KASAN_GENERIC
        and introduces an overhead of ~x1.5 for the rest of the allocations.
        The performance slowdown is ~x3.
 
-       For better error detection enable CONFIG_STACKTRACE.
-
        Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB
        (the resulting kernel does not boot).
 
@@ -72,9 +79,11 @@ config KASAN_SW_TAGS
      help
        Enables software tag-based KASAN mode.
 
-       This mode requires Top Byte Ignore support by the CPU and therefore
-       is only supported for arm64. This mode requires Clang version 7.0.0
-       or later.
+       This mode require software memory tagging support in the form of
+       HWASan-like compiler instrumentation.
+
+       Currently this mode is only implemented for arm64 CPUs and relies on
+       Top Byte Ignore. This mode requires Clang version 7.0.0 or later.
 
        This mode consumes about 1/16th of available memory at kernel start
        and introduces an overhead of ~20% for the rest of the allocations.
@@ -82,15 +91,27 @@ config KASAN_SW_TAGS
        casting and comparison, as it embeds tags into the top byte of each
        pointer.
 
-       For better error detection enable CONFIG_STACKTRACE.
-
        Currently CONFIG_KASAN_SW_TAGS doesn't work with CONFIG_DEBUG_SLAB
        (the resulting kernel does not boot).
 
+config KASAN_HW_TAGS
+     bool "Hardware tag-based mode"
+     depends on HAVE_ARCH_KASAN_HW_TAGS
+     depends on SLUB
+     help
+       Enables hardware tag-based KASAN mode.
+
+       This mode requires hardware memory tagging support, and can be used
+       by any architecture that provides it.
+
+       Currently this mode is only implemented for arm64 CPUs starting from
+       ARMv8.5 and relies on Memory Tagging Extension and Top Byte Ignore.
+
 endchoice
 
 choice
      prompt "Instrumentation type"
+     depends on KASAN_GENERIC || KASAN_SW_TAGS
      default KASAN_OUTLINE
 
 config KASAN_OUTLINE
@@ -114,6 +135,7 @@ endchoice
 
Quoted 11 lines

lib/Kconfig.kasan endchoice · line 141

Quoted 6 lines
lib/Kconfig.kasan +1 -0
 config KASAN_STACK_ENABLE
      bool "Enable stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST
+     depends on KASAN_GENERIC || KASAN_SW_TAGS
      help
        The LLVM stack address sanitizer has a know problem that
        causes excessive stack usage in a lot of functions, see

How about something like the below change (introduce KASAN_INSTRUMENTED Kconfig var) to avoid the repeated "KASAN_GENERIC || KASAN_SW_TAGS". This could then also be used in the various .c/.h files (and make some of the code more readable hopefully).

Quoted 7 lines

I tried doing that initially, but it didn't really look good. The reason is that we actually have two properties that are currently common for the software modes, but aren't actually tied to each other:
instrumentation and shadow memory. Therefore we will end up with two new configs: KASAN_INSTRUMENTED and KASAN_USES_SHADOW (or something), and things get quite confusing. I think it's better to keep KASAN_GENERIC || KASAN_SW_TAGS everywhere.

Ah, I see. So in some cases the reason the #ifdef exists is because of instrumentation, in other cases because there is some shadow memory (right?).

The only other option I see is to call it what it is ("KASAN_SW" or "KASAN_SOFTWARE"), but other than that, I don't mind if it stays as-is.

Thanks,
-- Marco

#36 lore raw

Re: [PATCH v2 21/37] kasan: introduce CONFIG_KASAN_HW_TAGS

Quoted 14 lines · Marco Elver
Quoted 6 lines
Quoted 5 lines

How about something like the below change (introduce KASAN_INSTRUMENTED Kconfig var) to avoid the repeated "KASAN_GENERIC || KASAN_SW_TAGS". This could then also be used in the various .c/.h files (and make some of the code more readable hopefully).

Quoted 7 lines

I tried doing that initially, but it didn't really look good. The reason is that we actually have two properties that are currently common for the software modes, but aren't actually tied to each other:
instrumentation and shadow memory. Therefore we will end up with two new configs: KASAN_INSTRUMENTED and KASAN_USES_SHADOW (or something), and things get quite confusing. I think it's better to keep KASAN_GENERIC || KASAN_SW_TAGS everywhere.

Quoted 3 lines

Ah, I see. So in some cases the reason the #ifdef exists is because of instrumentation, in other cases because there is some shadow memory (right?).

Correct.

Quoted 3 lines

The only other option I see is to call it what it is ("KASAN_SW" or "KASAN_SOFTWARE"), but other than that, I don't mind if it stays as-is.

Let's leave it as is then. I don't think the code will get much better in terms of readability if we add KASAN_SOFTWARE, but we'll get another "indirect" config option, which makes things a bit more confusing.

#37 lore raw

PATCH v2 22/37 arm64: mte: Add in-kernel MTE helpers

In reply to Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

Provide helper functions to manipulate allocation and pointer tags for kernel addresses.

Low-level helper functions (mte_assign_*, written in assembly) operate tag values from the [0x0, 0xF] range. High-level helper functions (mte_get/set_*) use the [0xF0, 0xFF] range to preserve compatibility with normal kernel pointers that have 0xFF in their top byte.

MTE_GRANULE_SIZE and related definitions are moved to mte-def.h header that doesn't have any dependencies and is safe to include into any low-level header.

---
Change-Id: I1b5230254f90dc21a913447cb17f07fea7944ece

---
 arch/arm64/include/asm/esr.h         |  1 +
 arch/arm64/include/asm/mte-helpers.h | 48 ++++++++++++++++++++++++++++
 arch/arm64/include/asm/mte.h         | 17 ++++++----
 arch/arm64/kernel/mte.c              | 48 ++++++++++++++++++++++++++++
 arch/arm64/lib/mte.S                 | 17 ++++++++++
 5 files changed, 125 insertions(+), 6 deletions(-)
 create mode 100644 arch/arm64/include/asm/mte-helpers.h
arch/arm64/include/asm/esr.h +1 -0
index 035003acfa87..bc0dc66a6a27 100644
@@ -103,6 +103,7 @@
 #define ESR_ELx_FSC		(0x3F)
 #define ESR_ELx_FSC_TYPE	(0x3C)
 #define ESR_ELx_FSC_EXTABT	(0x10)
+#define ESR_ELx_FSC_MTE		(0x11)
 #define ESR_ELx_FSC_SERROR	(0x11)
 #define ESR_ELx_FSC_ACCESS	(0x08)
 #define ESR_ELx_FSC_FAULT	(0x04)
arch/arm64/include/asm/mte-helpers.h new file +48 -0

48 lines not on this page · raw message

arch/arm64/include/asm/mte.h +11 -6
index 1c99fcadb58c..82cd7c89edec 100644
@@ -5,14 +5,13 @@
 #ifndef __ASM_MTE_H
 #define __ASM_MTE_H
 
-#define MTE_GRANULE_SIZE	UL(16)
-#define MTE_GRANULE_MASK	(~(MTE_GRANULE_SIZE - 1))
-#define MTE_TAG_SHIFT		56
-#define MTE_TAG_SIZE		4
+#include <asm/mte-helpers.h>
 
 #ifndef __ASSEMBLY__
 
+#include <linux/bitfield.h>
 #include <linux/page-flags.h>
+#include <linux/types.h>
 
 #include <asm/pgtable-types.h>
 
@@ -45,7 +44,9 @@ long get_mte_ctrl(struct task_struct *task);
 int mte_ptrace_copy_tags(struct task_struct *child, long request,
 			 unsigned long addr, unsigned long data);
 
-#else
+void mte_assign_mem_tag_range(void *addr, size_t size);
+
+#else /* CONFIG_ARM64_MTE */
 
 /* unused if !CONFIG_ARM64_MTE, silence the compiler */
 #define PG_mte_tagged	0
@@ -80,7 +81,11 @@ static inline int mte_ptrace_copy_tags(struct task_struct *child,
 	return -EIO;
 }
 
-#endif
+static inline void mte_assign_mem_tag_range(void *addr, size_t size)
+{
+}
+
+#endif /* CONFIG_ARM64_MTE */
 
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_MTE_H  */
arch/arm64/kernel/mte.c +48 -0

62 lines not on this page · raw message

arch/arm64/lib/mte.S +17 -0
index 03ca6d8b8670..cc2c3a378c00 100644
@@ -149,3 +149,20 @@ SYM_FUNC_START(mte_restore_page_tags)
 
 	ret
 SYM_FUNC_END(mte_restore_page_tags)
+
+/*
+ * Assign allocation tags for a region of memory based on the pointer tag
+ *   x0 - source pointer
+ *   x1 - size
+ *
+ * Note: size must be non-zero and MTE_GRANULE_SIZE aligned
+ */
+SYM_FUNC_START(mte_assign_mem_tag_range)
+	/* if (src == NULL) return; */
+	cbz	x0, 2f
+1:	stg	x0, [x0]
+	add	x0, x0, #MTE_GRANULE_SIZE
+	sub	x1, x1, #MTE_GRANULE_SIZE
+	cbnz	x1, 1b
+2:	ret
+SYM_FUNC_END(mte_assign_mem_tag_range)
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer
#38 lore raw

Re: [PATCH v2 22/37] arm64: mte: Add in-kernel MTE helpers

Quoted 6 lines · Andrey Konovalov
arch/arm64/include/asm/mte-helpers.h new file +0 -0
new file mode 100644
index 000000000000..5dc2d443851b

arch/arm64/include/asm/mte-helpers.h line 8

Quoted 8 lines
arch/arm64/include/asm/mte-helpers.h new file +8 -0
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 ARM Ltd.
+ */
+#ifndef __ASM_MTE_ASM_H
+#define __ASM_MTE_ASM_H
+
+#define __MTE_PREAMBLE		".arch armv8.5-a\n.arch_extension memtag\n"

Because of how the .arch overrides a previous .arch, we should follow the ARM64_ASM_PREAMBLE introduced in commit 1764c3edc668 ("arm64: use a common .arch preamble for inline assembly"). The above should be something like:

#define __MTE_PREAMBLE ARM64_ASM_PREAMBLE ".arch_extension memtag"

with the ARM64_ASM_PREAMBLE adjusted to armv8.5-a if available.

arch/arm64/include/asm/mte-helpers.h line 15

Quoted 6 lines
arch/arm64/include/asm/mte-helpers.h new file +6 -0
+#define MTE_GRANULE_SIZE	UL(16)
+#define MTE_GRANULE_MASK	(~(MTE_GRANULE_SIZE - 1))
+#define MTE_TAG_SHIFT		56
+#define MTE_TAG_SIZE		4
+#define MTE_TAG_MASK		GENMASK((MTE_TAG_SHIFT + (MTE_TAG_SIZE - 1)), MTE_TAG_SHIFT)
+#define MTE_TAG_MAX		(MTE_TAG_MASK >> MTE_TAG_SHIFT)

In v1 I suggested we keep those definitions in mte-def.h (or mte-hwdef.h) so that they can be included in cache.h. Anything else should go in mte.h, I don't see the point of two headers for various MTE function prototypes.

arch/arm64/include/asm/mte-helpers.h line 23

Quoted 8 lines
arch/arm64/include/asm/mte-helpers.h new file +8 -0
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+#ifdef CONFIG_ARM64_MTE
+
+#define mte_get_ptr_tag(ptr)	((u8)(((u64)(ptr)) >> MTE_TAG_SHIFT))

I wonder whether this could also be an inline function that takes a void *ptr.

Quoted 5 lines
arch/arm64/kernel/mte.c +0 -0
index 52a0638ed967..e238ffde2679 100644

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 83

Quoted 10 lines
arch/arm64/kernel/mte.c +7 -0
@@ -72,6 +74,52 @@ int memcmp_pages(struct page *page1, struct page *page2)
 	return ret;
 }
 
+u8 mte_get_mem_tag(void *addr)
+{
+	if (system_supports_mte())
+		asm volatile(ALTERNATIVE("ldr %0, [%0]",
+					 __MTE_PREAMBLE "ldg %0, [%0]",
+					 ARM64_MTE)
+			     : "+r" (addr));

This doesn't do what you think it does. LDG indeed reads the tag from memory but LDR loads the actual data at that address. Instead of the first LDR, you may want something like "mov %0, #0xf << 56" (and use some macros to avoid the hard-coded 56).

Quoted 4 lines
arch/arm64/kernel/mte.c +4 -0
+
+	return 0xF0 | mte_get_ptr_tag(addr);
+}
+

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 97

Quoted 10 lines
arch/arm64/kernel/mte.c +10 -0
+u8 mte_get_random_tag(void)
+{
+	u8 tag = 0xF;
+	u64 addr = 0;
+
+	if (system_supports_mte()) {
+		asm volatile(ALTERNATIVE("add %0, %0, %0",
+					 __MTE_PREAMBLE "irg %0, %0",
+					 ARM64_MTE)
+			     : "+r" (addr));

What was the intention here? The first ADD doubles the pointer value and gets a tag out of it (possibly doubled as well, depends on the carry from bit 55). Better use something like "orr %0, %0, #0xf << 56".

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 102

Quoted 5 lines
arch/arm64/kernel/mte.c +5 -0
+
+		tag = mte_get_ptr_tag(addr);
+	}
+
+	return 0xF0 | tag;

This function return seems inconsistent with the previous one. I'd prefer the return line to be the same in both.

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 115

Quoted 13 lines
arch/arm64/kernel/mte.c +13 -0
+}
+
+void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag)
+{
+	void *ptr = addr;
+
+	if ((!system_supports_mte()) || (size == 0))
+		return addr;
+
+	/* Make sure that size is aligned. */
+	WARN_ON(size & (MTE_GRANULE_SIZE - 1));
+
+	tag = 0xF0 | (tag & 0xF);

No point in tag & 0xf, the top nibble doesn't matter as you or 0xf0 in.

Quoted 25 lines
arch/arm64/kernel/mte.c +7 -0
+	ptr = (void *)__tag_set(ptr, tag);
+
+	mte_assign_mem_tag_range(ptr, size);
+
+	return ptr;
+}
+
 static void update_sctlr_el1_tcf0(u64 tcf0)
 {
 	/* ISB required for the kernel uaccess routines */
arch/arm64/lib/mte.S +7 -0
index 03ca6d8b8670..cc2c3a378c00 100644
@@ -149,3 +149,20 @@ SYM_FUNC_START(mte_restore_page_tags)
 
 	ret
 SYM_FUNC_END(mte_restore_page_tags)
+
+/*
+ * Assign allocation tags for a region of memory based on the pointer tag
+ *   x0 - source pointer
+ *   x1 - size
+ *
+ * Note: size must be non-zero and MTE_GRANULE_SIZE aligned

arch/arm64/lib/mte.S SYM_FUNC_START(mte_restore_page_tags) · line 168

Quoted 10 lines
arch/arm64/lib/mte.S +10 -0
+ */
+SYM_FUNC_START(mte_assign_mem_tag_range)
+	/* if (src == NULL) return; */
+	cbz	x0, 2f
+1:	stg	x0, [x0]
+	add	x0, x0, #MTE_GRANULE_SIZE
+	sub	x1, x1, #MTE_GRANULE_SIZE
+	cbnz	x1, 1b
+2:	ret
+SYM_FUNC_END(mte_assign_mem_tag_range)

I thought Vincenzo agreed to my comments on the previous version w.r.t. the fist cbz and the last cbnz:

https://lore.kernel.org/linux-arm-kernel/[email protected]/

Signature · 1 line
Catalin
#39 lore raw

Re: [PATCH v2 22/37] arm64: mte: Add in-kernel MTE helpers

Quoted 21 lines · Catalin Marinas
Quoted 6 lines · Andrey Konovalov
arch/arm64/include/asm/mte-helpers.h new file +0 -0
new file mode 100644
index 000000000000..5dc2d443851b

arch/arm64/include/asm/mte-helpers.h line 8

Quoted 8 lines
arch/arm64/include/asm/mte-helpers.h new file +8 -0
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 ARM Ltd.
+ */
+#ifndef __ASM_MTE_ASM_H
+#define __ASM_MTE_ASM_H
+
+#define __MTE_PREAMBLE		".arch armv8.5-a\n.arch_extension memtag\n"

Because of how the .arch overrides a previous .arch, we should follow the ARM64_ASM_PREAMBLE introduced in commit 1764c3edc668 ("arm64: use a common .arch preamble for inline assembly"). The above should be something like:

Quoted 3 lines

#define __MTE_PREAMBLE ARM64_ASM_PREAMBLE ".arch_extension memtag"

with the ARM64_ASM_PREAMBLE adjusted to armv8.5-a if available.

Good idea, I was not aware of commit 1764c3edc668. I will fix it accordingly.

Quoted 13 lines

arch/arm64/include/asm/mte-helpers.h line 15

Quoted 7 lines
arch/arm64/include/asm/mte-helpers.h new file +6 -0
+#define MTE_GRANULE_SIZE	UL(16)
+#define MTE_GRANULE_MASK	(~(MTE_GRANULE_SIZE - 1))
+#define MTE_TAG_SHIFT		56
+#define MTE_TAG_SIZE		4
+#define MTE_TAG_MASK		GENMASK((MTE_TAG_SHIFT + (MTE_TAG_SIZE - 1)), MTE_TAG_SHIFT)
+#define MTE_TAG_MAX		(MTE_TAG_MASK >> MTE_TAG_SHIFT)

In v1 I suggested we keep those definitions in mte-def.h (or mte-hwdef.h) so that they can be included in cache.h. Anything else should go in mte.h, I don't see the point of two headers for various MTE function prototypes.

This is what I did in my patches I shared with Andrey. I suppose that since in this version he introduced some functions that are needed in this file, he reverted to the old name (mte-helper.h).

Quoted 20 lines

arch/arm64/include/asm/mte-helpers.h line 23

Quoted 8 lines
arch/arm64/include/asm/mte-helpers.h new file +8 -0
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+#ifdef CONFIG_ARM64_MTE
+
+#define mte_get_ptr_tag(ptr)	((u8)(((u64)(ptr)) >> MTE_TAG_SHIFT))

I wonder whether this could also be an inline function that takes a void *ptr.

Quoted 8 lines
arch/arm64/kernel/mte.c +0 -0
index 52a0638ed967..e238ffde2679 100644
@@ -72,6 +74,52 @@ int memcmp_pages(struct page *page1, struct page *page2)
 	return ret;
 }
 
Quoted 13 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 83

Quoted 7 lines
arch/arm64/kernel/mte.c +7 -0
+u8 mte_get_mem_tag(void *addr)
+{
+	if (system_supports_mte())
+		asm volatile(ALTERNATIVE("ldr %0, [%0]",
+					 __MTE_PREAMBLE "ldg %0, [%0]",
+					 ARM64_MTE)
+			     : "+r" (addr));

This doesn't do what you think it does. LDG indeed reads the tag from memory but LDR loads the actual data at that address. Instead of the first LDR, you may want something like "mov %0, #0xf << 56" (and use some macros to avoid the hard-coded 56).

The result of the load should never be used since it is meaningful only if system_supports_mte(). It should be only required for compilation purposes.

Said that, I think I like more your solution hence I am going to adopt it.

Quoted 10 lines
Quoted 10 lines
arch/arm64/kernel/mte.c +10 -0
+
+	return 0xF0 | mte_get_ptr_tag(addr);
+}
+
+u8 mte_get_random_tag(void)
+{
+	u8 tag = 0xF;
+	u64 addr = 0;
+
+	if (system_supports_mte()) {
Quoted 9 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 97

Quoted 4 lines
arch/arm64/kernel/mte.c +4 -0
+		asm volatile(ALTERNATIVE("add %0, %0, %0",
+					 __MTE_PREAMBLE "irg %0, %0",
+					 ARM64_MTE)
+			     : "+r" (addr));

What was the intention here? The first ADD doubles the pointer value and gets a tag out of it (possibly doubled as well, depends on the carry from bit 55). Better use something like "orr %0, %0, #0xf << 56".

Same as above but I will use the orr in the next version.

Quoted 9 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 102

Quoted 5 lines
arch/arm64/kernel/mte.c +5 -0
+
+		tag = mte_get_ptr_tag(addr);
+	}
+
+	return 0xF0 | tag;

This function return seems inconsistent with the previous one. I'd prefer the return line to be the same in both.

The reason why it is different is that in this function extracting the tag from the address makes sense only if irg is executed.

I can initialize addr to 0xf << 56 and make them the same.

Quoted 7 lines
Quoted 7 lines
arch/arm64/kernel/mte.c +7 -0
+}
+
+void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag)
+{
+	void *ptr = addr;
+
+	if ((!system_supports_mte()) || (size == 0))
Quoted 9 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 115

Quoted 6 lines
arch/arm64/kernel/mte.c +6 -0
+		return addr;
+
+	/* Make sure that size is aligned. */
+	WARN_ON(size & (MTE_GRANULE_SIZE - 1));
+
+	tag = 0xF0 | (tag & 0xF);

No point in tag & 0xf, the top nibble doesn't matter as you or 0xf0 in.

Agree, will remove in the next version.

Quoted 39 lines
Quoted 25 lines
arch/arm64/kernel/mte.c +7 -0
+	ptr = (void *)__tag_set(ptr, tag);
+
+	mte_assign_mem_tag_range(ptr, size);
+
+	return ptr;
+}
+
 static void update_sctlr_el1_tcf0(u64 tcf0)
 {
 	/* ISB required for the kernel uaccess routines */
arch/arm64/lib/mte.S +7 -0
index 03ca6d8b8670..cc2c3a378c00 100644
@@ -149,3 +149,20 @@ SYM_FUNC_START(mte_restore_page_tags)
 
 	ret
 SYM_FUNC_END(mte_restore_page_tags)
+
+/*
+ * Assign allocation tags for a region of memory based on the pointer tag
+ *   x0 - source pointer
+ *   x1 - size
+ *
+ * Note: size must be non-zero and MTE_GRANULE_SIZE aligned

arch/arm64/lib/mte.S SYM_FUNC_START(mte_restore_page_tags) · line 168

Quoted 10 lines
arch/arm64/lib/mte.S +10 -0
+ */
+SYM_FUNC_START(mte_assign_mem_tag_range)
+	/* if (src == NULL) return; */
+	cbz	x0, 2f
+1:	stg	x0, [x0]
+	add	x0, x0, #MTE_GRANULE_SIZE
+	sub	x1, x1, #MTE_GRANULE_SIZE
+	cbnz	x1, 1b
+2:	ret
+SYM_FUNC_END(mte_assign_mem_tag_range)

I thought Vincenzo agreed to my comments on the previous version w.r.t. the fist cbz and the last cbnz:

Quoted 2 lines

https://lore.kernel.org/linux-arm-kernel/[email protected]/

Ups, this is my fault, I just realized I missed to unstash this change. Will be present in the next version.

Signature · 2 lines
Regards,
Vincenzo
maintainer
#40 lore raw

Re: [PATCH v2 22/37] arm64: mte: Add in-kernel MTE helpers

Quoted 23 lines · Vincenzo Frascino
Quoted 9 lines · Catalin Marinas
Quoted 8 lines · Andrey Konovalov
arch/arm64/kernel/mte.c +0 -0
index 52a0638ed967..e238ffde2679 100644
@@ -72,6 +74,52 @@ int memcmp_pages(struct page *page1, struct page *page2)
 	return ret;
 }
 
Quoted 12 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 83

Quoted 7 lines
arch/arm64/kernel/mte.c +7 -0
+u8 mte_get_mem_tag(void *addr)
+{
+	if (system_supports_mte())
+		asm volatile(ALTERNATIVE("ldr %0, [%0]",
+					 __MTE_PREAMBLE "ldg %0, [%0]",
+					 ARM64_MTE)
+			     : "+r" (addr));

This doesn't do what you think it does. LDG indeed reads the tag from memory but LDR loads the actual data at that address. Instead of the first LDR, you may want something like "mov %0, #0xf << 56" (and use some macros to avoid the hard-coded 56).

Quoted 4 lines

The result of the load should never be used since it is meaningful only if system_supports_mte(). It should be only required for compilation purposes.

Said that, I think I like more your solution hence I am going to adopt it.

Forgot to mention, please remove the system_supports_mte() if you use ALTERNATIVE, we don't need both. I think the first asm instruction can be a NOP since the kernel addresses without KASAN_HW or ARM64_MTE have the top byte 0xff.

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
arch/arm64/kernel/mte.c +10 -0
+
+	return 0xF0 | mte_get_ptr_tag(addr);
+}
+
+u8 mte_get_random_tag(void)
+{
+	u8 tag = 0xF;
+	u64 addr = 0;
+
+	if (system_supports_mte()) {
Quoted 8 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 97

Quoted 4 lines
arch/arm64/kernel/mte.c +4 -0
+		asm volatile(ALTERNATIVE("add %0, %0, %0",
+					 __MTE_PREAMBLE "irg %0, %0",
+					 ARM64_MTE)
+			     : "+r" (addr));

What was the intention here? The first ADD doubles the pointer value and gets a tag out of it (possibly doubled as well, depends on the carry from bit 55). Better use something like "orr %0, %0, #0xf << 56".

Quoted 1 line

Same as above but I will use the orr in the next version.

I wonder whether system_supports_mte() makes more sense here than the alternative:

	if (!system_supports_mte())
		return 0xff;

	... mte irg stuff ...

(you could do the same for the mte_get_mem_tag() function)

Quoted 9 lines
Quoted 8 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 102

Quoted 5 lines
arch/arm64/kernel/mte.c +5 -0
+
+		tag = mte_get_ptr_tag(addr);
+	}
+
+	return 0xF0 | tag;

This function return seems inconsistent with the previous one. I'd prefer the return line to be the same in both.

Quoted 4 lines

The reason why it is different is that in this function extracting the tag from the address makes sense only if irg is executed.

I can initialize addr to 0xf << 56 and make them the same.

I think you are right, they can be different. But see my comment above about not doing the unnecessary shifting when all you want is to return 0xff with !MTE.

Signature · 1 line
Catalin
#41 lore raw

Re: [PATCH v2 22/37] arm64: mte: Add in-kernel MTE helpers

Quoted 11 lines · Catalin Marinas
Quoted 1 line

Same as above but I will use the orr in the next version.

I wonder whether system_supports_mte() makes more sense here than the alternative:

	if (!system_supports_mte())
		return 0xff;

	... mte irg stuff ...

(you could do the same for the mte_get_mem_tag() function)

This would have been my preference from the beginning but then you mentioned alternatives ;)

Anyway, more then happy to change the code in this way, seems more clean and easy to understand.

Signature · 2 lines
Regards,
Vincenzo
#42 lore raw

Re: [PATCH v2 22/37] arm64: mte: Add in-kernel MTE helpers

In reply to Catalin Marinas
Quoted 8 lines · Catalin Marinas
Quoted 8 lines
arch/arm64/kernel/mte.c +0 -0
index 52a0638ed967..e238ffde2679 100644
@@ -72,6 +74,52 @@ int memcmp_pages(struct page *page1, struct page *page2)
 	return ret;
 }
 
Quoted 12 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 83

Quoted 7 lines
arch/arm64/kernel/mte.c +7 -0
+u8 mte_get_mem_tag(void *addr)
+{
+	if (system_supports_mte())
+		asm volatile(ALTERNATIVE("ldr %0, [%0]",
+					 __MTE_PREAMBLE "ldg %0, [%0]",
+					 ARM64_MTE)
+			     : "+r" (addr));

This doesn't do what you think it does. LDG indeed reads the tag from memory but LDR loads the actual data at that address. Instead of the first LDR, you may want something like "mov %0, #0xf << 56" (and use some macros to avoid the hard-coded 56).

Seems I can't encode a shift of 56 neither in mov nor in orr. I propose to replace both with an and of the address with itself.
This should not change anything.

Thoughts?

Quoted 18 lines
Quoted 4 lines
arch/arm64/kernel/mte.c +4 -0
+
+	return 0xF0 | mte_get_ptr_tag(addr);
+}
+

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 97

Quoted 10 lines
arch/arm64/kernel/mte.c +10 -0
+u8 mte_get_random_tag(void)
+{
+	u8 tag = 0xF;
+	u64 addr = 0;
+
+	if (system_supports_mte()) {
+		asm volatile(ALTERNATIVE("add %0, %0, %0",
+					 __MTE_PREAMBLE "irg %0, %0",
+					 ARM64_MTE)
+			     : "+r" (addr));

What was the intention here? The first ADD doubles the pointer value and gets a tag out of it (possibly doubled as well, depends on the carry from bit 55). Better use something like "orr %0, %0, #0xf << 56".

Signature · 2 lines
Regards,
Vincenzo
maintainer
#43 lore raw

Re: [PATCH v2 22/37] arm64: mte: Add in-kernel MTE helpers

Quoted 22 lines · Vincenzo Frascino
Quoted 8 lines · Catalin Marinas
Quoted 8 lines
arch/arm64/kernel/mte.c +0 -0
index 52a0638ed967..e238ffde2679 100644
@@ -72,6 +74,52 @@ int memcmp_pages(struct page *page1, struct page *page2)
 	return ret;
 }
 
Quoted 12 lines

arch/arm64/kernel/mte.c int memcmp_pages(struct page *page1, struct page *page2) · line 83

Quoted 7 lines
arch/arm64/kernel/mte.c +7 -0
+u8 mte_get_mem_tag(void *addr)
+{
+	if (system_supports_mte())
+		asm volatile(ALTERNATIVE("ldr %0, [%0]",
+					 __MTE_PREAMBLE "ldg %0, [%0]",
+					 ARM64_MTE)
+			     : "+r" (addr));

This doesn't do what you think it does. LDG indeed reads the tag from memory but LDR loads the actual data at that address. Instead of the first LDR, you may want something like "mov %0, #0xf << 56" (and use some macros to avoid the hard-coded 56).

Quoted 3 lines

Seems I can't encode a shift of 56 neither in mov nor in orr. I propose to replace both with an and of the address with itself.
This should not change anything.

Then use a NOP.

Signature · 1 line
Catalin
#44 lore raw

PATCH v2 23/37 arm64: kasan: Add arch layer for memory tagging helpers

In reply to Andrey Konovalov

This patch add a set of arch_*() memory tagging helpers currently only defined for arm64 when hardware tag-based KASAN is enabled. These helpers will be used by KASAN runtime to implement the hardware tag-based mode.

The arch-level indirection level is introduced to simplify adding hardware tag-based KASAN support for other architectures in the future by defining the appropriate arch_*() macros.

---
Change-Id: I42b0795a28067872f8308e00c6f0195bca435c2a

---
 arch/arm64/include/asm/memory.h |  8 ++++++++
 mm/kasan/kasan.h                | 19 +++++++++++++++++++
 2 files changed, 27 insertions(+)
arch/arm64/include/asm/memory.h +8 -0
index e424fc3a68cb..268a3b6cebd2 100644
@@ -231,6 +231,14 @@ static inline const void *__tag_set(const void *addr, u8 tag)
 	return (const void *)(__addr | __tag_shifted(tag));
 }
 
+#ifdef CONFIG_KASAN_HW_TAGS
+#define arch_init_tags(max_tag)			mte_init_tags(max_tag)
+#define arch_get_random_tag()			mte_get_random_tag()
+#define arch_get_mem_tag(addr)			mte_get_mem_tag(addr)
+#define arch_set_mem_tag_range(addr, size, tag)	\
+			mte_set_mem_tag_range((addr), (size), (tag))
+#endif /* CONFIG_KASAN_HW_TAGS */
+
 /*
  * Physical vs virtual RAM address space conversion.  These are
  * private definitions which should NOT be used outside memory.h
mm/kasan/kasan.h +19 -0
index 1d3c7c6ce771..8b43fc163ed1 100644
@@ -240,6 +240,25 @@ static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
 #define reset_tag(addr)		((void *)arch_kasan_reset_tag(addr))
 #define get_tag(addr)		arch_kasan_get_tag(addr)
 
+#ifndef arch_init_tags
+#define arch_init_tags(max_tag)
+#endif
+#ifndef arch_get_random_tag
+#define arch_get_random_tag()	(0xFF)
+#endif
+#ifndef arch_get_mem_tag
+#define arch_get_mem_tag(addr)	(0xFF)
+#endif
+#ifndef arch_set_mem_tag_range
+#define arch_set_mem_tag_range(addr, size, tag) ((void *)(addr))
+#endif
+
+#define init_tags(max_tag)			arch_init_tags(max_tag)
+#define get_random_tag()			arch_get_random_tag()
+#define get_mem_tag(addr)			arch_get_mem_tag(addr)
+#define set_mem_tag_range(addr, size, tag)	\
+				arch_set_mem_tag_range((addr), (size), (tag))
+
 /*
  * Exported functions for interfaces called from assembly or from generated
  * code. Declarations here to avoid warning about missing declarations.
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Reviewed-by on v2 23/37
#45 lore raw

Re: [PATCH v2 23/37] arm64: kasan: Add arch layer for memory tagging helpers

Quoted 11 lines · Andrey Konovalov

This patch add a set of arch_*() memory tagging helpers currently only defined for arm64 when hardware tag-based KASAN is enabled. These helpers will be used by KASAN runtime to implement the hardware tag-based mode.

The arch-level indirection level is introduced to simplify adding hardware tag-based KASAN support for other architectures in the future by defining the appropriate arch_*() macros.

#46 lore raw

Re: [PATCH v2 23/37] arm64: kasan: Add arch layer for memory tagging helpers

In reply to Andrey Konovalov
Quoted 23 lines · Andrey Konovalov' via kasan-dev

This patch add a set of arch_*() memory tagging helpers currently only defined for arm64 when hardware tag-based KASAN is enabled. These helpers will be used by KASAN runtime to implement the hardware tag-based mode.

The arch-level indirection level is introduced to simplify adding hardware tag-based KASAN support for other architectures in the future by defining the appropriate arch_*() macros.

---
Change-Id: I42b0795a28067872f8308e00c6f0195bca435c2a

---
 arch/arm64/include/asm/memory.h |  8 ++++++++
 mm/kasan/kasan.h                | 19 +++++++++++++++++++
 2 files changed, 27 insertions(+)
arch/arm64/include/asm/memory.h +0 -0
index e424fc3a68cb..268a3b6cebd2 100644

arch/arm64/include/asm/memory.h static inline const void *__tag_set(const void *addr, u8 tag) · line 239

Quoted 9 lines
arch/arm64/include/asm/memory.h +6 -0
@@ -231,6 +231,14 @@ static inline const void *__tag_set(const void *addr, u8 tag)
 	return (const void *)(__addr | __tag_shifted(tag));
 }
 
+#ifdef CONFIG_KASAN_HW_TAGS
+#define arch_init_tags(max_tag)			mte_init_tags(max_tag)
+#define arch_get_random_tag()			mte_get_random_tag()
+#define arch_get_mem_tag(addr)			mte_get_mem_tag(addr)
+#define arch_set_mem_tag_range(addr, size, tag)	\
+			mte_set_mem_tag_range((addr), (size), (tag))

Suggested edit below, assuming you're fine with checkpatch.pl's new 100col limit:

-#define set_mem_tag_range(addr, size, tag) \
- arch_set_mem_tag_range((addr), (size), (tag))
+#define set_mem_tag_range(addr, size, tag) arch_set_mem_tag_range((addr), (size), (tag))

#47 lore raw

Re: [PATCH v2 23/37] arm64: kasan: Add arch layer for memory tagging helpers

Quoted 38 lines · Marco Elver
Quoted 23 lines · Andrey Konovalov' via kasan-dev

This patch add a set of arch_*() memory tagging helpers currently only defined for arm64 when hardware tag-based KASAN is enabled. These helpers will be used by KASAN runtime to implement the hardware tag-based mode.

The arch-level indirection level is introduced to simplify adding hardware tag-based KASAN support for other architectures in the future by defining the appropriate arch_*() macros.

---
Change-Id: I42b0795a28067872f8308e00c6f0195bca435c2a

---
 arch/arm64/include/asm/memory.h |  8 ++++++++
 mm/kasan/kasan.h                | 19 +++++++++++++++++++
 2 files changed, 27 insertions(+)
arch/arm64/include/asm/memory.h +0 -0
index e424fc3a68cb..268a3b6cebd2 100644

arch/arm64/include/asm/memory.h static inline const void *__tag_set(const void *addr, u8 tag) · line 239

Quoted 9 lines
arch/arm64/include/asm/memory.h +6 -0
@@ -231,6 +231,14 @@ static inline const void *__tag_set(const void *addr, u8 tag)
      return (const void *)(__addr | __tag_shifted(tag));
 }
 
+#ifdef CONFIG_KASAN_HW_TAGS
+#define arch_init_tags(max_tag)                      mte_init_tags(max_tag)
+#define arch_get_random_tag()                        mte_get_random_tag()
+#define arch_get_mem_tag(addr)                       mte_get_mem_tag(addr)
+#define arch_set_mem_tag_range(addr, size, tag)      \
+                     mte_set_mem_tag_range((addr), (size), (tag))

Suggested edit below, assuming you're fine with checkpatch.pl's new 100col limit:

Review

Quoted 3 lines
(quoted diff) +1 -2
-#define set_mem_tag_range(addr, size, tag)     \
-                               arch_set_mem_tag_range((addr), (size), (tag))
+#define set_mem_tag_range(addr, size, tag)     arch_set_mem_tag_range((addr), (size), (tag))

Will do in v3, thanks!

#48 lore raw

PATCH v2 24/37 arm64: mte: Add in-kernel tag fault handler

In reply to Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

Add the implementation of the in-kernel fault handler.

When a tag fault happens on a kernel address:
* a warning is logged,
* MTE is disabled on the current CPU,
* the execution continues.

When a tag fault happens on a user address:
* the kernel executes do_bad_area() and panics.

---
Change-Id: I9b8aa79567f7c45f4d6a1290efcf34567e620717

---
 arch/arm64/mm/fault.c | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)
arch/arm64/mm/fault.c +35 -1
index a3bd189602df..cdc23662691c 100644
@@ -33,6 +33,7 @@
 #include <asm/debug-monitors.h>
 #include <asm/esr.h>
 #include <asm/kprobes.h>
+#include <asm/mte.h>
 #include <asm/processor.h>
 #include <asm/sysreg.h>
 #include <asm/system_misc.h>
@@ -294,6 +295,18 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
 	do_exit(SIGKILL);
 }
 
+static void report_tag_fault(unsigned long addr, unsigned int esr,
+			     struct pt_regs *regs)
+{
+	bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
+
+	pr_alert("Memory Tagging Extension Fault in %pS\n", (void *)regs->pc);
+	pr_alert("  %s at address %lx\n", is_write ? "Write" : "Read", addr);
+	pr_alert("  Pointer tag: [%02x], memory tag: [%02x]\n",
+			mte_get_ptr_tag(addr),
+			mte_get_mem_tag((void *)addr));
+}
+
 static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
 {
@@ -641,10 +654,31 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 	return 0;
 }
 
+static void do_tag_recovery(unsigned long addr, unsigned int esr,
+			   struct pt_regs *regs)
+{
+	report_tag_fault(addr, esr, regs);
+
+	/*
+	 * Disable Memory Tagging Extension Tag Checking on the local CPU
+	 * for the current EL.
+	 * It will be done lazily on the other CPUs when they will hit a
+	 * tag fault.
+	 */
+	sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_NONE);
+	isb();
+}
+
+
 static int do_tag_check_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
 {
-	do_bad_area(addr, esr, regs);
+	/* The tag check fault (TCF) is per TTBR */
+	if (is_ttbr0_addr(addr))
+		do_bad_area(addr, esr, regs);
+	else
+		do_tag_recovery(addr, esr, regs);
+
 	return 0;
 }
 
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer
#49 lore raw

Re: [PATCH v2 24/37] arm64: mte: Add in-kernel tag fault handler

Quoted 24 lines · Andrey Konovalov
arch/arm64/mm/fault.c +12 -0
index a3bd189602df..cdc23662691c 100644
@@ -294,6 +295,18 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
 	do_exit(SIGKILL);
 }
 
+static void report_tag_fault(unsigned long addr, unsigned int esr,
+			     struct pt_regs *regs)
+{
+	bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
+
+	pr_alert("Memory Tagging Extension Fault in %pS\n", (void *)regs->pc);
+	pr_alert("  %s at address %lx\n", is_write ? "Write" : "Read", addr);
+	pr_alert("  Pointer tag: [%02x], memory tag: [%02x]\n",
+			mte_get_ptr_tag(addr),
+			mte_get_mem_tag((void *)addr));
+}
+
 static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
 {

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 660

Quoted 7 lines
arch/arm64/mm/fault.c +4 -0
@@ -641,10 +654,31 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 	return 0;
 }
 
+static void do_tag_recovery(unsigned long addr, unsigned int esr,
+			   struct pt_regs *regs)
+{
+	report_tag_fault(addr, esr, regs);

I'd only report this once since we expect it to be disabled lazily on the other CPUs (i.e. just use a "static bool reported" to keep track).

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 663

Quoted 3 lines
arch/arm64/mm/fault.c +3 -0
+
+	/*
+	 * Disable Memory Tagging Extension Tag Checking on the local CPU

Too verbose, just say MTE tag checking, people reading this code should have learnt already what MTE stands for ;).

Quoted 8 lines
arch/arm64/mm/fault.c +8 -0
+	 * for the current EL.
+	 * It will be done lazily on the other CPUs when they will hit a
+	 * tag fault.
+	 */
+	sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_NONE);
+	isb();
+}
+

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 680

Quoted 10 lines
arch/arm64/mm/fault.c +6 -1
+
 static int do_tag_check_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
 {
-	do_bad_area(addr, esr, regs);
+	/* The tag check fault (TCF) is per TTBR */
+	if (is_ttbr0_addr(addr))
+		do_bad_area(addr, esr, regs);
+	else
+		do_tag_recovery(addr, esr, regs);

This part looks fine now.

Signature · 1 line
Catalin
#50 lore raw

Re: [PATCH v2 24/37] arm64: mte: Add in-kernel tag fault handler

Quoted 28 lines · Catalin Marinas
Quoted 27 lines · Andrey Konovalov
arch/arm64/mm/fault.c +12 -0
index a3bd189602df..cdc23662691c 100644
@@ -294,6 +295,18 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
 	do_exit(SIGKILL);
 }
 
+static void report_tag_fault(unsigned long addr, unsigned int esr,
+			     struct pt_regs *regs)
+{
+	bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
+
+	pr_alert("Memory Tagging Extension Fault in %pS\n", (void *)regs->pc);
+	pr_alert("  %s at address %lx\n", is_write ? "Write" : "Read", addr);
+	pr_alert("  Pointer tag: [%02x], memory tag: [%02x]\n",
+			mte_get_ptr_tag(addr),
+			mte_get_mem_tag((void *)addr));
+}
+
 static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
 {
@@ -641,10 +654,31 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 	return 0;
 }
 
Quoted 8 lines

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 660

Quoted 4 lines
arch/arm64/mm/fault.c +4 -0
+static void do_tag_recovery(unsigned long addr, unsigned int esr,
+			   struct pt_regs *regs)
+{
+	report_tag_fault(addr, esr, regs);

I'd only report this once since we expect it to be disabled lazily on the other CPUs (i.e. just use a "static bool reported" to keep track).

Ok, I will fix it in the next version.

Quoted 7 lines

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 663

Quoted 3 lines
arch/arm64/mm/fault.c +3 -0
+
+	/*
+	 * Disable Memory Tagging Extension Tag Checking on the local CPU

Too verbose, just say MTE tag checking, people reading this code should have learnt already what MTE stands for ;).

All right, will change it ;)

Quoted 12 lines
Quoted 12 lines
arch/arm64/mm/fault.c +9 -0
+	 * for the current EL.
+	 * It will be done lazily on the other CPUs when they will hit a
+	 * tag fault.
+	 */
+	sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_NONE);
+	isb();
+}
+
+
 static int do_tag_check_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
 {
Quoted 9 lines

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 680

Quoted 6 lines
arch/arm64/mm/fault.c +5 -1
-	do_bad_area(addr, esr, regs);
+	/* The tag check fault (TCF) is per TTBR */
+	if (is_ttbr0_addr(addr))
+		do_bad_area(addr, esr, regs);
+	else
+		do_tag_recovery(addr, esr, regs);

This part looks fine now.

Ok, thanks.

Signature · 2 lines
Regards,
Vincenzo
maintainer
#51 lore raw

Re: [PATCH v2 24/37] arm64: mte: Add in-kernel tag fault handler

In reply to Andrey Konovalov

arch/arm64/mm/fault.c static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · line 683

Quoted 12 lines · Andrey Konovalov

static int do_tag_check_fault(unsigned long addr, unsigned int esr,

arch/arm64/mm/fault.c +6 -1
 			      struct pt_regs *regs)
 {
-	do_bad_area(addr, esr, regs);
+	/* The tag check fault (TCF) is per TTBR */
+	if (is_ttbr0_addr(addr))
+		do_bad_area(addr, esr, regs);
+	else
+		do_tag_recovery(addr, esr, regs);
+
 	return 0;
 }

I had forgotten the details here. The TCF mode is per EL, so TCF0 affects EL0, TCF affects EL1 irrespective of which TTBR is used. Now, we know the kernel accesses TTBR0 usually with LDTR/STTR instructions if UAO is available (soon to get rid of), so these would act as EL0 accesses using TCF0. However, we have the futex.h code which uses exclusives and they'd be executed as EL1, so you can potentially get a tag check fault for such uaccess even if the user disabled it in TCF0.

The solution here I think is for uaccess_enable() to set PSTATE.TCO, restore it in uaccess_disable().

We get away with not toggling PSTATE.TCO in the user MTE patches since the TCF is always 0 for the kernel.

The do_tag_check_fault() above is still correct, apart from the comment which needs a better explanation on why we do a is_ttbr0_addr() check.

Signature · 1 line
Catalin
#52 lore raw

PATCH v2 25/37 arm64: kasan: Enable in-kernel MTE

In reply to Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

Hardware tag-based KASAN relies on Memory Tagging Extension (MTE) feature and requires it to be enabled.

The Tag Checking operation causes a synchronous data abort as a consequence of a tag check fault when MTE is configured in synchronous mode.

Enable MTE in Synchronous mode in EL1 to provide a more immediate way of tag check failure detection in the kernel.

As part of this change enable match-all tag for EL1 to allow the kernel to access user pages without faulting. This is required because the kernel does not have knowledge of the tags set by the user in a page.

Note: For MTE, the TCF bit field in SCTLR_EL1 affects only EL1 in a similar way as TCF0 affects EL0.

---
Change-Id: I4d67497268bb7f0c2fc5dcacefa1e273df4af71d

---
 arch/arm64/kernel/cpufeature.c |  7 +++++++
 arch/arm64/mm/proc.S           | 13 +++++++++++++
 2 files changed, 20 insertions(+)
arch/arm64/kernel/cpufeature.c +7 -0
index add9da5d8ea3..eca06b8c74db 100644
@@ -1718,6 +1718,13 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)
 		cleared_zero_page = true;
 		mte_clear_page_tags(lm_alias(empty_zero_page));
 	}
+
+	/* Enable in-kernel MTE only if KASAN_HW_TAGS is enabled */
+	if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) {
+		/* Enable MTE Sync Mode for EL1 */
+		sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_SYNC);
+		isb();
+	}
 }
 #endif /* CONFIG_ARM64_MTE */
 
arch/arm64/mm/proc.S +13 -0
index 23c326a06b2d..5ba7ac5e9c77 100644
@@ -427,6 +427,10 @@ SYM_FUNC_START(__cpu_setup)
 	 */
 	mov_q	x5, MAIR_EL1_SET
 #ifdef CONFIG_ARM64_MTE
+	mte_present	.req	x20
+
+	mov	mte_present, #0
+
 	/*
 	 * Update MAIR_EL1, GCR_EL1 and TFSR*_EL1 if MTE is supported
 	 * (ID_AA64PFR1_EL1[11:8] > 1).
@@ -447,6 +451,8 @@ SYM_FUNC_START(__cpu_setup)
 	/* clear any pending tag check faults in TFSR*_EL1 */
 	msr_s	SYS_TFSR_EL1, xzr
 	msr_s	SYS_TFSRE0_EL1, xzr
+
+	mov	mte_present, #1
 1:
 #endif
 	msr	mair_el1, x5
@@ -485,6 +491,13 @@ SYM_FUNC_START(__cpu_setup)
 	orr	x10, x10, #TCR_HA		// hardware Access flag update
 1:
 #endif	/* CONFIG_ARM64_HW_AFDBM */
+#ifdef CONFIG_ARM64_MTE
+	/* Update TCR_EL1 if MTE is supported (ID_AA64PFR1_EL1[11:8] > 1) */
+	cbz	mte_present, 1f
+	orr	x10, x10, #SYS_TCR_EL1_TCMA1
+1:
+	.unreq	mte_present
+#endif
 	msr	tcr_el1, x10
 	/*
 	 * Prepare SCTLR
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer
#53 lore raw

Re: [PATCH v2 25/37] arm64: kasan: Enable in-kernel MTE

Quoted 28 lines · Andrey Konovalov
arch/arm64/mm/proc.S +6 -0
index 23c326a06b2d..5ba7ac5e9c77 100644
@@ -427,6 +427,10 @@ SYM_FUNC_START(__cpu_setup)
 	 */
 	mov_q	x5, MAIR_EL1_SET
 #ifdef CONFIG_ARM64_MTE
+	mte_present	.req	x20
+
+	mov	mte_present, #0
+
 	/*
 	 * Update MAIR_EL1, GCR_EL1 and TFSR*_EL1 if MTE is supported
 	 * (ID_AA64PFR1_EL1[11:8] > 1).
@@ -447,6 +451,8 @@ SYM_FUNC_START(__cpu_setup)
 	/* clear any pending tag check faults in TFSR*_EL1 */
 	msr_s	SYS_TFSR_EL1, xzr
 	msr_s	SYS_TFSRE0_EL1, xzr
+
+	mov	mte_present, #1
 1:
 #endif
 	msr	mair_el1, x5
@@ -485,6 +491,13 @@ SYM_FUNC_START(__cpu_setup)
 	orr	x10, x10, #TCR_HA		// hardware Access flag update
 1:
 #endif	/* CONFIG_ARM64_HW_AFDBM */

arch/arm64/mm/proc.S SYM_FUNC_START(__cpu_setup) · line 503

Quoted 10 lines
arch/arm64/mm/proc.S +7 -0
+#ifdef CONFIG_ARM64_MTE
+	/* Update TCR_EL1 if MTE is supported (ID_AA64PFR1_EL1[11:8] > 1) */
+	cbz	mte_present, 1f
+	orr	x10, x10, #SYS_TCR_EL1_TCMA1
+1:
+	.unreq	mte_present
+#endif
 	msr	tcr_el1, x10
 	/*
 	 * Prepare SCTLR

I'd keep this simpler, no branches or #ifdefs (you can still add the .req if you want):

arch/arm64/mm/proc.S +5 -0
index 23c326a06b2d..a35344754081 100644
@@ -426,6 +426,7 @@ SYM_FUNC_START(__cpu_setup)
 	 * Memory region attributes
 	 */
 	mov_q	x5, MAIR_EL1_SET
+	mov	x8, #0
 #ifdef CONFIG_ARM64_MTE
 	/*
 	 * Update MAIR_EL1, GCR_EL1 and TFSR*_EL1 if MTE is supported
@@ -447,6 +448,9 @@ SYM_FUNC_START(__cpu_setup)
 	/* clear any pending tag check faults in TFSR*_EL1 */
 	msr_s	SYS_TFSR_EL1, xzr
 	msr_s	SYS_TFSRE0_EL1, xzr
+
+	/* set the TCR_EL1 bits */
+	orr	x8, x8, #SYS_TCR_EL1_TCMA1
 1:
 #endif
 	msr	mair_el1, x5
@@ -457,6 +461,7 @@ SYM_FUNC_START(__cpu_setup)
 	mov_q	x10, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
 			TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
 			TCR_TBI0 | TCR_A1 | TCR_KASAN_FLAGS
+	orr	x10, x10, x8
 	tcr_clear_errata_bits x10, x9, x5
 
 #ifdef CONFIG_ARM64_VA_BITS_52
Signature · 1 line
Catalin
#54 lore raw

PATCH v2 26/37 arm64: mte: Convert gcr_user into an exclude mask

In reply to Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

The gcr_user mask is a per thread mask that represents the tags that are excluded from random generation when the Memory Tagging Extension is present and an 'irg' instruction is invoked.

gcr_user affects the behavior on EL0 only.

Currently that mask is an include mask and it is controlled by the user via prctl() while GCR_EL1 accepts an exclude mask.

Convert the include mask into an exclude one to make it easier the register setting.

Note: This change will affect gcr_kernel (for EL1) introduced with a future patch.

---
Change-Id: Id15c0b47582fb51594bb26fb8353d78c7d0953c1

---
 arch/arm64/include/asm/processor.h |  2 +-
 arch/arm64/kernel/mte.c            | 29 +++++++++++++++--------------
 2 files changed, 16 insertions(+), 15 deletions(-)
arch/arm64/include/asm/processor.h +1 -1
index fec204d28fce..ed9efa5be8eb 100644
@@ -153,7 +153,7 @@ struct thread_struct {
 #endif
 #ifdef CONFIG_ARM64_MTE
 	u64			sctlr_tcf0;
-	u64			gcr_user_incl;
+	u64			gcr_user_excl;
 #endif
 };
 
arch/arm64/kernel/mte.c +15 -14
index e238ffde2679..858e75cfcaa0 100644
@@ -140,23 +140,22 @@ static void set_sctlr_el1_tcf0(u64 tcf0)
 	preempt_enable();
 }
 
-static void update_gcr_el1_excl(u64 incl)
+static void update_gcr_el1_excl(u64 excl)
 {
-	u64 excl = ~incl & SYS_GCR_EL1_EXCL_MASK;
 
 	/*
-	 * Note that 'incl' is an include mask (controlled by the user via
-	 * prctl()) while GCR_EL1 accepts an exclude mask.
+	 * Note that the mask controlled by the user via prctl() is an
+	 * include while GCR_EL1 accepts an exclude mask.
 	 * No need for ISB since this only affects EL0 currently, implicit
 	 * with ERET.
 	 */
 	sysreg_clear_set_s(SYS_GCR_EL1, SYS_GCR_EL1_EXCL_MASK, excl);
 }
 
-static void set_gcr_el1_excl(u64 incl)
+static void set_gcr_el1_excl(u64 excl)
 {
-	current->thread.gcr_user_incl = incl;
-	update_gcr_el1_excl(incl);
+	current->thread.gcr_user_excl = excl;
+	update_gcr_el1_excl(excl);
 }
 
 void flush_mte_state(void)
@@ -171,7 +170,7 @@ void flush_mte_state(void)
 	/* disable tag checking */
 	set_sctlr_el1_tcf0(SCTLR_EL1_TCF0_NONE);
 	/* reset tag generation mask */
-	set_gcr_el1_excl(0);
+	set_gcr_el1_excl(SYS_GCR_EL1_EXCL_MASK);
 }
 
 void mte_thread_switch(struct task_struct *next)
@@ -182,7 +181,7 @@ void mte_thread_switch(struct task_struct *next)
 	/* avoid expensive SCTLR_EL1 accesses if no change */
 	if (current->thread.sctlr_tcf0 != next->thread.sctlr_tcf0)
 		update_sctlr_el1_tcf0(next->thread.sctlr_tcf0);
-	update_gcr_el1_excl(next->thread.gcr_user_incl);
+	update_gcr_el1_excl(next->thread.gcr_user_excl);
 }
 
 void mte_suspend_exit(void)
@@ -190,13 +189,14 @@ void mte_suspend_exit(void)
 	if (!system_supports_mte())
 		return;
 
-	update_gcr_el1_excl(current->thread.gcr_user_incl);
+	update_gcr_el1_excl(current->thread.gcr_user_excl);
 }
 
 long set_mte_ctrl(struct task_struct *task, unsigned long arg)
 {
 	u64 tcf0;
-	u64 gcr_incl = (arg & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT;
+	u64 gcr_excl = ~((arg & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT) &
+		       SYS_GCR_EL1_EXCL_MASK;
 
 	if (!system_supports_mte())
 		return 0;
@@ -217,10 +217,10 @@ long set_mte_ctrl(struct task_struct *task, unsigned long arg)
 
 	if (task != current) {
 		task->thread.sctlr_tcf0 = tcf0;
-		task->thread.gcr_user_incl = gcr_incl;
+		task->thread.gcr_user_excl = gcr_excl;
 	} else {
 		set_sctlr_el1_tcf0(tcf0);
-		set_gcr_el1_excl(gcr_incl);
+		set_gcr_el1_excl(gcr_excl);
 	}
 
 	return 0;
@@ -229,11 +229,12 @@ long set_mte_ctrl(struct task_struct *task, unsigned long arg)
 long get_mte_ctrl(struct task_struct *task)
 {
 	unsigned long ret;
+	u64 incl = ~task->thread.gcr_user_excl & SYS_GCR_EL1_EXCL_MASK;
 
 	if (!system_supports_mte())
 		return 0;
 
-	ret = task->thread.gcr_user_incl << PR_MTE_TAG_SHIFT;
+	ret = incl << PR_MTE_TAG_SHIFT;
 
 	switch (task->thread.sctlr_tcf0) {
 	case SCTLR_EL1_TCF0_NONE:
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Reviewed-by on v2 26/37
#55 lore raw

Re: [PATCH v2 26/37] arm64: mte: Convert gcr_user into an exclude mask

Quoted 11 lines · Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

The gcr_user mask is a per thread mask that represents the tags that are excluded from random generation when the Memory Tagging Extension is present and an 'irg' instruction is invoked.

gcr_user affects the behavior on EL0 only.

Currently that mask is an include mask and it is controlled by the user via prctl() while GCR_EL1 accepts an exclude mask.

Quoted 8 lines

Convert the include mask into an exclude one to make it easier the register setting.

Note: This change will affect gcr_kernel (for EL1) introduced with a future patch.

#56 lore raw

PATCH v2 27/37 arm64: mte: Switch GCR_EL1 in kernel entry and exit

In reply to Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

When MTE is present, the GCR_EL1 register contains the tags mask that allows to exclude tags from the random generation via the IRG instruction.

With the introduction of the new Tag-Based KASAN API that provides a mechanism to reserve tags for special reasons, the MTE implementation has to make sure that the GCR_EL1 setting for the kernel does not affect the userspace processes and viceversa.

Save and restore the kernel/user mask in GCR_EL1 in kernel entry and exit.

---
Change-Id: I0081cba5ace27a9111bebb239075c9a466af4c84

---
 arch/arm64/include/asm/mte-helpers.h |  6 ++++++
 arch/arm64/include/asm/mte.h         |  2 ++
 arch/arm64/kernel/asm-offsets.c      |  3 +++
 arch/arm64/kernel/cpufeature.c       |  3 +++
 arch/arm64/kernel/entry.S            | 26 ++++++++++++++++++++++++++
 arch/arm64/kernel/mte.c              | 19 ++++++++++++++++---
 6 files changed, 56 insertions(+), 3 deletions(-)
arch/arm64/include/asm/mte-helpers.h +6 -0
index 5dc2d443851b..60a292fc747c 100644
@@ -25,6 +25,8 @@ u8 mte_get_mem_tag(void *addr);
 u8 mte_get_random_tag(void);
 void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag);
 
+void mte_init_tags(u64 max_tag);
+
 #else /* CONFIG_ARM64_MTE */
 
 #define mte_get_ptr_tag(ptr)	0xFF
@@ -41,6 +43,10 @@ static inline void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag)
 	return addr;
 }
 
+static inline void mte_init_tags(u64 max_tag)
+{
+}
+
 #endif /* CONFIG_ARM64_MTE */
 
 #endif /* __ASSEMBLY__ */
arch/arm64/include/asm/mte.h +2 -0
index 82cd7c89edec..3142a2de51ae 100644
@@ -15,6 +15,8 @@
 
 #include <asm/pgtable-types.h>
 
+extern u64 gcr_kernel_excl;
+
 void mte_clear_page_tags(void *addr);
 unsigned long mte_copy_tags_from_user(void *to, const void __user *from,
 				      unsigned long n);
arch/arm64/kernel/asm-offsets.c +3 -0
index 7d32fc959b1a..dfe6ed8446ac 100644
@@ -47,6 +47,9 @@ int main(void)
 #ifdef CONFIG_ARM64_PTR_AUTH
   DEFINE(THREAD_KEYS_USER,	offsetof(struct task_struct, thread.keys_user));
   DEFINE(THREAD_KEYS_KERNEL,	offsetof(struct task_struct, thread.keys_kernel));
+#endif
+#ifdef CONFIG_ARM64_MTE
+  DEFINE(THREAD_GCR_EL1_USER,	offsetof(struct task_struct, thread.gcr_user_excl));
 #endif
   BLANK();
   DEFINE(S_X0,			offsetof(struct pt_regs, regs[0]));
arch/arm64/kernel/cpufeature.c +3 -0
index eca06b8c74db..3602ac45d093 100644
@@ -1721,6 +1721,9 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)
 
 	/* Enable in-kernel MTE only if KASAN_HW_TAGS is enabled */
 	if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) {
+		/* Enable the kernel exclude mask for random tags generation */
+		write_sysreg_s((SYS_GCR_EL1_RRND | gcr_kernel_excl), SYS_GCR_EL1);
+
 		/* Enable MTE Sync Mode for EL1 */
 		sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_SYNC);
 		isb();
arch/arm64/kernel/entry.S +26 -0
index ff34461524d4..79a6848840bd 100644
@@ -175,6 +175,28 @@ alternative_else_nop_endif
 #endif
 	.endm
 
+	.macro mte_restore_gcr, el, tsk, tmp, tmp2
+#ifdef CONFIG_ARM64_MTE
+alternative_if_not ARM64_MTE
+	b	1f
+alternative_else_nop_endif
+	.if	\el == 0
+	ldr	\tmp, [\tsk, #THREAD_GCR_EL1_USER]
+	.else
+	ldr_l	\tmp, gcr_kernel_excl
+	.endif
+	/*
+	 * Calculate and set the exclude mask preserving
+	 * the RRND (bit[16]) setting.
+	 */
+	mrs_s	\tmp2, SYS_GCR_EL1
+	bfi	\tmp2, \tmp, #0, #16
+	msr_s	SYS_GCR_EL1, \tmp2
+	isb
+1:
+#endif
+	.endm
+
 	.macro	kernel_entry, el, regsize = 64
 	.if	\regsize == 32
 	mov	w0, w0				// zero upper 32 bits of x0
@@ -214,6 +236,8 @@ alternative_else_nop_endif
 
 	ptrauth_keys_install_kernel tsk, x20, x22, x23
 
+	mte_restore_gcr 1, tsk, x22, x23
+
 	scs_load tsk, x20
 	.else
 	add	x21, sp, #S_FRAME_SIZE
@@ -332,6 +356,8 @@ alternative_else_nop_endif
 	/* No kernel C function calls after this as user keys are set. */
 	ptrauth_keys_install_user tsk, x0, x1, x2
 
+	mte_restore_gcr 0, tsk, x0, x1
+
 	apply_ssbd 0, x0, x1
 	.endif
 
arch/arm64/kernel/mte.c +16 -3
index 858e75cfcaa0..1c7d963b5038 100644
@@ -18,10 +18,13 @@
 
 #include <asm/barrier.h>
 #include <asm/cpufeature.h>
+#include <asm/kprobes.h>
 #include <asm/mte.h>
 #include <asm/ptrace.h>
 #include <asm/sysreg.h>
 
+u64 gcr_kernel_excl __ro_after_init;
+
 static void mte_sync_page_tags(struct page *page, pte_t *ptep, bool check_swap)
 {
 	pte_t old_pte = READ_ONCE(*ptep);
@@ -120,6 +123,13 @@ void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag)
 	return ptr;
 }
 
+void mte_init_tags(u64 max_tag)
+{
+	u64 incl = GENMASK(max_tag & MTE_TAG_MAX, 0);
+
+	gcr_kernel_excl = ~incl & SYS_GCR_EL1_EXCL_MASK;
+}
+
 static void update_sctlr_el1_tcf0(u64 tcf0)
 {
 	/* ISB required for the kernel uaccess routines */
@@ -155,7 +165,11 @@ static void update_gcr_el1_excl(u64 excl)
 static void set_gcr_el1_excl(u64 excl)
 {
 	current->thread.gcr_user_excl = excl;
-	update_gcr_el1_excl(excl);
+
+	/*
+	 * SYS_GCR_EL1 will be set to current->thread.gcr_user_incl value
+	 * by mte_restore_gcr() in kernel_exit,
+	 */
 }
 
 void flush_mte_state(void)
@@ -181,7 +195,6 @@ void mte_thread_switch(struct task_struct *next)
 	/* avoid expensive SCTLR_EL1 accesses if no change */
 	if (current->thread.sctlr_tcf0 != next->thread.sctlr_tcf0)
 		update_sctlr_el1_tcf0(next->thread.sctlr_tcf0);
-	update_gcr_el1_excl(next->thread.gcr_user_excl);
 }
 
 void mte_suspend_exit(void)
@@ -189,7 +202,7 @@ void mte_suspend_exit(void)
 	if (!system_supports_mte())
 		return;
 
-	update_gcr_el1_excl(current->thread.gcr_user_excl);
+	update_gcr_el1_excl(gcr_kernel_excl);
 }
 
 long set_mte_ctrl(struct task_struct *task, unsigned long arg)
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer
#57 lore raw

Re: [PATCH v2 27/37] arm64: mte: Switch GCR_EL1 in kernel entry and exit

arch/arm64/kernel/cpufeature.c static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap) · line 1725

Quoted 10 lines · Andrey Konovalov
arch/arm64/kernel/cpufeature.c +2 -0
index eca06b8c74db..3602ac45d093 100644
@@ -1721,6 +1721,9 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)
 
 	/* Enable in-kernel MTE only if KASAN_HW_TAGS is enabled */
 	if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) {
+		/* Enable the kernel exclude mask for random tags generation */
+		write_sysreg_s((SYS_GCR_EL1_RRND | gcr_kernel_excl), SYS_GCR_EL1);

Nitpick: no need for extra braces, the comma has lower precedence.

Quoted 47 lines
arch/arm64/kernel/cpufeature.c +1 -0
+
 		/* Enable MTE Sync Mode for EL1 */
 		sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_SYNC);
 		isb();
arch/arm64/kernel/entry.S +24 -0
index ff34461524d4..79a6848840bd 100644
@@ -175,6 +175,28 @@ alternative_else_nop_endif
 #endif
 	.endm
 
+	.macro mte_restore_gcr, el, tsk, tmp, tmp2
+#ifdef CONFIG_ARM64_MTE
+alternative_if_not ARM64_MTE
+	b	1f
+alternative_else_nop_endif
+	.if	\el == 0
+	ldr	\tmp, [\tsk, #THREAD_GCR_EL1_USER]
+	.else
+	ldr_l	\tmp, gcr_kernel_excl
+	.endif
+	/*
+	 * Calculate and set the exclude mask preserving
+	 * the RRND (bit[16]) setting.
+	 */
+	mrs_s	\tmp2, SYS_GCR_EL1
+	bfi	\tmp2, \tmp, #0, #16
+	msr_s	SYS_GCR_EL1, \tmp2
+	isb
+1:
+#endif
+	.endm
+
 	.macro	kernel_entry, el, regsize = 64
 	.if	\regsize == 32
 	mov	w0, w0				// zero upper 32 bits of x0
@@ -214,6 +236,8 @@ alternative_else_nop_endif
 
 	ptrauth_keys_install_kernel tsk, x20, x22, x23
 
+	mte_restore_gcr 1, tsk, x22, x23
+
 	scs_load tsk, x20
 	.else
 	add	x21, sp, #S_FRAME_SIZE

arch/arm64/kernel/entry.S alternative_else_nop_endif · line 359

Quoted 4 lines
arch/arm64/kernel/entry.S +1 -0
@@ -332,6 +356,8 @@ alternative_else_nop_endif
 	/* No kernel C function calls after this as user keys are set. */
 	ptrauth_keys_install_user tsk, x0, x1, x2
 
+	mte_restore_gcr 0, tsk, x0, x1

Some nitpicks on these macros to match the ptrauth_keys_* above. Define separate mte_set_{user,kernel}_gcr macros with a common mte_set_gcr that is used by both.

arch/arm64/kernel/mte.c line 21

Quoted 13 lines
arch/arm64/kernel/entry.S +1 -0
+
 	apply_ssbd 0, x0, x1
 	.endif
 
arch/arm64/kernel/mte.c +1 -0
index 858e75cfcaa0..1c7d963b5038 100644
@@ -18,10 +18,13 @@
 
 #include <asm/barrier.h>
 #include <asm/cpufeature.h>
+#include <asm/kprobes.h>

What's this apparently random kprobes.h include?

Quoted 10 lines
arch/arm64/kernel/mte.c +2 -0
 #include <asm/mte.h>
 #include <asm/ptrace.h>
 #include <asm/sysreg.h>
 
+u64 gcr_kernel_excl __ro_after_init;
+
 static void mte_sync_page_tags(struct page *page, pte_t *ptep, bool check_swap)
 {
 	pte_t old_pte = READ_ONCE(*ptep);

arch/arm64/kernel/mte.c void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag) · line 131

Quoted 9 lines
arch/arm64/kernel/mte.c +6 -0
@@ -120,6 +123,13 @@ void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag)
 	return ptr;
 }
 
+void mte_init_tags(u64 max_tag)
+{
+	u64 incl = GENMASK(max_tag & MTE_TAG_MAX, 0);
+
+	gcr_kernel_excl = ~incl & SYS_GCR_EL1_EXCL_MASK;
+}

Do we need to set the actual GCR_EL1 register here? We may not get an exception by the time KASAN starts using it.

Signature · 1 line
Catalin
maintainer
#58 lore raw

Re: [PATCH v2 27/37] arm64: mte: Switch GCR_EL1 in kernel entry and exit

Quoted 47 lines · Catalin Marinas
Quoted 46 lines · Andrey Konovalov
arch/arm64/kernel/entry.S +24 -0
index ff34461524d4..79a6848840bd 100644
@@ -175,6 +175,28 @@ alternative_else_nop_endif
 #endif
 	.endm
 
+	.macro mte_restore_gcr, el, tsk, tmp, tmp2
+#ifdef CONFIG_ARM64_MTE
+alternative_if_not ARM64_MTE
+	b	1f
+alternative_else_nop_endif
+	.if	\el == 0
+	ldr	\tmp, [\tsk, #THREAD_GCR_EL1_USER]
+	.else
+	ldr_l	\tmp, gcr_kernel_excl
+	.endif
+	/*
+	 * Calculate and set the exclude mask preserving
+	 * the RRND (bit[16]) setting.
+	 */
+	mrs_s	\tmp2, SYS_GCR_EL1
+	bfi	\tmp2, \tmp, #0, #16
+	msr_s	SYS_GCR_EL1, \tmp2
+	isb
+1:
+#endif
+	.endm
+
 	.macro	kernel_entry, el, regsize = 64
 	.if	\regsize == 32
 	mov	w0, w0				// zero upper 32 bits of x0
@@ -214,6 +236,8 @@ alternative_else_nop_endif
 
 	ptrauth_keys_install_kernel tsk, x20, x22, x23
 
+	mte_restore_gcr 1, tsk, x22, x23
+
 	scs_load tsk, x20
 	.else
 	add	x21, sp, #S_FRAME_SIZE
@@ -332,6 +356,8 @@ alternative_else_nop_endif
 	/* No kernel C function calls after this as user keys are set. */
 	ptrauth_keys_install_user tsk, x0, x1, x2
 
Quoted 5 lines

arch/arm64/kernel/entry.S alternative_else_nop_endif · line 359

Quoted 1 line
arch/arm64/kernel/entry.S +1 -0
+	mte_restore_gcr 0, tsk, x0, x1

Some nitpicks on these macros to match the ptrauth_keys_* above. Define separate mte_set_{user,kernel}_gcr macros with a common mte_set_gcr that is used by both.

One more thing - the new mte_set_kernel_gcr should probably skip the GCR_EL1 update if KASAN_HW_TAGS is disabled.

Signature · 1 line
Catalin
#59 lore raw

Re: [PATCH v2 27/37] arm64: mte: Switch GCR_EL1 in kernel entry and exit

In reply to Catalin Marinas
Quoted 8 lines · Catalin Marinas

arch/arm64/kernel/mte.c void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag) · line 131

Quoted 6 lines
arch/arm64/kernel/mte.c +6 -0
+void mte_init_tags(u64 max_tag)
+{
+	u64 incl = GENMASK(max_tag & MTE_TAG_MAX, 0);
+
+	gcr_kernel_excl = ~incl & SYS_GCR_EL1_EXCL_MASK;
+}

Do we need to set the actual GCR_EL1 register here? We may not get an exception by the time KASAN starts using it.

It is ok not setting it here because to get exceptions cpuframework mte enable needs to be executed first. In that context we set even the register.

Signature · 2 lines
Regards,
Vincenzo
maintainer
#60 lore raw

Re: [PATCH v2 27/37] arm64: mte: Switch GCR_EL1 in kernel entry and exit

Quoted 10 lines · Vincenzo Frascino
Quoted 8 lines · Catalin Marinas

arch/arm64/kernel/mte.c void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag) · line 131

Quoted 6 lines
arch/arm64/kernel/mte.c +6 -0
+void mte_init_tags(u64 max_tag)
+{
+	u64 incl = GENMASK(max_tag & MTE_TAG_MAX, 0);
+
+	gcr_kernel_excl = ~incl & SYS_GCR_EL1_EXCL_MASK;
+}

Do we need to set the actual GCR_EL1 register here? We may not get an exception by the time KASAN starts using it.

Quoted 2 lines

It is ok not setting it here because to get exceptions cpuframework mte enable needs to be executed first. In that context we set even the register.

OK, that should do for now. If we ever add stack tagging, we'd have to rethink the GCR_EL1 initialisation.

Signature · 1 line
Catalin
#61 lore raw

PATCH v2 28/37 arm64: kasan: Enable TBI EL1

In reply to Andrey Konovalov

From: Vincenzo Frascino <[email protected]>

Hardware tag-based KASAN relies on Memory Tagging Extension (MTE) that is built on top of the Top Byte Ignore (TBI) feature.

Enable in-kernel TBI when CONFIG_KASAN_HW_TAGS is turned on by enabling the TCR_TBI1 bit in proc.S.

---
Change-Id: I91944903bc9c9c9044f0d50e74bcd6b9971d21ff

---
 arch/arm64/mm/proc.S | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
arch/arm64/mm/proc.S +10 -3
index 5ba7ac5e9c77..1687447dee7a 100644
@@ -40,9 +40,13 @@
 #define TCR_CACHE_FLAGS	TCR_IRGN_WBWA | TCR_ORGN_WBWA
 
 #ifdef CONFIG_KASAN_SW_TAGS
-#define TCR_KASAN_FLAGS TCR_TBI1
+#define TCR_KASAN_SW_FLAGS TCR_TBI1
 #else
-#define TCR_KASAN_FLAGS 0
+#define TCR_KASAN_SW_FLAGS 0
+#endif
+
+#ifdef CONFIG_KASAN_HW_TAGS
+#define TCR_KASAN_HW_FLAGS TCR_TBI1
 #endif
 
 /*
@@ -462,7 +466,7 @@ SYM_FUNC_START(__cpu_setup)
 	 */
 	mov_q	x10, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
 			TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
-			TCR_TBI0 | TCR_A1 | TCR_KASAN_FLAGS
+			TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS
 	tcr_clear_errata_bits x10, x9, x5
 
 #ifdef CONFIG_ARM64_VA_BITS_52
@@ -495,6 +499,9 @@ SYM_FUNC_START(__cpu_setup)
 	/* Update TCR_EL1 if MTE is supported (ID_AA64PFR1_EL1[11:8] > 1) */
 	cbz	mte_present, 1f
 	orr	x10, x10, #SYS_TCR_EL1_TCMA1
+#ifdef CONFIG_KASAN_HW_TAGS
+	orr	x10, x10, #TCR_KASAN_HW_FLAGS
+#endif
 1:
 	.unreq	mte_present
 #endif
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer
#62 lore raw

Re: [PATCH v2 28/37] arm64: kasan: Enable TBI EL1

Quoted 30 lines · Andrey Konovalov
arch/arm64/mm/proc.S +7 -3
index 5ba7ac5e9c77..1687447dee7a 100644
@@ -40,9 +40,13 @@
 #define TCR_CACHE_FLAGS	TCR_IRGN_WBWA | TCR_ORGN_WBWA
 
 #ifdef CONFIG_KASAN_SW_TAGS
-#define TCR_KASAN_FLAGS TCR_TBI1
+#define TCR_KASAN_SW_FLAGS TCR_TBI1
 #else
-#define TCR_KASAN_FLAGS 0
+#define TCR_KASAN_SW_FLAGS 0
+#endif
+
+#ifdef CONFIG_KASAN_HW_TAGS
+#define TCR_KASAN_HW_FLAGS TCR_TBI1
 #endif
 
 /*
@@ -462,7 +466,7 @@ SYM_FUNC_START(__cpu_setup)
 	 */
 	mov_q	x10, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
 			TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
-			TCR_TBI0 | TCR_A1 | TCR_KASAN_FLAGS
+			TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS
 	tcr_clear_errata_bits x10, x9, x5
 
 #ifdef CONFIG_ARM64_VA_BITS_52

arch/arm64/mm/proc.S SYM_FUNC_START(__cpu_setup) · line 504

Quoted 6 lines
arch/arm64/mm/proc.S +3 -0
@@ -495,6 +499,9 @@ SYM_FUNC_START(__cpu_setup)
 	/* Update TCR_EL1 if MTE is supported (ID_AA64PFR1_EL1[11:8] > 1) */
 	cbz	mte_present, 1f
 	orr	x10, x10, #SYS_TCR_EL1_TCMA1
+#ifdef CONFIG_KASAN_HW_TAGS
+	orr	x10, x10, #TCR_KASAN_HW_FLAGS
+#endif

That's fine in general but see my comment about refactoring the other patch touching this file, this will move around a bit.

Signature · 1 line
Catalin
#63 lore raw

PATCH v2 29/37 arm64: kasan: Align allocations for HW_TAGS

In reply to Andrey Konovalov

Hardware tag-based KASAN uses the memory tagging approach, which requires all allocations to be aligned to the memory granule size. Align the allocations to MTE_GRANULE_SIZE via ARCH_SLAB_MINALIGN when CONFIG_KASAN_HW_TAGS is enabled.

---
Change-Id: I51ebd3f9645e6330e5a92973bf7c86b62d632c2b

---
 arch/arm64/include/asm/cache.h | 3 +++
 1 file changed, 3 insertions(+)
arch/arm64/include/asm/cache.h +3 -0
index a4d1b5f771f6..acf6a5097cce 100644
@@ -6,6 +6,7 @@
 #define __ASM_CACHE_H
 
 #include <asm/cputype.h>
+#include <asm/mte-helpers.h>
 
 #define CTR_L1IP_SHIFT		14
 #define CTR_L1IP_MASK		3
@@ -50,6 +51,8 @@
 
 #ifdef CONFIG_KASAN_SW_TAGS
 #define ARCH_SLAB_MINALIGN	(1ULL << KASAN_SHADOW_SCALE_SHIFT)
+#elif defined(CONFIG_KASAN_HW_TAGS)
+#define ARCH_SLAB_MINALIGN	MTE_GRANULE_SIZE
 #endif
 
 #ifndef __ASSEMBLY__
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Reviewed-by on v2 29/37
#64 lore raw

Re: [PATCH v2 29/37] arm64: kasan: Align allocations for HW_TAGS

Quoted 7 lines · Andrey Konovalov

Hardware tag-based KASAN uses the memory tagging approach, which requires all allocations to be aligned to the memory granule size. Align the allocations to MTE_GRANULE_SIZE via ARCH_SLAB_MINALIGN when CONFIG_KASAN_HW_TAGS is enabled.

#65 lore raw

PATCH v2 30/37 kasan: define KASAN_GRANULE_SIZE for HW_TAGS

In reply to Andrey Konovalov

Hardware tag-based KASAN has granules of MTE_GRANULE_SIZE. Define KASAN_GRANULE_SIZE to MTE_GRANULE_SIZE for CONFIG_KASAN_HW_TAGS.

---
Change-Id: I5d1117e6a991cbca00d2cfb4ba66e8ae2d8f513a

---
 mm/kasan/kasan.h | 6 ++++++
 1 file changed, 6 insertions(+)
mm/kasan/kasan.h +6 -0
index 8b43fc163ed1..ba63d8a62968 100644
@@ -5,7 +5,13 @@
 #include <linux/kasan.h>
 #include <linux/stackdepot.h>
 
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 #define KASAN_GRANULE_SIZE	(1UL << KASAN_SHADOW_SCALE_SHIFT)
+#else
+#include <asm/mte-helpers.h>
+#define KASAN_GRANULE_SIZE	(MTE_GRANULE_SIZE)
+#endif
+
 #define KASAN_GRANULE_MASK	(KASAN_GRANULE_SIZE - 1)
 
 #define KASAN_TAG_KERNEL	0xFF /* native kernel pointers tag */
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#66 lore raw

PATCH v2 31/37 kasan, x86, s390: update undef CONFIG_KASAN

In reply to Andrey Konovalov

With the intoduction of hardware tag-based KASAN some kernel checks of this kind:

ifdef CONFIG_KASAN

will be updated to:

  if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)

x86 and s390 use a trick to #undef CONFIG_KASAN for some of the code that isn't linked with KASAN runtime and shouldn't have any KASAN annotations.

Also #undef CONFIG_KASAN_GENERIC with CONFIG_KASAN.

---
Change-Id: I2a622db0cb86a8feb60c30d8cb09190075be2a90

---
 arch/s390/boot/string.c         | 1 +
 arch/x86/boot/compressed/misc.h | 1 +
 2 files changed, 2 insertions(+)
arch/s390/boot/string.c +1 -0
index b11e8108773a..faccb33b462c 100644
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #undef CONFIG_KASAN
+#undef CONFIG_KASAN_GENERIC
 #include "../lib/string.c"
 
 int strncmp(const char *cs, const char *ct, size_t count)
arch/x86/boot/compressed/misc.h +1 -0
index 726e264410ff..2ac973983a8e 100644
@@ -12,6 +12,7 @@
 #undef CONFIG_PARAVIRT_XXL
 #undef CONFIG_PARAVIRT_SPINLOCKS
 #undef CONFIG_KASAN
+#undef CONFIG_KASAN_GENERIC
 
 /* cpu_feature_enabled() cannot be used this early */
 #define USE_EARLY_PGTABLE_L5
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#67 lore raw

Re: [PATCH v2 31/37] kasan, x86, s390: update undef CONFIG_KASAN

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

arch/s390/boot/string.c line 6

Quoted 13 lines
 arch/s390/boot/string.c         | 1 +
 arch/x86/boot/compressed/misc.h | 1 +
 2 files changed, 2 insertions(+)
arch/s390/boot/string.c +1 -0
index b11e8108773a..faccb33b462c 100644
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #undef CONFIG_KASAN
+#undef CONFIG_KASAN_GENERIC

Is CONFIG_KASAN still used to guard instrumented versions of functions?

It looks like #undef CONFIG_KASAN is no longer needed -- at least <linux/string.h> no longer mentions it.

arch/x86/boot/compressed/misc.h line 15

Quoted 12 lines
arch/s390/boot/string.c +0 -0
 #include "../lib/string.c"
 
 int strncmp(const char *cs, const char *ct, size_t count)
arch/x86/boot/compressed/misc.h +1 -0
index 726e264410ff..2ac973983a8e 100644
@@ -12,6 +12,7 @@
 #undef CONFIG_PARAVIRT_XXL
 #undef CONFIG_PARAVIRT_SPINLOCKS
 #undef CONFIG_KASAN
+#undef CONFIG_KASAN_GENERIC

Similar here; although it seems a little harder to check if CONFIG_KASAN is still needed. (Maybe could check the preprocessed output diffs?)

Quoted 6 lines
arch/x86/boot/compressed/misc.h +0 -0
 /* cpu_feature_enabled() cannot be used this early */
 #define USE_EARLY_PGTABLE_L5
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#68 lore raw

Re: [PATCH v2 31/37] kasan, x86, s390: update undef CONFIG_KASAN

Quoted 19 lines · Marco Elver

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

arch/s390/boot/string.c line 6

Quoted 13 lines
 arch/s390/boot/string.c         | 1 +
 arch/x86/boot/compressed/misc.h | 1 +
 2 files changed, 2 insertions(+)
arch/s390/boot/string.c +1 -0
index b11e8108773a..faccb33b462c 100644
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #undef CONFIG_KASAN
+#undef CONFIG_KASAN_GENERIC

Is CONFIG_KASAN still used to guard instrumented versions of functions?

Quoted 2 lines

It looks like #undef CONFIG_KASAN is no longer needed -- at least <linux/string.h> no longer mentions it.

I'm pretty sure this is still necessary (something didn't work when I forgot to make this change), but I'll check again.

#69 lore raw

Re: [PATCH v2 31/37] kasan, x86, s390: update undef CONFIG_KASAN

Quoted 21 lines · Andrey Konovalov
Quoted 19 lines · Marco Elver

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

arch/s390/boot/string.c line 6

Quoted 13 lines
 arch/s390/boot/string.c         | 1 +
 arch/x86/boot/compressed/misc.h | 1 +
 2 files changed, 2 insertions(+)
arch/s390/boot/string.c +1 -0
index b11e8108773a..faccb33b462c 100644
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #undef CONFIG_KASAN
+#undef CONFIG_KASAN_GENERIC

Is CONFIG_KASAN still used to guard instrumented versions of functions?

Quoted 5 lines
Quoted 2 lines

It looks like #undef CONFIG_KASAN is no longer needed -- at least <linux/string.h> no longer mentions it.

I'm pretty sure this is still necessary (something didn't work when I forgot to make this change), but I'll check again.

Yes, it still fails, as compressed code provides its own memmove.

#70 lore raw

PATCH v2 32/37 kasan, arm64: expand CONFIG_KASAN checks

In reply to Andrey Konovalov

Some #ifdef CONFIG_KASAN checks are only relevant for software KASAN modes (either related to shadow memory or compiler instrumentation). Expand those into CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS.

---
Change-Id: I91e661e2c1627783cb845d877c6371dfc8779505

---
 arch/arm64/Kconfig                 |  2 +-
 arch/arm64/Makefile                |  2 +-
 arch/arm64/include/asm/assembler.h |  2 +-
 arch/arm64/include/asm/memory.h    |  2 +-
 arch/arm64/include/asm/string.h    |  5 +++--
 arch/arm64/kernel/head.S           |  2 +-
 arch/arm64/kernel/image-vars.h     |  2 +-
 arch/arm64/mm/dump.c               |  6 +++---
 include/linux/kasan-checks.h       |  2 +-
 include/linux/kasan.h              |  7 ++++---
 include/linux/moduleloader.h       |  3 ++-
 include/linux/string.h             |  2 +-
 mm/ptdump.c                        | 13 ++++++++-----
 scripts/Makefile.lib               |  2 ++
 14 files changed, 30 insertions(+), 22 deletions(-)
arch/arm64/Kconfig +1 -1
index e875db8e1c86..c8e45870e993 100644
@@ -320,7 +320,7 @@ config BROKEN_GAS_INST
 
 config KASAN_SHADOW_OFFSET
 	hex
-	depends on KASAN
+	depends on KASAN_GENERIC || KASAN_SW_TAGS
 	default 0xdfffa00000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && !KASAN_SW_TAGS
 	default 0xdfffd00000000000 if ARM64_VA_BITS_47 && !KASAN_SW_TAGS
 	default 0xdffffe8000000000 if ARM64_VA_BITS_42 && !KASAN_SW_TAGS
arch/arm64/Makefile +1 -1
index 130569f90c54..888a9359f905 100644
@@ -137,7 +137,7 @@ TEXT_OFFSET := 0x0
 
 ifeq ($(CONFIG_KASAN_SW_TAGS), y)
 KASAN_SHADOW_SCALE_SHIFT := 4
-else
+else ifeq ($(CONFIG_KASAN_GENERIC), y)
 KASAN_SHADOW_SCALE_SHIFT := 3
 endif
 
arch/arm64/include/asm/assembler.h +1 -1
index 54d181177656..bc9ace1e5f3a 100644
@@ -464,7 +464,7 @@ USER(\label, ic	ivau, \tmp2)			// invalidate I line PoU
 #define NOKPROBE(x)
 #endif
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 #define EXPORT_SYMBOL_NOKASAN(name)
 #else
 #define EXPORT_SYMBOL_NOKASAN(name)	EXPORT_SYMBOL(name)
arch/arm64/include/asm/memory.h +1 -1
index 268a3b6cebd2..de9af7bea90d 100644
@@ -72,7 +72,7 @@
  * address space for the shadow region respectively. They can bloat the stack
  * significantly, so double the (minimum) stack size when they are in use.
  */
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 #define KASAN_SHADOW_OFFSET	_AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
 #define KASAN_SHADOW_END	((UL(1) << (64 - KASAN_SHADOW_SCALE_SHIFT)) \
 					+ KASAN_SHADOW_OFFSET)
arch/arm64/include/asm/string.h +3 -2
index b31e8e87a0db..3a3264ff47b9 100644
@@ -5,7 +5,7 @@
 #ifndef __ASM_STRING_H
 #define __ASM_STRING_H
 
-#ifndef CONFIG_KASAN
+#if !(defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 #define __HAVE_ARCH_STRRCHR
 extern char *strrchr(const char *, int c);
 
@@ -48,7 +48,8 @@ extern void *__memset(void *, int, __kernel_size_t);
 void memcpy_flushcache(void *dst, const void *src, size_t cnt);
 #endif
 
-#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
+#if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \
+	!defined(__SANITIZE_ADDRESS__)
 
 /*
  * For files that are not instrumented (e.g. mm/slub.c) we
arch/arm64/kernel/head.S +1 -1
index 037421c66b14..427ded9e68e8 100644
@@ -452,7 +452,7 @@ SYM_FUNC_START_LOCAL(__primary_switched)
 	bl	__pi_memset
 	dsb	ishst				// Make zero page visible to PTW
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	bl	kasan_early_init
 #endif
 #ifdef CONFIG_RANDOMIZE_BASE
arch/arm64/kernel/image-vars.h +1 -1
index 8982b68289b7..ed8d086d601c 100644
@@ -37,7 +37,7 @@ __efistub_strncmp		= __pi_strncmp;
 __efistub_strrchr		= __pi_strrchr;
 __efistub___clean_dcache_area_poc = __pi___clean_dcache_area_poc;
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 __efistub___memcpy		= __pi_memcpy;
 __efistub___memmove		= __pi_memmove;
 __efistub___memset		= __pi_memset;
arch/arm64/mm/dump.c +3 -3
index ba6d1d89f9b2..bf8ddeac5d8f 100644
@@ -29,7 +29,7 @@
 enum address_markers_idx {
 	PAGE_OFFSET_NR = 0,
 	PAGE_END_NR,
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	KASAN_START_NR,
 #endif
 };
@@ -37,7 +37,7 @@ enum address_markers_idx {
 static struct addr_marker address_markers[] = {
 	{ PAGE_OFFSET,			"Linear Mapping start" },
 	{ 0 /* PAGE_END */,		"Linear Mapping end" },
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	{ 0 /* KASAN_SHADOW_START */,	"Kasan shadow start" },
 	{ KASAN_SHADOW_END,		"Kasan shadow end" },
 #endif
@@ -381,7 +381,7 @@ void ptdump_check_wx(void)
 static int ptdump_init(void)
 {
 	address_markers[PAGE_END_NR].start_address = PAGE_END;
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	address_markers[KASAN_START_NR].start_address = KASAN_SHADOW_START;
 #endif
 	ptdump_initialize();
include/linux/kasan-checks.h +1 -1
index ac6aba632f2d..ca5e89fb10d3 100644
@@ -9,7 +9,7 @@
  * even in compilation units that selectively disable KASAN, but must use KASAN
  * to validate access to an address.   Never use these in header files!
  */
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 bool __kasan_check_read(const volatile void *p, unsigned int size);
 bool __kasan_check_write(const volatile void *p, unsigned int size);
 #else
include/linux/kasan.h +4 -3
index 894f4d9163ee..875bbcedd994 100644
@@ -232,7 +232,8 @@ static inline void kasan_release_vmalloc(unsigned long start,
 
 #endif /* CONFIG_KASAN_VMALLOC */
 
-#if defined(CONFIG_KASAN) && !defined(CONFIG_KASAN_VMALLOC)
+#if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \
+		!defined(CONFIG_KASAN_VMALLOC)
 
 /*
  * These functions provide a special case to support backing module
@@ -242,12 +243,12 @@ static inline void kasan_release_vmalloc(unsigned long start,
 int kasan_module_alloc(void *addr, size_t size);
 void kasan_free_shadow(const struct vm_struct *vm);
 
-#else /* CONFIG_KASAN && !CONFIG_KASAN_VMALLOC */
+#else /* (CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS) && !CONFIG_KASAN_VMALLOC */
 
 static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
 static inline void kasan_free_shadow(const struct vm_struct *vm) {}
 
-#endif /* CONFIG_KASAN && !CONFIG_KASAN_VMALLOC */
+#endif /* (CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS) && !CONFIG_KASAN_VMALLOC */
 
 #ifdef CONFIG_KASAN_INLINE
 void kasan_non_canonical_hook(unsigned long addr);
include/linux/moduleloader.h +2 -1
index 4fa67a8b2265..9e09d11ffe5b 100644
@@ -96,7 +96,8 @@ void module_arch_cleanup(struct module *mod);
 /* Any cleanup before freeing mod->module_init */
 void module_arch_freeing_init(struct module *mod);
 
-#if defined(CONFIG_KASAN) && !defined(CONFIG_KASAN_VMALLOC)
+#if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \
+		!defined(CONFIG_KASAN_VMALLOC)
 #include <linux/kasan.h>
 #define MODULE_ALIGN (PAGE_SIZE << KASAN_SHADOW_SCALE_SHIFT)
 #else
include/linux/string.h +1 -1
index 9b7a0632e87a..607322616363 100644
@@ -273,7 +273,7 @@ void __write_overflow(void) __compiletime_error("detected write beyond size of o
 
 #if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE)
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 extern void *__underlying_memchr(const void *p, int c, __kernel_size_t size) __RENAME(memchr);
 extern int __underlying_memcmp(const void *p, const void *q, __kernel_size_t size) __RENAME(memcmp);
 extern void *__underlying_memcpy(void *p, const void *q, __kernel_size_t size) __RENAME(memcpy);
mm/ptdump.c +8 -5
index ba88ec43ff21..4354c1422d57 100644
@@ -4,7 +4,7 @@
 #include <linux/ptdump.h>
 #include <linux/kasan.h>
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 /*
  * This is an optimization for KASAN=y case. Since all kasan page tables
  * eventually point to the kasan_early_shadow_page we could call note_page()
@@ -31,7 +31,8 @@ static int ptdump_pgd_entry(pgd_t *pgd, unsigned long addr,
 	struct ptdump_state *st = walk->private;
 	pgd_t val = READ_ONCE(*pgd);
 
-#if CONFIG_PGTABLE_LEVELS > 4 && defined(CONFIG_KASAN)
+#if CONFIG_PGTABLE_LEVELS > 4 && \
+		(defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 	if (pgd_page(val) == virt_to_page(lm_alias(kasan_early_shadow_p4d)))
 		return note_kasan_page_table(walk, addr);
 #endif
@@ -51,7 +52,8 @@ static int ptdump_p4d_entry(p4d_t *p4d, unsigned long addr,
 	struct ptdump_state *st = walk->private;
 	p4d_t val = READ_ONCE(*p4d);
 
-#if CONFIG_PGTABLE_LEVELS > 3 && defined(CONFIG_KASAN)
+#if CONFIG_PGTABLE_LEVELS > 3 && \
+		(defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 	if (p4d_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pud)))
 		return note_kasan_page_table(walk, addr);
 #endif
@@ -71,7 +73,8 @@ static int ptdump_pud_entry(pud_t *pud, unsigned long addr,
 	struct ptdump_state *st = walk->private;
 	pud_t val = READ_ONCE(*pud);
 
-#if CONFIG_PGTABLE_LEVELS > 2 && defined(CONFIG_KASAN)
+#if CONFIG_PGTABLE_LEVELS > 2 && \
+		(defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS))
 	if (pud_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pmd)))
 		return note_kasan_page_table(walk, addr);
 #endif
@@ -91,7 +94,7 @@ static int ptdump_pmd_entry(pmd_t *pmd, unsigned long addr,
 	struct ptdump_state *st = walk->private;
 	pmd_t val = READ_ONCE(*pmd);
 
-#if defined(CONFIG_KASAN)
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	if (pmd_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pte)))
 		return note_kasan_page_table(walk, addr);
 #endif
scripts/Makefile.lib +2 -0
index 3d599716940c..dc2d13c4455a 100644
@@ -148,10 +148,12 @@ endif
 # we don't want to check (depends on variables KASAN_SANITIZE_obj.o, KASAN_SANITIZE)
 #
 ifeq ($(CONFIG_KASAN),y)
+ifneq ($(CONFIG_KASAN_HW_TAGS),y)
 _c_flags += $(if $(patsubst n%,, \
 		$(KASAN_SANITIZE_$(basetarget).o)$(KASAN_SANITIZE)y), \
 		$(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE))
 endif
+endif
 
 ifeq ($(CONFIG_UBSAN),y)
 _c_flags += $(if $(patsubst n%,, \
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Reviewed-by on v2 32/37
#71 lore raw

Re: [PATCH v2 32/37] kasan, arm64: expand CONFIG_KASAN checks

Quoted 6 lines · Andrey Konovalov

Some #ifdef CONFIG_KASAN checks are only relevant for software KASAN modes (either related to shadow memory or compiler instrumentation). Expand those into CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS.

#72 lore raw

PATCH v2 33/37 kasan, arm64: implement HW_TAGS runtime

In reply to Andrey Konovalov

Provide implementation of KASAN functions required for the hardware tag-based mode. Those include core functions for memory and pointer tagging (tags_hw.c) and bug reporting (report_tags_hw.c). Also adapt common KASAN code to support the new mode.

---
Change-Id: I8a8689ba098174a4d0ef3f1d008178387c80ee1c

---
 arch/arm64/include/asm/memory.h   |  4 +-
 arch/arm64/kernel/setup.c         |  1 -
 include/linux/kasan.h             |  6 +--
 include/linux/mm.h                |  2 +-
 include/linux/page-flags-layout.h |  2 +-
 mm/kasan/Makefile                 |  5 ++
 mm/kasan/common.c                 | 14 +++---
 mm/kasan/kasan.h                  | 17 +++++--
 mm/kasan/report_tags_hw.c         | 47 +++++++++++++++++++
 mm/kasan/report_tags_sw.c         |  2 +-
 mm/kasan/shadow.c                 |  2 +-
 mm/kasan/tags_hw.c                | 78 +++++++++++++++++++++++++++++++
 mm/kasan/tags_sw.c                |  2 +-
 13 files changed, 162 insertions(+), 20 deletions(-)
 create mode 100644 mm/kasan/report_tags_hw.c
 create mode 100644 mm/kasan/tags_hw.c
arch/arm64/include/asm/memory.h +2 -2
index de9af7bea90d..b5d6b824c21c 100644
@@ -215,7 +215,7 @@ static inline unsigned long kaslr_offset(void)
 	(__force __typeof__(addr))__addr;				\
 })
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 #define __tag_shifted(tag)	((u64)(tag) << 56)
 #define __tag_reset(addr)	__untagged_addr(addr)
 #define __tag_get(addr)		(__u8)((u64)(addr) >> 56)
@@ -223,7 +223,7 @@ static inline unsigned long kaslr_offset(void)
 #define __tag_shifted(tag)	0UL
 #define __tag_reset(addr)	(addr)
 #define __tag_get(addr)		0
-#endif /* CONFIG_KASAN_SW_TAGS */
+#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
 
 static inline const void *__tag_set(const void *addr, u8 tag)
 {
arch/arm64/kernel/setup.c +0 -1
index 77c4c9bad1b8..5985be8af2c6 100644
@@ -358,7 +358,6 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
 	smp_init_cpus();
 	smp_build_mpidr_hash();
 
-	/* Init percpu seeds for random tags after cpus are set up. */
 	kasan_init_tags();
 
 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
include/linux/kasan.h +3 -3
index 875bbcedd994..613c9d38eee5 100644
@@ -184,7 +184,7 @@ static inline void kasan_record_aux_stack(void *ptr) {}
 
 #endif /* CONFIG_KASAN_GENERIC */
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 
 void kasan_init_tags(void);
 
@@ -193,7 +193,7 @@ void *kasan_reset_tag(const void *addr);
 bool kasan_report(unsigned long addr, size_t size,
 		bool is_write, unsigned long ip);
 
-#else /* CONFIG_KASAN_SW_TAGS */
+#else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
 
 static inline void kasan_init_tags(void) { }
 
@@ -202,7 +202,7 @@ static inline void *kasan_reset_tag(const void *addr)
 	return (void *)addr;
 }
 
-#endif /* CONFIG_KASAN_SW_TAGS */
+#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS*/
 
 #ifdef CONFIG_KASAN_VMALLOC
 
include/linux/mm.h +1 -1
index 4312c6c808e9..a3cac68c737c 100644
@@ -1411,7 +1411,7 @@ static inline bool cpupid_match_pid(struct task_struct *task, int cpupid)
 }
 #endif /* CONFIG_NUMA_BALANCING */
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 static inline u8 page_kasan_tag(const struct page *page)
 {
 	return (page->flags >> KASAN_TAG_PGSHIFT) & KASAN_TAG_MASK;
include/linux/page-flags-layout.h +1 -1
index e200eef6a7fd..7d4ec26d8a3e 100644
@@ -77,7 +77,7 @@
 #define LAST_CPUPID_SHIFT 0
 #endif
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 #define KASAN_TAG_WIDTH 8
 #else
 #define KASAN_TAG_WIDTH 0
mm/kasan/Makefile +5 -0
index 0789f9023884..f8cf9ba674a1 100644
@@ -10,9 +10,11 @@ CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_quarantine.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report_generic.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_report_tags_hw.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_report_tags_sw.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_shadow.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_tags_sw.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_tags_hw.o = $(CC_FLAGS_FTRACE)
 
 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
 # see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533
@@ -27,10 +29,13 @@ CFLAGS_init.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_quarantine.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report_tags_hw.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_report_tags_sw.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_shadow.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_tags_hw.o := $(CC_FLAGS_KASAN_RUNTIME)
 CFLAGS_tags_sw.o := $(CC_FLAGS_KASAN_RUNTIME)
 
 obj-$(CONFIG_KASAN) := common.o report.o
 obj-$(CONFIG_KASAN_GENERIC) += init.o generic.o report_generic.o shadow.o quarantine.o
 obj-$(CONFIG_KASAN_SW_TAGS) += init.o report_tags_sw.o shadow.o tags_sw.o
+obj-$(CONFIG_KASAN_HW_TAGS) += tags_hw.o report_tags_hw.o
mm/kasan/common.c +8 -6
index 41c7f1105eaa..412a23d1546b 100644
@@ -118,7 +118,7 @@ void kasan_free_pages(struct page *page, unsigned int order)
  */
 static inline unsigned int optimal_redzone(unsigned int object_size)
 {
-	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+	if (!IS_ENABLED(CONFIG_KASAN_GENERIC))
 		return 0;
 
 	return
@@ -183,14 +183,14 @@ size_t kasan_metadata_size(struct kmem_cache *cache)
 struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
 					const void *object)
 {
-	return (void *)object + cache->kasan_info.alloc_meta_offset;
+	return (void *)reset_tag(object) + cache->kasan_info.alloc_meta_offset;
 }
 
 struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
 				      const void *object)
 {
 	BUILD_BUG_ON(sizeof(struct kasan_free_meta) > 32);
-	return (void *)object + cache->kasan_info.free_meta_offset;
+	return (void *)reset_tag(object) + cache->kasan_info.free_meta_offset;
 }
 
 void kasan_poison_slab(struct page *page)
@@ -272,7 +272,8 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
 	alloc_info = get_alloc_info(cache, object);
 	__memset(alloc_info, 0, sizeof(*alloc_info));
 
-	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
+			IS_ENABLED(CONFIG_KASAN_HW_TAGS))
 		object = set_tag(object,
 				assign_tag(cache, object, true, false));
 
@@ -342,10 +343,11 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
 	redzone_end = round_up((unsigned long)object + cache->object_size,
 				KASAN_GRANULE_SIZE);
 
-	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
+			IS_ENABLED(CONFIG_KASAN_HW_TAGS))
 		tag = assign_tag(cache, object, false, keep_tag);
 
-	/* Tag is ignored in set_tag without CONFIG_KASAN_SW_TAGS */
+	/* Tag is ignored in set_tag without CONFIG_KASAN_SW/HW_TAGS */
 	kasan_unpoison_memory(set_tag(object, tag), size);
 	kasan_poison_memory((void *)redzone_start, redzone_end - redzone_start,
 		KASAN_KMALLOC_REDZONE);
mm/kasan/kasan.h +14 -3
index ba63d8a62968..1e9eda217be7 100644
@@ -152,6 +152,10 @@ struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
 struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
 					const void *object);
 
+void kasan_poison_memory(const void *address, size_t size, u8 value);
+
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
+
 static inline const void *kasan_shadow_to_mem(const void *shadow_addr)
 {
 	return (void *)(((unsigned long)shadow_addr - KASAN_SHADOW_OFFSET)
@@ -163,8 +167,6 @@ static inline bool addr_has_metadata(const void *addr)
 	return (addr >= kasan_shadow_to_mem((void *)KASAN_SHADOW_START));
 }
 
-void kasan_poison_memory(const void *address, size_t size, u8 value);
-
 /**
  * check_memory_region - Check memory region, and report if invalid access.
  * @addr: the accessed address
@@ -176,6 +178,15 @@ void kasan_poison_memory(const void *address, size_t size, u8 value);
 bool check_memory_region(unsigned long addr, size_t size, bool write,
 				unsigned long ret_ip);
 
+#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
+static inline bool addr_has_metadata(const void *addr)
+{
+	return true;
+}
+
+#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+
 bool check_invalid_free(void *addr);
 
 void *find_first_bad_addr(void *addr, size_t size);
@@ -212,7 +223,7 @@ static inline void quarantine_reduce(void) { }
 static inline void quarantine_remove_cache(struct kmem_cache *cache) { }
 #endif
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 
 void print_tags(u8 addr_tag, const void *addr);
 
mm/kasan/report_tags_hw.c new file +47 -0

47 lines not on this page · raw message

mm/kasan/report_tags_sw.c +1 -1
index 4060d0503462..b2894902bc47 100644
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * This file contains tag-based KASAN specific error reporting code.
+ * This file contains software tag-based KASAN specific error reporting code.
  *
  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
  * Author: Andrey Ryabinin <[email protected]>
mm/kasan/shadow.c +1 -1
index 4888084ecdfc..ca69726adf8f 100644
@@ -111,7 +111,7 @@ void kasan_unpoison_memory(const void *address, size_t size)
 
 		if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
 			*shadow = tag;
-		else
+		else /* CONFIG_KASAN_GENERIC */
 			*shadow = size & KASAN_GRANULE_MASK;
 	}
 }
mm/kasan/tags_hw.c new file +78 -0

78 lines not on this page · raw message

mm/kasan/tags_sw.c +1 -1
index feb42c1763b8..3df978b8d1d9 100644
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * This file contains core tag-based KASAN code.
+ * This file contains core software tag-based KASAN code.
  *
  * Copyright (c) 2018 Google, Inc.
  * Author: Andrey Konovalov <[email protected]>
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer Acked-by on v2 33/37
#73 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

Quoted 7 lines · Andrey Konovalov

Provide implementation of KASAN functions required for the hardware tag-based mode. Those include core functions for memory and pointer tagging (tags_hw.c) and bug reporting (report_tags_hw.c). Also adapt common KASAN code to support the new mode.

For the arm64 bits in this patch:

#74 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

In reply to Andrey Konovalov

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

Quoted 16 lines
 arch/arm64/include/asm/memory.h   |  4 +-
 arch/arm64/kernel/setup.c         |  1 -
 include/linux/kasan.h             |  6 +--
 include/linux/mm.h                |  2 +-
 include/linux/page-flags-layout.h |  2 +-
 mm/kasan/Makefile                 |  5 ++
 mm/kasan/common.c                 | 14 +++---
 mm/kasan/kasan.h                  | 17 +++++--
 mm/kasan/report_tags_hw.c         | 47 +++++++++++++++++++
 mm/kasan/report_tags_sw.c         |  2 +-
 mm/kasan/shadow.c                 |  2 +-
 mm/kasan/tags_hw.c                | 78 +++++++++++++++++++++++++++++++
 mm/kasan/tags_sw.c                |  2 +-
 13 files changed, 162 insertions(+), 20 deletions(-)
 create mode 100644 mm/kasan/report_tags_hw.c
 create mode 100644 mm/kasan/tags_hw.c

[...]

arch/arm64/kernel/setup.c void __init __no_sanitize_address setup_arch(char **cmdline_p) · line 361

Quoted 9 lines
arch/arm64/kernel/setup.c +0 -1
index 77c4c9bad1b8..5985be8af2c6 100644
@@ -358,7 +358,6 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
 	smp_init_cpus();
 	smp_build_mpidr_hash();
 
-	/* Init percpu seeds for random tags after cpus are set up. */

Why was the comment removed and not updated?

Quoted 3 lines
arch/arm64/kernel/setup.c +0 -0
 	kasan_init_tags();
 
 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
#75 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

Quoted 28 lines · Marco Elver

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

Quoted 16 lines
 arch/arm64/include/asm/memory.h   |  4 +-
 arch/arm64/kernel/setup.c         |  1 -
 include/linux/kasan.h             |  6 +--
 include/linux/mm.h                |  2 +-
 include/linux/page-flags-layout.h |  2 +-
 mm/kasan/Makefile                 |  5 ++
 mm/kasan/common.c                 | 14 +++---
 mm/kasan/kasan.h                  | 17 +++++--
 mm/kasan/report_tags_hw.c         | 47 +++++++++++++++++++
 mm/kasan/report_tags_sw.c         |  2 +-
 mm/kasan/shadow.c                 |  2 +-
 mm/kasan/tags_hw.c                | 78 +++++++++++++++++++++++++++++++
 mm/kasan/tags_sw.c                |  2 +-
 13 files changed, 162 insertions(+), 20 deletions(-)
 create mode 100644 mm/kasan/report_tags_hw.c
 create mode 100644 mm/kasan/tags_hw.c

[...]

Quoted 8 lines
arch/arm64/kernel/setup.c +0 -0
index 77c4c9bad1b8..5985be8af2c6 100644
@@ -358,7 +358,6 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
      smp_init_cpus();
      smp_build_mpidr_hash();
 
Quoted 3 lines

arch/arm64/kernel/setup.c void __init __no_sanitize_address setup_arch(char **cmdline_p) · line 361

Quoted 1 line
arch/arm64/kernel/setup.c +0 -1
-     /* Init percpu seeds for random tags after cpus are set up. */

Why was the comment removed and not updated?

Will fix in v3, thanks!

#76 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

In reply to Andrey Konovalov

[ Sorry for the additional email on this patch; trying to consolidate
comments now. ]

Quoted 26 lines · Andrey Konovalov

Provide implementation of KASAN functions required for the hardware tag-based mode. Those include core functions for memory and pointer tagging (tags_hw.c) and bug reporting (report_tags_hw.c). Also adapt common KASAN code to support the new mode.

---
Change-Id: I8a8689ba098174a4d0ef3f1d008178387c80ee1c

---
 arch/arm64/include/asm/memory.h   |  4 +-
 arch/arm64/kernel/setup.c         |  1 -
 include/linux/kasan.h             |  6 +--
 include/linux/mm.h                |  2 +-
 include/linux/page-flags-layout.h |  2 +-
 mm/kasan/Makefile                 |  5 ++
 mm/kasan/common.c                 | 14 +++---
 mm/kasan/kasan.h                  | 17 +++++--
 mm/kasan/report_tags_hw.c         | 47 +++++++++++++++++++
 mm/kasan/report_tags_sw.c         |  2 +-
 mm/kasan/shadow.c                 |  2 +-
 mm/kasan/tags_hw.c                | 78 +++++++++++++++++++++++++++++++
 mm/kasan/tags_sw.c                |  2 +-
 13 files changed, 162 insertions(+), 20 deletions(-)
 create mode 100644 mm/kasan/report_tags_hw.c
 create mode 100644 mm/kasan/tags_hw.c

[...]

Quoted 31 lines
mm/kasan/common.c +3 -3
index 41c7f1105eaa..412a23d1546b 100644
@@ -118,7 +118,7 @@ void kasan_free_pages(struct page *page, unsigned int order)
  */
 static inline unsigned int optimal_redzone(unsigned int object_size)
 {
-	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+	if (!IS_ENABLED(CONFIG_KASAN_GENERIC))
 		return 0;
 
 	return
@@ -183,14 +183,14 @@ size_t kasan_metadata_size(struct kmem_cache *cache)
 struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
 					const void *object)
 {
-	return (void *)object + cache->kasan_info.alloc_meta_offset;
+	return (void *)reset_tag(object) + cache->kasan_info.alloc_meta_offset;
 }
 
 struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
 				      const void *object)
 {
 	BUILD_BUG_ON(sizeof(struct kasan_free_meta) > 32);
-	return (void *)object + cache->kasan_info.free_meta_offset;
+	return (void *)reset_tag(object) + cache->kasan_info.free_meta_offset;
 }
 
 void kasan_poison_slab(struct page *page)

mm/kasan/common.c void * __must_check kasan_init_slab_obj(struct kmem_cache *cache, · line 273

Quoted 2 lines
mm/kasan/common.c +0 -0
@@ -272,7 +272,8 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
 	alloc_info = get_alloc_info(cache, object);
 	__memset(alloc_info, 0, sizeof(*alloc_info));

Suggested edit below (assuming the line-break wasn't intentional; this should still be within checkpatch.pl's 100 col limit):
------
- if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
- IS_ENABLED(CONFIG_KASAN_HW_TAGS))
+ if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) || IS_ENABLED(CONFIG_KASAN_HW_TAGS))
object = set_tag(object,
assign_tag(cache, object, true, false));

@@ -343,8 +342,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
redzone_end = round_up((unsigned long)object + cache->object_size,
KASAN_GRANULE_SIZE);

- if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
- IS_ENABLED(CONFIG_KASAN_HW_TAGS))
+ if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) || IS_ENABLED(CONFIG_KASAN_HW_TAGS))
tag = assign_tag(cache, object, false, keep_tag);
------

Quoted 20 lines
mm/kasan/common.c +5 -3
-	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
+			IS_ENABLED(CONFIG_KASAN_HW_TAGS))
 		object = set_tag(object,
 				assign_tag(cache, object, true, false));
 
@@ -342,10 +343,11 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
 	redzone_end = round_up((unsigned long)object + cache->object_size,
 				KASAN_GRANULE_SIZE);
 
-	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+	if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
+			IS_ENABLED(CONFIG_KASAN_HW_TAGS))
 		tag = assign_tag(cache, object, false, keep_tag);
 
-	/* Tag is ignored in set_tag without CONFIG_KASAN_SW_TAGS */
+	/* Tag is ignored in set_tag without CONFIG_KASAN_SW/HW_TAGS */
 	kasan_unpoison_memory(set_tag(object, tag), size);
 	kasan_poison_memory((void *)redzone_start, redzone_end - redzone_start,
 		KASAN_KMALLOC_REDZONE);

[...]

Quoted 7 lines
mm/kasan/report_tags_hw.c new file +1 -0
new file mode 100644
index 000000000000..c2f73c46279a
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0

mm/kasan/report_tags_hw.c line 11

Quoted 10 lines
mm/kasan/report_tags_hw.c new file +10 -0
+/*
+ * This file contains hardware tag-based KASAN specific error reporting code.
+ *
+ * Copyright (c) 2020 Google, Inc.
+ * Author: Andrey Konovalov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *

I do not think we put the "This program is ..." preamble in new files anymore. It should be covered by SPDX tag above.

Quoted 8 lines
mm/kasan/report_tags_hw.c new file +8 -0
+ */
+
+#include <linux/kasan.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>

[...]

Quoted 20 lines
mm/kasan/shadow.c +1 -1
index 4888084ecdfc..ca69726adf8f 100644
@@ -111,7 +111,7 @@ void kasan_unpoison_memory(const void *address, size_t size)
 
 		if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
 			*shadow = tag;
-		else
+		else /* CONFIG_KASAN_GENERIC */
 			*shadow = size & KASAN_GRANULE_MASK;
 	}
 }
mm/kasan/tags_hw.c new file +1 -0
new file mode 100644
index 000000000000..c93d43379e39
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0

mm/kasan/tags_hw.c line 11

Quoted 10 lines
mm/kasan/tags_hw.c new file +10 -0
+/*
+ * This file contains core hardware tag-based KASAN code.
+ *
+ * Copyright (c) 2020 Google, Inc.
+ * Author: Andrey Konovalov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *

I do not think we put the "This program is ..." preamble in new files anymore. It should be covered by SPDX tag above.

Quoted 11 lines
mm/kasan/tags_hw.c new file +11 -0
+ */
+
+#include <linux/kasan.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "kasan.h"
+

mm/kasan/tags_hw.c line 32

Quoted 10 lines
mm/kasan/tags_hw.c new file +10 -0
+void kasan_init_tags(void)
+{
+	init_tags(KASAN_TAG_MAX);
+}
+
+void *kasan_reset_tag(const void *addr)
+{
+	return reset_tag(addr);
+}
+

To help readability, would this edit be ok?
------
void kasan_poison_memory(const void *address, size_t size, u8 value) {
- set_mem_tag_range(reset_tag(address),
- round_up(size, KASAN_GRANULE_SIZE), value);
+ set_mem_tag_range(reset_tag(address), round_up(size, KASAN_GRANULE_SIZE), value);
}

void kasan_unpoison_memory(const void *address, size_t size) {
- set_mem_tag_range(reset_tag(address),
- round_up(size, KASAN_GRANULE_SIZE), get_tag(address));
+ set_mem_tag_range(reset_tag(address), round_up(size, KASAN_GRANULE_SIZE), get_tag(address));
}
------

Quoted 12 lines
mm/kasan/tags_hw.c new file +12 -0
+void kasan_poison_memory(const void *address, size_t size, u8 value)
+{
+	set_mem_tag_range(reset_tag(address),
+		round_up(size, KASAN_GRANULE_SIZE), value);
+}
+
+void kasan_unpoison_memory(const void *address, size_t size)
+{
+	set_mem_tag_range(reset_tag(address),
+		round_up(size, KASAN_GRANULE_SIZE), get_tag(address));
+}
+

mm/kasan/tags_hw.c line 54

Quoted 10 lines
mm/kasan/tags_hw.c new file +10 -0
+u8 random_tag(void)
+{
+	return get_random_tag();
+}
+
+bool check_invalid_free(void *addr)
+{
+	u8 ptr_tag = get_tag(addr);
+	u8 mem_tag = get_mem_tag(addr);
+

Why not just:
------
- if (shadow_byte == KASAN_TAG_INVALID)
- return true;
- if (tag != KASAN_TAG_KERNEL && tag != shadow_byte)
- return true;
- return false;
+ return shadow_byte == KASAN_TAG_INVALID ||
+ (tag != KASAN_TAG_KERNEL && tag != shadow_byte);
}
------

mm/kasan/tags_hw.c line 61

Quoted 7 lines
mm/kasan/tags_hw.c new file +7 -0
+	if (mem_tag == KASAN_TAG_INVALID)
+		return true;
+	if (ptr_tag != KASAN_TAG_KERNEL && ptr_tag != mem_tag)
+		return true;
+	return false;
+}
+

Thanks,
-- Marco

#77 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

Quoted 245 lines · Marco Elver

[ Sorry for the additional email on this patch; trying to consolidate
comments now. ]

Quoted 26 lines · Andrey Konovalov

Provide implementation of KASAN functions required for the hardware tag-based mode. Those include core functions for memory and pointer tagging (tags_hw.c) and bug reporting (report_tags_hw.c). Also adapt common KASAN code to support the new mode.

---
Change-Id: I8a8689ba098174a4d0ef3f1d008178387c80ee1c

---
 arch/arm64/include/asm/memory.h   |  4 +-
 arch/arm64/kernel/setup.c         |  1 -
 include/linux/kasan.h             |  6 +--
 include/linux/mm.h                |  2 +-
 include/linux/page-flags-layout.h |  2 +-
 mm/kasan/Makefile                 |  5 ++
 mm/kasan/common.c                 | 14 +++---
 mm/kasan/kasan.h                  | 17 +++++--
 mm/kasan/report_tags_hw.c         | 47 +++++++++++++++++++
 mm/kasan/report_tags_sw.c         |  2 +-
 mm/kasan/shadow.c                 |  2 +-
 mm/kasan/tags_hw.c                | 78 +++++++++++++++++++++++++++++++
 mm/kasan/tags_sw.c                |  2 +-
 13 files changed, 162 insertions(+), 20 deletions(-)
 create mode 100644 mm/kasan/report_tags_hw.c
 create mode 100644 mm/kasan/tags_hw.c

[...]

Quoted 31 lines
mm/kasan/common.c +3 -3
index 41c7f1105eaa..412a23d1546b 100644
@@ -118,7 +118,7 @@ void kasan_free_pages(struct page *page, unsigned int order)
  */
 static inline unsigned int optimal_redzone(unsigned int object_size)
 {
-     if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+     if (!IS_ENABLED(CONFIG_KASAN_GENERIC))
              return 0;
 
      return
@@ -183,14 +183,14 @@ size_t kasan_metadata_size(struct kmem_cache *cache)
 struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
                                      const void *object)
 {
-     return (void *)object + cache->kasan_info.alloc_meta_offset;
+     return (void *)reset_tag(object) + cache->kasan_info.alloc_meta_offset;
 }
 
 struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
                                    const void *object)
 {
      BUILD_BUG_ON(sizeof(struct kasan_free_meta) > 32);
-     return (void *)object + cache->kasan_info.free_meta_offset;
+     return (void *)reset_tag(object) + cache->kasan_info.free_meta_offset;
 }
 
 void kasan_poison_slab(struct page *page)

mm/kasan/common.c void * __must_check kasan_init_slab_obj(struct kmem_cache *cache, · line 273

Quoted 2 lines
mm/kasan/common.c +0 -0
@@ -272,7 +272,8 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
      alloc_info = get_alloc_info(cache, object);
      __memset(alloc_info, 0, sizeof(*alloc_info));

Suggested edit below (assuming the line-break wasn't intentional; this should still be within checkpatch.pl's 100 col limit):
------

mm/kasan/common.c +2 -5
-       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
-                       IS_ENABLED(CONFIG_KASAN_HW_TAGS))
+       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) || IS_ENABLED(CONFIG_KASAN_HW_TAGS))
                object = set_tag(object,
                                assign_tag(cache, object, true, false));
@@ -343,8 +342,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
        redzone_end = round_up((unsigned long)object + cache->object_size,
                                KASAN_GRANULE_SIZE);
 
-       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
-                       IS_ENABLED(CONFIG_KASAN_HW_TAGS))
+       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) || IS_ENABLED(CONFIG_KASAN_HW_TAGS))
                tag = assign_tag(cache, object, false, keep_tag);
------
 
Quoted 20 lines
mm/kasan/common.c +5 -3
-     if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+     if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
+                     IS_ENABLED(CONFIG_KASAN_HW_TAGS))
              object = set_tag(object,
                              assign_tag(cache, object, true, false));
 
@@ -342,10 +343,11 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
      redzone_end = round_up((unsigned long)object + cache->object_size,
                              KASAN_GRANULE_SIZE);
 
-     if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+     if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) ||
+                     IS_ENABLED(CONFIG_KASAN_HW_TAGS))
              tag = assign_tag(cache, object, false, keep_tag);
 
-     /* Tag is ignored in set_tag without CONFIG_KASAN_SW_TAGS */
+     /* Tag is ignored in set_tag without CONFIG_KASAN_SW/HW_TAGS */
      kasan_unpoison_memory(set_tag(object, tag), size);
      kasan_poison_memory((void *)redzone_start, redzone_end - redzone_start,
              KASAN_KMALLOC_REDZONE);

[...]

Quoted 7 lines
mm/kasan/report_tags_hw.c new file +1 -0
new file mode 100644
index 000000000000..c2f73c46279a
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0

mm/kasan/report_tags_hw.c line 11

Quoted 10 lines
mm/kasan/report_tags_hw.c new file +10 -0
+/*
+ * This file contains hardware tag-based KASAN specific error reporting code.
+ *
+ * Copyright (c) 2020 Google, Inc.
+ * Author: Andrey Konovalov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *

I do not think we put the "This program is ..." preamble in new files anymore. It should be covered by SPDX tag above.

Quoted 8 lines
mm/kasan/report_tags_hw.c new file +8 -0
+ */
+
+#include <linux/kasan.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>

[...]

Quoted 20 lines
mm/kasan/shadow.c +1 -1
index 4888084ecdfc..ca69726adf8f 100644
@@ -111,7 +111,7 @@ void kasan_unpoison_memory(const void *address, size_t size)
 
              if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
                      *shadow = tag;
-             else
+             else /* CONFIG_KASAN_GENERIC */
                      *shadow = size & KASAN_GRANULE_MASK;
      }
 }
mm/kasan/tags_hw.c new file +1 -0
new file mode 100644
index 000000000000..c93d43379e39
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0

mm/kasan/tags_hw.c line 11

Quoted 10 lines
mm/kasan/tags_hw.c new file +10 -0
+/*
+ * This file contains core hardware tag-based KASAN code.
+ *
+ * Copyright (c) 2020 Google, Inc.
+ * Author: Andrey Konovalov <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *

I do not think we put the "This program is ..." preamble in new files anymore. It should be covered by SPDX tag above.

Quoted 11 lines
mm/kasan/tags_hw.c new file +11 -0
+ */
+
+#include <linux/kasan.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "kasan.h"
+

mm/kasan/tags_hw.c line 32

Quoted 10 lines
mm/kasan/tags_hw.c new file +10 -0
+void kasan_init_tags(void)
+{
+     init_tags(KASAN_TAG_MAX);
+}
+
+void *kasan_reset_tag(const void *addr)
+{
+     return reset_tag(addr);
+}
+

To help readability, would this edit be ok?
------
void kasan_poison_memory(const void *address, size_t size, u8 value) {

(quoted diff) +2 -5
-       set_mem_tag_range(reset_tag(address),
-               round_up(size, KASAN_GRANULE_SIZE), value);
+       set_mem_tag_range(reset_tag(address), round_up(size, KASAN_GRANULE_SIZE), value);
 }
 
 void kasan_unpoison_memory(const void *address, size_t size)
 {
-       set_mem_tag_range(reset_tag(address),
-               round_up(size, KASAN_GRANULE_SIZE), get_tag(address));
+       set_mem_tag_range(reset_tag(address), round_up(size, KASAN_GRANULE_SIZE), get_tag(address));
 }
------
Quoted 13 lines
mm/kasan/tags_hw.c new file +12 -0
+void kasan_poison_memory(const void *address, size_t size, u8 value)
+{
+     set_mem_tag_range(reset_tag(address),
+             round_up(size, KASAN_GRANULE_SIZE), value);
+}
+
+void kasan_unpoison_memory(const void *address, size_t size)
+{
+     set_mem_tag_range(reset_tag(address),
+             round_up(size, KASAN_GRANULE_SIZE), get_tag(address));
+}
+

mm/kasan/tags_hw.c line 54

Quoted 10 lines
mm/kasan/tags_hw.c new file +10 -0
+u8 random_tag(void)
+{
+     return get_random_tag();
+}
+
+bool check_invalid_free(void *addr)
+{
+     u8 ptr_tag = get_tag(addr);
+     u8 mem_tag = get_mem_tag(addr);
+

Why not just:
------

(quoted diff) +2 -6
-       if (shadow_byte == KASAN_TAG_INVALID)
-               return true;
-       if (tag != KASAN_TAG_KERNEL && tag != shadow_byte)
-               return true;
-       return false;
+       return shadow_byte == KASAN_TAG_INVALID ||
+              (tag != KASAN_TAG_KERNEL && tag != shadow_byte);
 }
------
Quoted 7 lines

mm/kasan/tags_hw.c line 61

Quoted 7 lines
mm/kasan/tags_hw.c new file +7 -0
+     if (mem_tag == KASAN_TAG_INVALID)
+             return true;
+     if (ptr_tag != KASAN_TAG_KERNEL && ptr_tag != mem_tag)
+             return true;
+     return false;
+}
+

Will fix all these in v3, thanks!

#78 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

In reply to Andrey Konovalov
Quoted 23 lines · Andrey Konovalov
include/linux/kasan.h +2 -2
index 875bbcedd994..613c9d38eee5 100644
@@ -184,7 +184,7 @@ static inline void kasan_record_aux_stack(void *ptr) {}
 
 #endif /* CONFIG_KASAN_GENERIC */
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 
 void kasan_init_tags(void);
 
@@ -193,7 +193,7 @@ void *kasan_reset_tag(const void *addr);
 bool kasan_report(unsigned long addr, size_t size,
 		bool is_write, unsigned long ip);
 
-#else /* CONFIG_KASAN_SW_TAGS */
+#else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
 
 static inline void kasan_init_tags(void) { }
 

include/linux/kasan.h static inline void *kasan_reset_tag(const void *addr) · line 207

Quoted 7 lines
include/linux/kasan.h +1 -1
@@ -202,7 +202,7 @@ static inline void *kasan_reset_tag(const void *addr)
 	return (void *)addr;
 }
 
-#endif /* CONFIG_KASAN_SW_TAGS */
+#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS*/
 
 #ifdef CONFIG_KASAN_VMALLOC

It's not visible by looking at this diff, but there is some #ifdef-redundancy that I do not understand where it came from.

This is what I have to fix it:

include/linux/kasan.h +6 -12
index 613c9d38eee5..80a0e5b11f2b 100644
@@ -40,6 +40,12 @@ static inline void *kasan_mem_to_shadow(const void *addr)
 int kasan_add_zero_shadow(void *start, unsigned long size);
 void kasan_remove_zero_shadow(void *start, unsigned long size);
 
+/* Enable reporting bugs after kasan_disable_current() */
+extern void kasan_enable_current(void);
+
+/* Disable reporting bugs for current task */
+extern void kasan_disable_current(void);
+
 #else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
 static inline int kasan_add_zero_shadow(void *start, unsigned long size)
@@ -50,18 +56,6 @@ static inline void kasan_remove_zero_shadow(void *start,
 					unsigned long size)
 {}
 
-#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
-
-#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
-
-/* Enable reporting bugs after kasan_disable_current() */
-extern void kasan_enable_current(void);
-
-/* Disable reporting bugs for current task */
-extern void kasan_disable_current(void);
-
-#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
-
 static inline void kasan_enable_current(void) {}
 static inline void kasan_disable_current(void) {}
 
#79 lore raw

Re: [PATCH v2 33/37] kasan, arm64: implement HW_TAGS runtime

Quoted 64 lines · Marco Elver
Quoted 23 lines · Andrey Konovalov
include/linux/kasan.h +2 -2
index 875bbcedd994..613c9d38eee5 100644
@@ -184,7 +184,7 @@ static inline void kasan_record_aux_stack(void *ptr) {}
 
 #endif /* CONFIG_KASAN_GENERIC */
 
-#ifdef CONFIG_KASAN_SW_TAGS
+#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
 
 void kasan_init_tags(void);
 
@@ -193,7 +193,7 @@ void *kasan_reset_tag(const void *addr);
 bool kasan_report(unsigned long addr, size_t size,
              bool is_write, unsigned long ip);
 
-#else /* CONFIG_KASAN_SW_TAGS */
+#else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
 
 static inline void kasan_init_tags(void) { }
 

include/linux/kasan.h static inline void *kasan_reset_tag(const void *addr) · line 207

Quoted 7 lines
include/linux/kasan.h +1 -1
@@ -202,7 +202,7 @@ static inline void *kasan_reset_tag(const void *addr)
      return (void *)addr;
 }
 
-#endif /* CONFIG_KASAN_SW_TAGS */
+#endif /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS*/
 
 #ifdef CONFIG_KASAN_VMALLOC

It's not visible by looking at this diff, but there is some #ifdef-redundancy that I do not understand where it came from.

This is what I have to fix it:

include/linux/kasan.h +6 -4
index 613c9d38eee5..80a0e5b11f2b 100644
@@ -40,6 +40,12 @@ static inline void *kasan_mem_to_shadow(const void *addr)
 int kasan_add_zero_shadow(void *start, unsigned long size);
 void kasan_remove_zero_shadow(void *start, unsigned long size);
 
+/* Enable reporting bugs after kasan_disable_current() */
+extern void kasan_enable_current(void);
+
+/* Disable reporting bugs for current task */
+extern void kasan_disable_current(void);
+
 #else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
 static inline int kasan_add_zero_shadow(void *start, unsigned long size)
@@ -50,18 +56,6 @@ static inline void kasan_remove_zero_shadow(void *start,
                                        unsigned long size)
 {}
 
-#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
-
-#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
-

include/linux/kasan.h static inline void kasan_remove_zero_shadow(void *start, · line 60

Quoted 10 lines
include/linux/kasan.h +0 -8
-/* Enable reporting bugs after kasan_disable_current() */
-extern void kasan_enable_current(void);
-
-/* Disable reporting bugs for current task */
-extern void kasan_disable_current(void);
-
-#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
-
 static inline void kasan_enable_current(void) {}
 static inline void kasan_disable_current(void) {}

Oh yeah, I'll fix this, thanks!

#80 lore raw

PATCH v2 34/37 kasan, arm64: print report from tag fault handler

In reply to Andrey Konovalov

Add error reporting for hardware tag-based KASAN. When CONFIG_KASAN_HW_TAGS is enabled, print KASAN report from the arm64 tag fault handler.

SAS bits aren't set in ESR for all faults reported in EL1, so it's impossible to find out the size of the access the caused the fault. Adapt KASAN reporting code to handle this case.

---
Change-Id: I3780fe7db6e075dff2937d3d8508f55c9322b095

---
 arch/arm64/mm/fault.c | 19 +++++++++++++------
 mm/kasan/report.c     | 11 ++++++++---
 2 files changed, 21 insertions(+), 9 deletions(-)
arch/arm64/mm/fault.c +13 -6
index cdc23662691c..ac79819317f2 100644
@@ -14,6 +14,7 @@
 #include <linux/mm.h>
 #include <linux/hardirq.h>
 #include <linux/init.h>
+#include <linux/kasan.h>
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
 #include <linux/page-flags.h>
@@ -295,17 +296,23 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
 	do_exit(SIGKILL);
 }
 
+#ifdef CONFIG_KASAN_HW_TAGS
 static void report_tag_fault(unsigned long addr, unsigned int esr,
 			     struct pt_regs *regs)
 {
-	bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
+	bool is_write  = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
 
-	pr_alert("Memory Tagging Extension Fault in %pS\n", (void *)regs->pc);
-	pr_alert("  %s at address %lx\n", is_write ? "Write" : "Read", addr);
-	pr_alert("  Pointer tag: [%02x], memory tag: [%02x]\n",
-			mte_get_ptr_tag(addr),
-			mte_get_mem_tag((void *)addr));
+	/*
+	 * SAS bits aren't set for all faults reported in EL1, so we can't
+	 * find out access size.
+	 */
+	kasan_report(addr, 0, is_write, regs->pc);
 }
+#else
+/* Tag faults aren't enabled without CONFIG_KASAN_HW_TAGS. */
+static inline void report_tag_fault(unsigned long addr, unsigned int esr,
+				    struct pt_regs *regs) { }
+#endif
 
 static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 			      struct pt_regs *regs)
mm/kasan/report.c +8 -3
index c904edab33b8..34ef81736d73 100644
@@ -65,9 +65,14 @@ static void print_error_description(struct kasan_access_info *info)
 {
 	pr_err("BUG: KASAN: %s in %pS\n",
 		get_bug_type(info), (void *)info->ip);
-	pr_err("%s of size %zu at addr %px by task %s/%d\n",
-		info->is_write ? "Write" : "Read", info->access_size,
-		info->access_addr, current->comm, task_pid_nr(current));
+	if (info->access_size)
+		pr_err("%s of size %zu at addr %px by task %s/%d\n",
+			info->is_write ? "Write" : "Read", info->access_size,
+			info->access_addr, current->comm, task_pid_nr(current));
+	else
+		pr_err("%s at addr %px by task %s/%d\n",
+			info->is_write ? "Write" : "Read",
+			info->access_addr, current->comm, task_pid_nr(current));
 }
 
 static DEFINE_SPINLOCK(report_lock);
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
maintainer
#81 lore raw

Re: [PATCH v2 34/37] kasan, arm64: print report from tag fault handler

Quoted 29 lines · Andrey Konovalov
arch/arm64/mm/fault.c +4 -6
index cdc23662691c..ac79819317f2 100644
@@ -14,6 +14,7 @@
 #include <linux/mm.h>
 #include <linux/hardirq.h>
 #include <linux/init.h>
+#include <linux/kasan.h>
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
 #include <linux/page-flags.h>
@@ -295,17 +296,23 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
 	do_exit(SIGKILL);
 }
 
+#ifdef CONFIG_KASAN_HW_TAGS
 static void report_tag_fault(unsigned long addr, unsigned int esr,
 			     struct pt_regs *regs)
 {
-	bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
+	bool is_write  = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
 
-	pr_alert("Memory Tagging Extension Fault in %pS\n", (void *)regs->pc);
-	pr_alert("  %s at address %lx\n", is_write ? "Write" : "Read", addr);
-	pr_alert("  Pointer tag: [%02x], memory tag: [%02x]\n",
-			mte_get_ptr_tag(addr),
-			mte_get_mem_tag((void *)addr));
+	/*

arch/arm64/mm/fault.c static void die_kernel_fault(const char *msg, unsigned long addr, · line 315

Quoted 10 lines
arch/arm64/mm/fault.c +9 -0
+	 * SAS bits aren't set for all faults reported in EL1, so we can't
+	 * find out access size.
+	 */
+	kasan_report(addr, 0, is_write, regs->pc);
 }
+#else
+/* Tag faults aren't enabled without CONFIG_KASAN_HW_TAGS. */
+static inline void report_tag_fault(unsigned long addr, unsigned int esr,
+				    struct pt_regs *regs) { }
+#endif

So is there a point in introducing this function in an earlier patch, just to remove its content here?

Signature · 1 line
Catalin
#82 lore raw

Re: [PATCH v2 34/37] kasan, arm64: print report from tag fault handler

Quoted 36 lines · Catalin Marinas
Quoted 34 lines · Andrey Konovalov
arch/arm64/mm/fault.c +8 -6
index cdc23662691c..ac79819317f2 100644
@@ -14,6 +14,7 @@
 #include <linux/mm.h>
 #include <linux/hardirq.h>
 #include <linux/init.h>
+#include <linux/kasan.h>
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
 #include <linux/page-flags.h>
@@ -295,17 +296,23 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
      do_exit(SIGKILL);
 }
 
+#ifdef CONFIG_KASAN_HW_TAGS
 static void report_tag_fault(unsigned long addr, unsigned int esr,
                           struct pt_regs *regs)
 {
-     bool is_write = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
+     bool is_write  = ((esr & ESR_ELx_WNR) >> ESR_ELx_WNR_SHIFT) != 0;
 
-     pr_alert("Memory Tagging Extension Fault in %pS\n", (void *)regs->pc);
-     pr_alert("  %s at address %lx\n", is_write ? "Write" : "Read", addr);
-     pr_alert("  Pointer tag: [%02x], memory tag: [%02x]\n",
-                     mte_get_ptr_tag(addr),
-                     mte_get_mem_tag((void *)addr));
+     /*
+      * SAS bits aren't set for all faults reported in EL1, so we can't
+      * find out access size.
+      */
+     kasan_report(addr, 0, is_write, regs->pc);
 }
Quoted 8 lines

arch/arm64/mm/fault.c static void die_kernel_fault(const char *msg, unsigned long addr, · line 315

Quoted 5 lines
arch/arm64/mm/fault.c +5 -0
+#else
+/* Tag faults aren't enabled without CONFIG_KASAN_HW_TAGS. */
+static inline void report_tag_fault(unsigned long addr, unsigned int esr,
+                                 struct pt_regs *regs) { }
+#endif

So is there a point in introducing this function in an earlier patch, just to remove its content here?

I added it to make the first patch somewhat self-consistent. But we can drop it in v3 if you think it's not needed.

#83 lore raw

PATCH v2 35/37 kasan, slub: reset tags when accessing metadata

In reply to Andrey Konovalov

SLUB allocator accesses metadata for slab objects, that may lie out-of-bounds of the object itself, or be accessed when an object is freed. Such accesses trigger tag faults and lead to false-positive reports with hardware tag-based KASAN.

Software KASAN modes disable instrumentation for allocator code via KASAN_SANITIZE Makefile macro, and rely on kasan_enable/disable_current() annotations which are used to ignore KASAN reports.

With hardware tag-based KASAN neither of those options are available, as it doesn't use compiler instrumetation, no tag faults are ignored, and MTE is disabled after the first one.

Instead, reset tags when accessing metadata.

---
Change-Id: I39f3c4d4f29299d4fbbda039bedf230db1c746fb

---
 mm/page_poison.c |  2 +-
 mm/slub.c        | 25 ++++++++++++++-----------
 2 files changed, 15 insertions(+), 12 deletions(-)
mm/page_poison.c +1 -1
index 34b9181ee5d1..d90d342a391f 100644
@@ -43,7 +43,7 @@ static void poison_page(struct page *page)
 
 	/* KASAN still think the page is in-use, so skip it. */
 	kasan_disable_current();
-	memset(addr, PAGE_POISON, PAGE_SIZE);
+	memset(kasan_reset_tag(addr), PAGE_POISON, PAGE_SIZE);
 	kasan_enable_current();
 	kunmap_atomic(addr);
 }
mm/slub.c +14 -11
index 68c02b2eecd9..8e134ca3a6fb 100644
@@ -249,7 +249,7 @@ static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
 {
 #ifdef CONFIG_SLAB_FREELIST_HARDENED
 	/*
-	 * When CONFIG_KASAN_SW_TAGS is enabled, ptr_addr might be tagged.
+	 * When CONFIG_KASAN_SW/HW_TAGS is enabled, ptr_addr might be tagged.
 	 * Normally, this doesn't cause any issues, as both set_freepointer()
 	 * and get_freepointer() are called with a pointer with the same tag.
 	 * However, there are some issues with CONFIG_SLUB_DEBUG code. For
@@ -275,6 +275,7 @@ static inline void *freelist_dereference(const struct kmem_cache *s,
 
 static inline void *get_freepointer(struct kmem_cache *s, void *object)
 {
+	object = kasan_reset_tag(object);
 	return freelist_dereference(s, object + s->offset);
 }
 
@@ -304,6 +305,7 @@ static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
 	BUG_ON(object == fp); /* naive detection of double free or corruption */
 #endif
 
+	freeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);
 	*(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr);
 }
 
@@ -538,8 +540,8 @@ static void print_section(char *level, char *text, u8 *addr,
 			  unsigned int length)
 {
 	metadata_access_enable();
-	print_hex_dump(level, text, DUMP_PREFIX_ADDRESS, 16, 1, addr,
-			length, 1);
+	print_hex_dump(level, kasan_reset_tag(text), DUMP_PREFIX_ADDRESS,
+			16, 1, addr, length, 1);
 	metadata_access_disable();
 }
 
@@ -570,7 +572,7 @@ static struct track *get_track(struct kmem_cache *s, void *object,
 
 	p = object + get_info_end(s);
 
-	return p + alloc;
+	return kasan_reset_tag(p + alloc);
 }
 
 static void set_track(struct kmem_cache *s, void *object,
@@ -583,7 +585,8 @@ static void set_track(struct kmem_cache *s, void *object,
 		unsigned int nr_entries;
 
 		metadata_access_enable();
-		nr_entries = stack_trace_save(p->addrs, TRACK_ADDRS_COUNT, 3);
+		nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
+						TRACK_ADDRS_COUNT, 3);
 		metadata_access_disable();
 
 		if (nr_entries < TRACK_ADDRS_COUNT)
@@ -747,7 +750,7 @@ static __printf(3, 4) void slab_err(struct kmem_cache *s, struct page *page,
 
 static void init_object(struct kmem_cache *s, void *object, u8 val)
 {
-	u8 *p = object;
+	u8 *p = kasan_reset_tag(object);
 
 	if (s->flags & SLAB_RED_ZONE)
 		memset(p - s->red_left_pad, val, s->red_left_pad);
@@ -777,7 +780,7 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page,
 	u8 *addr = page_address(page);
 
 	metadata_access_enable();
-	fault = memchr_inv(start, value, bytes);
+	fault = memchr_inv(kasan_reset_tag(start), value, bytes);
 	metadata_access_disable();
 	if (!fault)
 		return 1;
@@ -873,7 +876,7 @@ static int slab_pad_check(struct kmem_cache *s, struct page *page)
 
 	pad = end - remainder;
 	metadata_access_enable();
-	fault = memchr_inv(pad, POISON_INUSE, remainder);
+	fault = memchr_inv(kasan_reset_tag(pad), POISON_INUSE, remainder);
 	metadata_access_disable();
 	if (!fault)
 		return 1;
@@ -1118,7 +1121,7 @@ void setup_page_debug(struct kmem_cache *s, struct page *page, void *addr)
 		return;
 
 	metadata_access_enable();
-	memset(addr, POISON_INUSE, page_size(page));
+	memset(kasan_reset_tag(addr), POISON_INUSE, page_size(page));
 	metadata_access_disable();
 }
 
@@ -2884,10 +2887,10 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
 		stat(s, ALLOC_FASTPATH);
 	}
 
-	maybe_wipe_obj_freeptr(s, object);
+	maybe_wipe_obj_freeptr(s, kasan_reset_tag(object));
 
 	if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
-		memset(object, 0, s->object_size);
+		memset(kasan_reset_tag(object), 0, s->object_size);
 
 	slab_post_alloc_hook(s, objcg, gfpflags, 1, &object);
 
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#84 lore raw

Re: [PATCH v2 35/37] kasan, slub: reset tags when accessing metadata

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

mm/slub.c static void set_track(struct kmem_cache *s, void *object, · line 589

Quoted 8 lines

static void set_track(struct kmem_cache *s, void *object,

mm/slub.c +2 -1
@@ -583,7 +585,8 @@ static void set_track(struct kmem_cache *s, void *object,
 		unsigned int nr_entries;
 
 		metadata_access_enable();
-		nr_entries = stack_trace_save(p->addrs, TRACK_ADDRS_COUNT, 3);
+		nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
+						TRACK_ADDRS_COUNT, 3);

Suggested edit (below 100 cols):

- nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
- TRACK_ADDRS_COUNT, 3);
+ nr_entries = stack_trace_save(kasan_reset_tag(p->addrs), TRACK_ADDRS_COUNT, 3);

#85 lore raw

Re: [PATCH v2 35/37] kasan, slub: reset tags when accessing metadata

Quoted 15 lines · Marco Elver

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

mm/slub.c static void set_track(struct kmem_cache *s, void *object, · line 589

Quoted 8 lines

static void set_track(struct kmem_cache *s, void *object,

mm/slub.c +2 -1
@@ -583,7 +585,8 @@ static void set_track(struct kmem_cache *s, void *object,
              unsigned int nr_entries;
 
              metadata_access_enable();
-             nr_entries = stack_trace_save(p->addrs, TRACK_ADDRS_COUNT, 3);
+             nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
+                                             TRACK_ADDRS_COUNT, 3);

Suggested edit (below 100 cols):

Review

Quoted 3 lines
(quoted diff) +1 -2
-               nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
-                                               TRACK_ADDRS_COUNT, 3);
+               nr_entries = stack_trace_save(kasan_reset_tag(p->addrs), TRACK_ADDRS_COUNT, 3);

Ah, yes, it's a 100 lines now :) Will do in v3, thanks!

maintainer
#86 lore raw

Re: [PATCH v2 35/37] kasan, slub: reset tags when accessing metadata

Quoted 15 lines · Andrey Konovalov
Quoted 14 lines · Marco Elver

On Tue, Sep 15, 2020 at 11:16PM +0200, Andrey Konovalov wrote:
[...]

mm/slub.c static void set_track(struct kmem_cache *s, void *object, · line 589

Quoted 8 lines

static void set_track(struct kmem_cache *s, void *object,

mm/slub.c +2 -1
@@ -583,7 +585,8 @@ static void set_track(struct kmem_cache *s, void *object,
              unsigned int nr_entries;
 
              metadata_access_enable();
-             nr_entries = stack_trace_save(p->addrs, TRACK_ADDRS_COUNT, 3);
+             nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
+                                             TRACK_ADDRS_COUNT, 3);

Suggested edit (below 100 cols):

Quoted 5 lines

Review

Quoted 3 lines
(quoted diff) +1 -2
-               nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
-                                               TRACK_ADDRS_COUNT, 3);
+               nr_entries = stack_trace_save(kasan_reset_tag(p->addrs), TRACK_ADDRS_COUNT, 3);

Ah, yes, it's a 100 lines now :) Will do in v3, thanks!

Don't get too carried way ;). The preferred limit is still 80, as per Documentation/process/coding-style.rst (and commit bdc48fa11e46), unless it significantly increases readability and does not hide information. The checkpatch.pl was changed as not to make 80 a hard limit (and so an arbitrary higher limit was picked).

What (to me) would increase readability above is aligning the descendants to the open function parenthesis rather than increasing the line length. Anyway, it's up to you on the kasan code, just don't bother changing the patches for longer lines in arch/arm64.

Signature · 1 line
Catalin
#87 lore raw

PATCH v2 36/37 kasan, arm64: enable CONFIG_KASAN_HW_TAGS

In reply to Andrey Konovalov

Hardware tag-based KASAN is now ready, enable the configuration option.

---
Change-Id: I6eb1eea770e6b61ad71c701231b8d815a7ccc853

---
 arch/arm64/Kconfig | 1 +
 1 file changed, 1 insertion(+)
arch/arm64/Kconfig +1 -0
index c8e45870e993..afeb5dde437d 100644
@@ -132,6 +132,7 @@ config ARM64
 	select HAVE_ARCH_JUMP_LABEL_RELATIVE
 	select HAVE_ARCH_KASAN if !(ARM64_16K_PAGES && ARM64_VA_BITS_48)
 	select HAVE_ARCH_KASAN_SW_TAGS if (HAVE_ARCH_KASAN && !ARM64_MTE)
+	select HAVE_ARCH_KASAN_HW_TAGS if (HAVE_ARCH_KASAN && ARM64_MTE)
 	select HAVE_ARCH_KGDB
 	select HAVE_ARCH_MMAP_RND_BITS
 	select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog
#89 lore raw

PATCH v2 37/37 kasan: add documentation for hardware tag-based mode

In reply to Andrey Konovalov

Add documentation for hardware tag-based KASAN mode and also add some clarifications for software tag-based mode.

---
Change-Id: Ib46cb444cfdee44054628940a82f5139e10d0258

---
 Documentation/dev-tools/kasan.rst | 78 ++++++++++++++++++++++---------
 1 file changed, 57 insertions(+), 21 deletions(-)
Documentation/dev-tools/kasan.rst +57 -21
index a3030fc6afe5..d2d47c82a7b9 100644
@@ -5,12 +5,14 @@ Overview
 --------
 
 KernelAddressSANitizer (KASAN) is a dynamic memory error detector designed to
-find out-of-bound and use-after-free bugs. KASAN has two modes: generic KASAN
-(similar to userspace ASan) and software tag-based KASAN (similar to userspace
-HWASan).
+find out-of-bound and use-after-free bugs. KASAN has three modes:
+1. generic KASAN (similar to userspace ASan),
+2. software tag-based KASAN (similar to userspace HWASan),
+3. hardware tag-based KASAN (based on hardware memory tagging).
 
-KASAN uses compile-time instrumentation to insert validity checks before every
-memory access, and therefore requires a compiler version that supports that.
+Software KASAN modes (1 and 2) use compile-time instrumentation to insert
+validity checks before every memory access, and therefore require a compiler
+version that supports that.
 
 Generic KASAN is supported in both GCC and Clang. With GCC it requires version
 8.3.0 or later. With Clang it requires version 7.0.0 or later, but detection of
@@ -19,7 +21,7 @@ out-of-bounds accesses for global variables is only supported since Clang 11.
 Tag-based KASAN is only supported in Clang and requires version 7.0.0 or later.
 
 Currently generic KASAN is supported for the x86_64, arm64, xtensa, s390 and
-riscv architectures, and tag-based KASAN is supported only for arm64.
+riscv architectures, and tag-based KASAN modes are supported only for arm64.
 
 Usage
 -----
@@ -28,14 +30,16 @@ To enable KASAN configure kernel with::
 
 	  CONFIG_KASAN = y
 
-and choose between CONFIG_KASAN_GENERIC (to enable generic KASAN) and
-CONFIG_KASAN_SW_TAGS (to enable software tag-based KASAN).
+and choose between CONFIG_KASAN_GENERIC (to enable generic KASAN),
+CONFIG_KASAN_SW_TAGS (to enable software tag-based KASAN), and
+CONFIG_KASAN_HW_TAGS (to enable hardware tag-based KASAN).
 
-You also need to choose between CONFIG_KASAN_OUTLINE and CONFIG_KASAN_INLINE.
-Outline and inline are compiler instrumentation types. The former produces
-smaller binary while the latter is 1.1 - 2 times faster.
+For software modes, you also need to choose between CONFIG_KASAN_OUTLINE and
+CONFIG_KASAN_INLINE. Outline and inline are compiler instrumentation types.
+The former produces smaller binary while the latter is 1.1 - 2 times faster.
 
-Both KASAN modes work with both SLUB and SLAB memory allocators.
+Both software KASAN modes work with both SLUB and SLAB memory allocators,
+hardware tag-based KASAN currently only support SLUB.
 For better bug detection and nicer reporting, enable CONFIG_STACKTRACE.
 
 To augment reports with last allocation and freeing stack of the physical page,
@@ -196,17 +200,24 @@ and the second to last.
 Software tag-based KASAN
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
-Tag-based KASAN uses the Top Byte Ignore (TBI) feature of modern arm64 CPUs to
-store a pointer tag in the top byte of kernel pointers. Like generic KASAN it
-uses shadow memory to store memory tags associated with each 16-byte memory
+Software tag-based KASAN requires software memory tagging support in the form
+of HWASan-like compiler instrumentation (see HWASan documentation for details).
+
+Software tag-based KASAN is currently only implemented for arm64 architecture.
+
+Software tag-based KASAN uses the Top Byte Ignore (TBI) feature of arm64 CPUs
+to store a pointer tag in the top byte of kernel pointers. Like generic KASAN
+it uses shadow memory to store memory tags associated with each 16-byte memory
 cell (therefore it dedicates 1/16th of the kernel memory for shadow memory).
 
-On each memory allocation tag-based KASAN generates a random tag, tags the
-allocated memory with this tag, and embeds this tag into the returned pointer.
+On each memory allocation software tag-based KASAN generates a random tag, tags
+the allocated memory with this tag, and embeds this tag into the returned
+pointer.
+
 Software tag-based KASAN uses compile-time instrumentation to insert checks
 before each memory access. These checks make sure that tag of the memory that
 is being accessed is equal to tag of the pointer that is used to access this
-memory. In case of a tag mismatch tag-based KASAN prints a bug report.
+memory. In case of a tag mismatch software tag-based KASAN prints a bug report.
 
 Software tag-based KASAN also has two instrumentation modes (outline, that
 emits callbacks to check memory accesses; and inline, that performs the shadow
@@ -215,9 +226,34 @@ simply printed from the function that performs the access check. With inline
 instrumentation a brk instruction is emitted by the compiler, and a dedicated
 brk handler is used to print bug reports.
 
-A potential expansion of this mode is a hardware tag-based mode, which would
-use hardware memory tagging support instead of compiler instrumentation and
-manual shadow memory manipulation.
+Software tag-based KASAN uses 0xFF as a match-all pointer tag (accesses through
+pointers with 0xFF pointer tag aren't checked). The value 0xFE is currently
+reserved to tag freed memory regions.
+
+Software tag-based KASAN currently only supports tagging of slab memory.
+
+Hardware tag-based KASAN
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Hardware tag-based KASAN is similar to the software mode in concept, but uses
+hardware memory tagging support instead of compiler instrumentation and
+shadow memory.
+
+Hardware tag-based KASAN is currently only implemented for arm64 architecture
+and based on both arm64 Memory Tagging Extension (MTE) introduced in ARMv8.5
+Instruction Set Architecture, and Top Byte Ignore (TBI).
+
+Special arm64 instructions are used to assign memory tags for each allocation.
+Same tags are assigned to pointers to those allocations. On every memory
+access, hardware makes sure that tag of the memory that is being accessed is
+equal to tag of the pointer that is used to access this memory. In case of a
+tag mismatch a fault is generated and a report is printed.
+
+Hardware tag-based KASAN uses 0xFF as a match-all pointer tag (accesses through
+pointers with 0xFF pointer tag aren't checked). The value 0xFE is currently
+reserved to tag freed memory regions.
+
+Hardware tag-based KASAN currently only supports tagging of slab memory.
 
 What memory accesses are sanitised by KASAN?
 --------------------------------------------
Signature · 1 line
2.28.0.618.gf4bc123cb7-goog