[PATCH RFC v3 07/12] kasan: provide memory access information to KCOV

From: Jann Horn

Date: Tue Sep 08 2026 - 13:09:57 EST


In CONFIG_KCOV_MEMORY builds, let KASAN provide information about memory
accesses to KCOV.
Since KCOV already receives information from instrument_*() directly,
filter out those accesses by introducing KASAN_TYPE_EXPLICIT.
Because the KCOV usecase requires seeing ~every memory access (including
accesses to globals and repeated accesses to the same memory location
within a basic block), gate this on CC_IS_CLANG and let it flip some hidden
LLVM flags that disable ASAN optimizations.

Signed-off-by: Jann Horn <jannh@xxxxxxxxxx>
---
include/linux/kasan.h | 1 +
lib/Kconfig.debug | 2 ++
lib/Kconfig.kasan | 9 +++++++++
mm/kasan/generic.c | 15 +++++++++++++++
mm/kasan/shadow.c | 5 +++--
scripts/Makefile.kasan | 17 +++++++++++++++++
tools/objtool/check.c | 1 +
7 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 03c7ac79345d..4b915e0c51bc 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -34,6 +34,7 @@ typedef unsigned int __bitwise kasan_vmalloc_flags_t;
#define KASAN_VMALLOC_TLB_FLUSH 0x2 /* TLB flush */

#define KASAN_TYPE_WRITE 0x1
+#define KASAN_TYPE_EXPLICIT 0x2

#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 5de427ccc42d..f763f0504f62 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2221,6 +2221,8 @@ config KCOV_MEMORY
bool "Enable memory access trace collection by KCOV"
depends on KCOV
depends on KCOV_EXT_RECORDS
+ depends on HAVE_KASAN_REPORT_EVERY_ACCESS
+ select KASAN_REPORT_EVERY_ACCESS
help
Provide a KCOV mode which records memory access operations and allows
userspace to inject execution delays to impose constraints on the
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index a4bb610a7a6f..9a43f6269c5c 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -228,4 +228,13 @@ config KASAN_EXTRA_INFO
boot parameter, it will add 8 * stack_ring_size bytes of additional
memory consumption.

+# Is KASAN_REPORT_EVERY_ACCESS allowed?
+config HAVE_KASAN_REPORT_EVERY_ACCESS
+ def_bool y
+ depends on KASAN_OUTLINE
+ depends on CC_IS_CLANG
+
+config KASAN_REPORT_EVERY_ACCESS
+ bool
+
endif # KASAN
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index 9efd6fbbb7c3..6cf88f569e64 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -32,6 +32,8 @@
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/bug.h>
+#include <linux/kcov.h>
+#include <uapi/linux/kcov.h>

#include "kasan.h"
#include "../slab.h"
@@ -182,6 +184,19 @@ static __always_inline bool check_region_inline(const void *addr,
if (unlikely(size == 0))
return true;

+ /*
+ * Do not route information about an access to KCOV if we got called
+ * through the instrument_*() path - KCOV can get those accesses
+ * directly from instrument_*(), and get a bit more metadata about the
+ * access that way.
+ */
+ if (likely((flags & KASAN_TYPE_EXPLICIT) == 0)) {
+ unsigned int kcov_flags =
+ (flags & KASAN_TYPE_WRITE) ? MEMORY_ACCESS_RECORD_WRITE : 0;
+
+ __kcov_handle_memaccess(addr, size, kcov_flags, ret_ip);
+ }
+
if (unlikely(addr + size < addr))
return !kasan_report(addr, size, flags, ret_ip);

diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index a24f1225dd88..84f8567d4f2c 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -28,13 +28,14 @@

bool __kasan_check_read(const volatile void *p, unsigned int size)
{
- return kasan_check_range((void *)p, size, 0, _RET_IP_);
+ return kasan_check_range((void *)p, size, KASAN_TYPE_EXPLICIT, _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, KASAN_TYPE_WRITE, _RET_IP_);
+ return kasan_check_range((void *)p, size,
+ KASAN_TYPE_WRITE|KASAN_TYPE_EXPLICIT, _RET_IP_);
}
EXPORT_SYMBOL(__kasan_check_write);

diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 91504e81247a..82e88c5fb9bc 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -60,6 +60,23 @@ kasan_params += asan-instrumentation-with-call-threshold=$(call_threshold) \
asan-instrument-allocas=1 \
asan-globals=1

+# When we piggyback tracing of memory accesses for race condition testing on top
+# of KASAN, we want the compiler to report memory accesses even when KASAN can
+# prove that no UAF/OOB can occur; in particular, these optimizations must be
+# inhibited:
+#
+# - suppression of ASAN hook calls for global variables
+# - merging of multiple accesses in a basic block into a single ASAN hook call
+#
+# For now, known stack variable accesses are still ignored as a performance
+# tradeoff, though that will probably make a small number of races (where
+# another task concurrently accesses stuff on our stack) invisible to the
+# instrumentation. (Stack access instrumentation is gated on
+# asan-use-stack-safety and asan-skip-promotable-allocas.)
+ifdef CONFIG_KASAN_REPORT_EVERY_ACCESS
+kasan_params += asan-opt-globals=0 asan-opt-same-temp=0
+endif # CONFIG_KASAN_REPORT_EVERY_ACCESS
+
# Instrument memcpy/memset/memmove calls by using instrumented __asan_mem*()
# instead. With compilers that don't support this option, compiler-inserted
# memintrinsics won't be checked by KASAN on GENERIC_ENTRY architectures.
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index d70cb640e2ec..08ebfe1f3fac 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1219,6 +1219,7 @@ static const char *uaccess_safe_builtin[] = {
/* KCOV */
"write_comp_data",
"check_kcov_mode",
+ "__kcov_handle_memaccess",
"__sanitizer_cov_trace_pc",
"__sanitizer_cov_trace_pc_entry",
"__sanitizer_cov_trace_pc_exit",

--
2.55.0.979.g7e5102b832-goog