All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jann Horn <jannh@google.com>
To: Dmitry Vyukov <dvyukov@google.com>,
	 Andrey Konovalov <andreyknvl@gmail.com>,
	 Alexander Potapenko <glider@google.com>
Cc: Nathan Chancellor <nathan@kernel.org>,
	 Nick Desaulniers <nick.desaulniers+lkml@gmail.com>,
	 Bill Wendling <morbo@google.com>,
	Justin Stitt <justinstitt@google.com>,
	 linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com,
	 llvm@lists.linux.dev, Jann Horn <jannh@google.com>
Subject: [PATCH RFC v3 05/12] kasan: refactor write/is_write arguments to flags
Date: Tue, 08 Sep 2026 18:54:45 +0200	[thread overview]
Message-ID: <20260908-kcov-extrecord-v3-5-dcbc11593e88@google.com> (raw)
In-Reply-To: <20260908-kcov-extrecord-v3-0-dcbc11593e88@google.com>

Refactor the "write"/"is_write" arguments of kasan_check_range() and
kasan_report() into "flags" arguments that can contain the flag
KASAN_TYPE_WRITE.
This prepares for a following patch that introduces a second flag.

This should hopefully not change the machine code in hotpaths - both before
and after this change, the argument is either 0 or 1 at the assembly level
depending on whether the access is a write.
Only kasan_report() should have to do a tiny bit of extra work to do a
bittest.

Signed-off-by: Jann Horn <jannh@google.com>
---
 arch/arm64/kernel/traps.c |  2 +-
 arch/arm64/mm/fault.c     |  2 +-
 include/linux/kasan.h     |  6 ++++--
 mm/kasan/common.c         |  2 +-
 mm/kasan/generic.c        | 20 ++++++++++----------
 mm/kasan/kasan.h          |  6 +++---
 mm/kasan/report.c         |  3 ++-
 mm/kasan/report_generic.c |  8 ++++----
 mm/kasan/shadow.c         | 24 ++++++++++++------------
 mm/kasan/sw_tags.c        | 20 ++++++++++----------
 10 files changed, 48 insertions(+), 45 deletions(-)

diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 914282016069..9f31fe3f1660 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -1069,7 +1069,7 @@ int kasan_brk_handler(struct pt_regs *regs, unsigned long esr)
 	void *addr = (void *)regs->regs[0];
 	u64 pc = regs->pc;
 
-	kasan_report(addr, size, write, pc);
+	kasan_report(addr, size, write ? KASAN_TYPE_WRITE : 0, pc);
 
 	/*
 	 * The instrumentation allows to control whether we can proceed after
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 0b52557652be..221e39869ae6 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -365,7 +365,7 @@ static void report_tag_fault(unsigned long addr, unsigned long esr,
 	 * find out access size.
 	 */
 	bool is_write = !!(esr & ESR_ELx_WNR);
-	kasan_report((void *)addr, 0, is_write, regs->pc);
+	kasan_report((void *)addr, 0, is_write ? KASAN_TYPE_WRITE : 0, regs->pc);
 }
 #else
 /* Tag faults aren't enabled without CONFIG_KASAN_HW_TAGS. */
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index bf233bde68c7..03c7ac79345d 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -33,6 +33,8 @@ typedef unsigned int __bitwise kasan_vmalloc_flags_t;
 #define KASAN_VMALLOC_PAGE_RANGE 0x1 /* Apply exsiting page range */
 #define KASAN_VMALLOC_TLB_FLUSH  0x2 /* TLB flush */
 
+#define KASAN_TYPE_WRITE 0x1
+
 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 
 #include <linux/pgtable.h>
@@ -526,11 +528,11 @@ static inline void *kasan_reset_tag(const void *addr)
  * kasan_report - print a report about a bad memory access detected by KASAN
  * @addr: address of the bad access
  * @size: size of the bad access
- * @is_write: whether the bad access is a write or a read
+ * @flags: bitmask, can contain KASAN_TYPE_* flags
  * @ip: instruction pointer for the accessibility check or the bad access itself
  */
 bool kasan_report(const void *addr, size_t size,
-		bool is_write, unsigned long ip);
+		unsigned int flags, unsigned long ip);
 
 #else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */
 
diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index b7d05c2a6d93..1ab77ac9719c 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -571,7 +571,7 @@ void __kasan_mempool_unpoison_object(void *ptr, size_t size, unsigned long ip)
 bool __kasan_check_byte(const void *address, unsigned long ip)
 {
 	if (!kasan_byte_accessible(address)) {
-		kasan_report(address, 1, false, ip);
+		kasan_report(address, 1, 0, ip);
 		return false;
 	}
 	return true;
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index 2b8e73f5f6a7..9efd6fbbb7c3 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -173,7 +173,7 @@ static __always_inline bool memory_is_poisoned(const void *addr, size_t size)
 }
 
 static __always_inline bool check_region_inline(const void *addr,
-						size_t size, bool write,
+						size_t size, unsigned int flags,
 						unsigned long ret_ip)
 {
 	if (!kasan_enabled())
@@ -183,21 +183,21 @@ static __always_inline bool check_region_inline(const void *addr,
 		return true;
 
 	if (unlikely(addr + size < addr))
-		return !kasan_report(addr, size, write, ret_ip);
+		return !kasan_report(addr, size, flags, ret_ip);
 
 	if (unlikely(!addr_has_metadata(addr)))
-		return !kasan_report(addr, size, write, ret_ip);
+		return !kasan_report(addr, size, flags, ret_ip);
 
 	if (likely(!memory_is_poisoned(addr, size)))
 		return true;
 
-	return !kasan_report(addr, size, write, ret_ip);
+	return !kasan_report(addr, size, flags, ret_ip);
 }
 
-bool kasan_check_range(const void *addr, size_t size, bool write,
+bool kasan_check_range(const void *addr, size_t size, unsigned int flags,
 					unsigned long ret_ip)
 {
-	return check_region_inline(addr, size, write, ret_ip);
+	return check_region_inline(addr, size, flags, ret_ip);
 }
 
 bool kasan_byte_accessible(const void *addr)
@@ -252,7 +252,7 @@ EXPORT_SYMBOL(__asan_unregister_globals);
 #define DEFINE_ASAN_LOAD_STORE(size)					\
 	void __asan_load##size(void *addr)				\
 	{								\
-		check_region_inline(addr, size, false, _RET_IP_);	\
+		check_region_inline(addr, size, 0, _RET_IP_);	\
 	}								\
 	EXPORT_SYMBOL(__asan_load##size);				\
 	__alias(__asan_load##size)					\
@@ -260,7 +260,7 @@ EXPORT_SYMBOL(__asan_unregister_globals);
 	EXPORT_SYMBOL(__asan_load##size##_noabort);			\
 	void __asan_store##size(void *addr)				\
 	{								\
-		check_region_inline(addr, size, true, _RET_IP_);	\
+		check_region_inline(addr, size, KASAN_TYPE_WRITE, _RET_IP_); \
 	}								\
 	EXPORT_SYMBOL(__asan_store##size);				\
 	__alias(__asan_store##size)					\
@@ -275,7 +275,7 @@ DEFINE_ASAN_LOAD_STORE(16);
 
 void __asan_loadN(void *addr, ssize_t size)
 {
-	kasan_check_range(addr, size, false, _RET_IP_);
+	kasan_check_range(addr, size, 0, _RET_IP_);
 }
 EXPORT_SYMBOL(__asan_loadN);
 
@@ -285,7 +285,7 @@ EXPORT_SYMBOL(__asan_loadN_noabort);
 
 void __asan_storeN(void *addr, ssize_t size)
 {
-	kasan_check_range(addr, size, true, _RET_IP_);
+	kasan_check_range(addr, size, KASAN_TYPE_WRITE, _RET_IP_);
 }
 EXPORT_SYMBOL(__asan_storeN);
 
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index fc9169a54766..c833bd44e3cc 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -339,11 +339,11 @@ static __always_inline bool addr_has_metadata(const void *addr)
  * kasan_check_range - Check memory region, and report if invalid access.
  * @addr: the accessed address
  * @size: the accessed size
- * @write: true if access is a write access
+ * @flags: bitmask, can contain KASAN_TYPE_* flags
  * @ret_ip: return address
  * @return: true if access was valid, false if invalid
  */
-bool kasan_check_range(const void *addr, size_t size, bool write,
+bool kasan_check_range(const void *addr, size_t size, unsigned int flags,
 				unsigned long ret_ip);
 
 #else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
@@ -379,7 +379,7 @@ static inline void kasan_print_aux_stacks(struct kmem_cache *cache, const void *
 #endif
 
 bool kasan_report(const void *addr, size_t size,
-		bool is_write, unsigned long ip);
+		unsigned int flags, unsigned long ip);
 void kasan_report_invalid_free(void *object, unsigned long ip, enum kasan_report_type type);
 
 struct slab *kasan_addr_to_slab(const void *addr);
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index e804b1e1f886..cfe00ebb98ec 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -568,13 +568,14 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_ty
  * user_access_save/restore(): kasan_report_invalid_free() cannot be called
  * from a UACCESS region, and kasan_report_async() is not used on x86.
  */
-bool kasan_report(const void *addr, size_t size, bool is_write,
+bool kasan_report(const void *addr, size_t size, unsigned int flags,
 			unsigned long ip)
 {
 	bool ret = true;
 	unsigned long ua_flags = user_access_save();
 	unsigned long irq_flags;
 	struct kasan_report_info info;
+	bool is_write = (flags & KASAN_TYPE_WRITE);
 
 	if (unlikely(report_suppressed_sw()) || unlikely(!report_enabled())) {
 		ret = false;
diff --git a/mm/kasan/report_generic.c b/mm/kasan/report_generic.c
index f5b8e37b3805..445183e2f4d3 100644
--- a/mm/kasan/report_generic.c
+++ b/mm/kasan/report_generic.c
@@ -364,14 +364,14 @@ void kasan_print_address_stack_frame(const void *addr)
 #define DEFINE_ASAN_REPORT_LOAD(size)                     \
 void __asan_report_load##size##_noabort(void *addr) \
 {                                                         \
-	kasan_report(addr, size, false, _RET_IP_);	  \
+	kasan_report(addr, size, 0, _RET_IP_);	  \
 }                                                         \
 EXPORT_SYMBOL(__asan_report_load##size##_noabort)
 
 #define DEFINE_ASAN_REPORT_STORE(size)                     \
 void __asan_report_store##size##_noabort(void *addr) \
 {                                                          \
-	kasan_report(addr, size, true, _RET_IP_);	   \
+	kasan_report(addr, size, KASAN_TYPE_WRITE, _RET_IP_);	   \
 }                                                          \
 EXPORT_SYMBOL(__asan_report_store##size##_noabort)
 
@@ -388,12 +388,12 @@ DEFINE_ASAN_REPORT_STORE(16);
 
 void __asan_report_load_n_noabort(void *addr, ssize_t size)
 {
-	kasan_report(addr, size, false, _RET_IP_);
+	kasan_report(addr, size, 0, _RET_IP_);
 }
 EXPORT_SYMBOL(__asan_report_load_n_noabort);
 
 void __asan_report_store_n_noabort(void *addr, ssize_t size)
 {
-	kasan_report(addr, size, true, _RET_IP_);
+	kasan_report(addr, size, KASAN_TYPE_WRITE, _RET_IP_);
 }
 EXPORT_SYMBOL(__asan_report_store_n_noabort);
diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index d286e0a04543..a24f1225dd88 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -28,13 +28,13 @@
 
 bool __kasan_check_read(const volatile void *p, unsigned int size)
 {
-	return kasan_check_range((void *)p, size, false, _RET_IP_);
+	return kasan_check_range((void *)p, size, 0, _RET_IP_);
 }
 EXPORT_SYMBOL(__kasan_check_read);
 
 bool __kasan_check_write(const volatile void *p, unsigned int size)
 {
-	return kasan_check_range((void *)p, size, true, _RET_IP_);
+	return kasan_check_range((void *)p, size, KASAN_TYPE_WRITE, _RET_IP_);
 }
 EXPORT_SYMBOL(__kasan_check_write);
 
@@ -50,7 +50,7 @@ EXPORT_SYMBOL(__kasan_check_write);
 #undef memset
 void *memset(void *addr, int c, size_t len)
 {
-	if (!kasan_check_range(addr, len, true, _RET_IP_))
+	if (!kasan_check_range(addr, len, KASAN_TYPE_WRITE, _RET_IP_))
 		return NULL;
 
 	return __memset(addr, c, len);
@@ -60,8 +60,8 @@ void *memset(void *addr, int c, size_t len)
 #undef memmove
 void *memmove(void *dest, const void *src, size_t len)
 {
-	if (!kasan_check_range(src, len, false, _RET_IP_) ||
-	    !kasan_check_range(dest, len, true, _RET_IP_))
+	if (!kasan_check_range(src, len, 0, _RET_IP_) ||
+	    !kasan_check_range(dest, len, KASAN_TYPE_WRITE, _RET_IP_))
 		return NULL;
 
 	return __memmove(dest, src, len);
@@ -71,8 +71,8 @@ void *memmove(void *dest, const void *src, size_t len)
 #undef memcpy
 void *memcpy(void *dest, const void *src, size_t len)
 {
-	if (!kasan_check_range(src, len, false, _RET_IP_) ||
-	    !kasan_check_range(dest, len, true, _RET_IP_))
+	if (!kasan_check_range(src, len, 0, _RET_IP_) ||
+	    !kasan_check_range(dest, len, KASAN_TYPE_WRITE, _RET_IP_))
 		return NULL;
 
 	return __memcpy(dest, src, len);
@@ -81,7 +81,7 @@ void *memcpy(void *dest, const void *src, size_t len)
 
 void *__asan_memset(void *addr, int c, ssize_t len)
 {
-	if (!kasan_check_range(addr, len, true, _RET_IP_))
+	if (!kasan_check_range(addr, len, KASAN_TYPE_WRITE, _RET_IP_))
 		return NULL;
 
 	return __memset(addr, c, len);
@@ -91,8 +91,8 @@ EXPORT_SYMBOL(__asan_memset);
 #ifdef __HAVE_ARCH_MEMMOVE
 void *__asan_memmove(void *dest, const void *src, ssize_t len)
 {
-	if (!kasan_check_range(src, len, false, _RET_IP_) ||
-	    !kasan_check_range(dest, len, true, _RET_IP_))
+	if (!kasan_check_range(src, len, 0, _RET_IP_) ||
+	    !kasan_check_range(dest, len, KASAN_TYPE_WRITE, _RET_IP_))
 		return NULL;
 
 	return __memmove(dest, src, len);
@@ -102,8 +102,8 @@ EXPORT_SYMBOL(__asan_memmove);
 
 void *__asan_memcpy(void *dest, const void *src, ssize_t len)
 {
-	if (!kasan_check_range(src, len, false, _RET_IP_) ||
-	    !kasan_check_range(dest, len, true, _RET_IP_))
+	if (!kasan_check_range(src, len, 0, _RET_IP_) ||
+	    !kasan_check_range(dest, len, KASAN_TYPE_WRITE, _RET_IP_))
 		return NULL;
 
 	return __memcpy(dest, src, len);
diff --git a/mm/kasan/sw_tags.c b/mm/kasan/sw_tags.c
index c75741a74602..af77b642ede7 100644
--- a/mm/kasan/sw_tags.c
+++ b/mm/kasan/sw_tags.c
@@ -72,7 +72,7 @@ u8 kasan_random_tag(void)
 	return (u8)(state % (KASAN_TAG_MAX + 1));
 }
 
-bool kasan_check_range(const void *addr, size_t size, bool write,
+bool kasan_check_range(const void *addr, size_t size, unsigned int flags,
 			unsigned long ret_ip)
 {
 	u8 tag;
@@ -83,7 +83,7 @@ bool kasan_check_range(const void *addr, size_t size, bool write,
 		return true;
 
 	if (unlikely(addr + size < addr))
-		return !kasan_report(addr, size, write, ret_ip);
+		return !kasan_report(addr, size, flags, ret_ip);
 
 	tag = get_tag((const void *)addr);
 
@@ -109,12 +109,12 @@ bool kasan_check_range(const void *addr, size_t size, bool write,
 
 	untagged_addr = kasan_reset_tag((const void *)addr);
 	if (unlikely(!addr_has_metadata(untagged_addr)))
-		return !kasan_report(addr, size, write, ret_ip);
+		return !kasan_report(addr, size, flags, ret_ip);
 	shadow_first = kasan_mem_to_shadow(untagged_addr);
 	shadow_last = kasan_mem_to_shadow(untagged_addr + size - 1);
 	for (shadow = shadow_first; shadow <= shadow_last; shadow++) {
 		if (*shadow != tag) {
-			return !kasan_report(addr, size, write, ret_ip);
+			return !kasan_report(addr, size, flags, ret_ip);
 		}
 	}
 
@@ -137,12 +137,12 @@ bool kasan_byte_accessible(const void *addr)
 #define DEFINE_HWASAN_LOAD_STORE(size)					\
 	void __hwasan_load##size##_noabort(void *addr)			\
 	{								\
-		kasan_check_range(addr, size, false, _RET_IP_);		\
+		kasan_check_range(addr, size, 0, _RET_IP_);		\
 	}								\
 	EXPORT_SYMBOL(__hwasan_load##size##_noabort);			\
 	void __hwasan_store##size##_noabort(void *addr)			\
 	{								\
-		kasan_check_range(addr, size, true, _RET_IP_);		\
+		kasan_check_range(addr, size, KASAN_TYPE_WRITE, _RET_IP_); \
 	}								\
 	EXPORT_SYMBOL(__hwasan_store##size##_noabort)
 
@@ -154,13 +154,13 @@ DEFINE_HWASAN_LOAD_STORE(16);
 
 void __hwasan_loadN_noabort(void *addr, ssize_t size)
 {
-	kasan_check_range(addr, size, false, _RET_IP_);
+	kasan_check_range(addr, size, 0, _RET_IP_);
 }
 EXPORT_SYMBOL(__hwasan_loadN_noabort);
 
 void __hwasan_storeN_noabort(void *addr, ssize_t size)
 {
-	kasan_check_range(addr, size, true, _RET_IP_);
+	kasan_check_range(addr, size, KASAN_TYPE_WRITE, _RET_IP_);
 }
 EXPORT_SYMBOL(__hwasan_storeN_noabort);
 
@@ -173,6 +173,6 @@ EXPORT_SYMBOL(__hwasan_tag_memory);
 void kasan_tag_mismatch(void *addr, unsigned long access_info,
 			unsigned long ret_ip)
 {
-	kasan_report(addr, 1 << (access_info & 0xf), access_info & 0x10,
-		     ret_ip);
+	kasan_report(addr, 1 << (access_info & 0xf),
+		(access_info & 0x10) ? KASAN_TYPE_WRITE : 0, ret_ip);
 }

-- 
2.55.0.979.g7e5102b832-goog


  parent reply	other threads:[~2026-09-08 16:55 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-08 16:54 [PATCH RFC v3 00/12] KCOV: entry/exit records, memory access records, and delay injection Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 01/12] kcov: wire up compiler instrumentation for CONFIG_KCOV_EXT_RECORDS Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 02/12] kcov: refactor mode check out of check_kcov_mode() Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 03/12] kcov: introduce extended PC coverage collection mode Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 04/12] kcov: summarize entry/exit while disabled Jann Horn
2026-09-08 16:54 ` Jann Horn [this message]
2026-09-08 16:54 ` [PATCH RFC v3 06/12] kcov: introduce memory access tracing Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 07/12] kasan: provide memory access information to KCOV Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 08/12] kcov: log freeing of SLUB objects and pages Jann Horn
2026-09-08 17:04   ` Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 09/12] kcov: record return address on function entry Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 10/12] kcov: log old value Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 11/12] kcov: introduce delay injection Jann Horn
2026-09-08 16:54 ` [PATCH RFC v3 12/12] Documentation/kcov: add documentation for EXT_RECORDS and KCOV_MEMORY Jann Horn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260908-kcov-extrecord-v3-5-dcbc11593e88@google.com \
    --to=jannh@google.com \
    --cc=andreyknvl@gmail.com \
    --cc=dvyukov@google.com \
    --cc=glider@google.com \
    --cc=justinstitt@google.com \
    --cc=kasan-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=nick.desaulniers+lkml@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.