lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20250804-kasan-via-kcsan-v1-3-823a6d5b5f84@google.com>
Date: Mon, 04 Aug 2025 21:17:07 +0200
From: Jann Horn <jannh@...gle.com>
To: Masahiro Yamada <masahiroy@...nel.org>, 
 Nathan Chancellor <nathan@...nel.org>, 
 Nicolas Schier <nicolas.schier@...ux.dev>, 
 Andrey Ryabinin <ryabinin.a.a@...il.com>, 
 Alexander Potapenko <glider@...gle.com>, 
 Andrey Konovalov <andreyknvl@...il.com>, Dmitry Vyukov <dvyukov@...gle.com>, 
 Vincenzo Frascino <vincenzo.frascino@....com>, 
 Andrew Morton <akpm@...ux-foundation.org>, Marco Elver <elver@...gle.com>, 
 Christoph Lameter <cl@...two.org>, David Rientjes <rientjes@...gle.com>, 
 Vlastimil Babka <vbabka@...e.cz>, Roman Gushchin <roman.gushchin@...ux.dev>, 
 Harry Yoo <harry.yoo@...cle.com>
Cc: linux-kbuild@...r.kernel.org, linux-kernel@...r.kernel.org, 
 kasan-dev@...glegroups.com, linux-mm@...ck.org, 
 Jann Horn <jannh@...gle.com>
Subject: [PATCH early RFC 3/4] kasan: add support for running via KCSAN
 hooks

Inserting ASAN and TSAN instrumentation at the same time is not
supported by gcc/clang, and so the kernel currently does not support
enabling KASAN (which uses ASAN) and KCSAN (which uses TSAN) at the same
time.
But luckily, the TSAN hooks provide a large part of what we get from ASAN
hooks; so it is possible to hook up KASAN indirectly through KCSAN.

There are some trade-offs with this - in particular:

 - Since OOB detection for stack and globals relies on ASAN-specific
   redzone creation in the compiler, it won't be available when using
   TSAN instrumentation (because the compiler thinks we only want
   instrumentation for catching UAF).
 - Unlike KASAN, KCSAN does not have instrumentation for functions like
   memcpy(), and this KASAN mode inherits this issue from KCSAN.
 - It makes it impossible to selectively disable KCSAN without also
   disabling KASAN, or the other way around. To be safe, this mode only
   enables KCSAN instrumentation in files in which both KASAN and KCSAN
   are allowed.
   (There are currently some places in the kernel that disable KASAN
   without disabling KCSAN - I think that's probably unintentional, and
   we might want to refactor that at some point such that either KASAN
   and KCSAN are enabled in the same files, or files covered by KCSAN
   are a subset of files covered by KASAN if that's somehow problematic.
   Opting out of every compiler instrumentation individually in makefiles
   seems suboptimal to me.)
 - I expect its performance to be significantly worse than normal KASAN,
   but have not tested that; performance is not really something I care
   about for my usecase.

NOTE: instrument_read() and such call both KASAN and KCSAN, so KASAN
will see duplicate accesses from instrument_read().

Signed-off-by: Jann Horn <jannh@...gle.com>
---
 include/linux/kasan.h   | 14 ++++++++++++++
 kernel/kcsan/core.c     | 13 +++++++++++++
 lib/Kconfig.kasan       | 17 +++++++++++++++++
 lib/Kconfig.kcsan       |  2 +-
 mm/kasan/kasan.h        | 11 -----------
 mm/kasan/kasan_test_c.c |  4 ++++
 mm/kasan/shadow.c       |  3 ++-
 scripts/Makefile.lib    |  6 +++++-
 8 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 890011071f2b..818c53707e72 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -75,6 +75,20 @@ extern void kasan_enable_current(void);
 /* Disable reporting bugs for current task */
 extern void kasan_disable_current(void);
 
+/**
+ * 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
+ * @ret_ip: return address
+ * @return: true if access was valid, false if invalid
+ *
+ * This function is intended for KASAN-internal use and for integration with
+ * KCSAN.
+ */
+bool kasan_check_range(const void *addr, size_t size, bool write,
+				unsigned long ret_ip);
+
 #else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
 static inline int kasan_add_zero_shadow(void *start, unsigned long size)
diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c
index 8a7baf4e332e..aaa9bf0141a8 100644
--- a/kernel/kcsan/core.c
+++ b/kernel/kcsan/core.c
@@ -728,6 +728,19 @@ check_access(const volatile void *ptr, size_t size, int type, unsigned long ip)
 	if (unlikely(size == 0))
 		return;
 
+#ifdef CONFIG_KASAN_KCSAN
+	/*
+	 * Use the KCSAN infrastructure to inform KASAN about memory accesses.
+	 * Do this only for real memory access, not for KCSAN assertions - in
+	 * particular, SLUB makes KCSAN assertions that can cross into ASAN
+	 * redzones, which would KASAN think that an OOB access occurred.
+	 */
+	if ((type & KCSAN_ACCESS_ASSERT) == 0) {
+		kasan_check_range((const void *)ptr, size,
+				  (type & (KCSAN_ACCESS_WRITE|KCSAN_ACCESS_COMPOUND)) != 0, ip);
+	}
+#endif
+
 again:
 	/*
 	 * Avoid user_access_save in fast-path: find_watchpoint is safe without
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index f82889a830fa..0ee9f2196448 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -133,6 +133,7 @@ choice
 
 config KASAN_OUTLINE
 	bool "Outline instrumentation"
+	depends on !KCSAN
 	help
 	  Makes the compiler insert function calls that check whether the memory
 	  is accessible before each memory access. Slower than KASAN_INLINE, but
@@ -141,17 +142,33 @@ config KASAN_OUTLINE
 config KASAN_INLINE
 	bool "Inline instrumentation"
 	depends on !ARCH_DISABLE_KASAN_INLINE
+	depends on !KCSAN
 	help
 	  Makes the compiler directly insert memory accessibility checks before
 	  each memory access. Faster than KASAN_OUTLINE (gives ~x2 boost for
 	  some workloads), but makes the kernel's .text size much bigger.
 
+config KASAN_KCSAN
+	bool "Piggyback on KCSAN (EXPERIMENTAL)"
+	depends on KASAN_GENERIC
+	depends on KCSAN
+	help
+	  Let KASAN piggyback on KCSAN instrumentation callbacks instead of
+	  using KASAN-specific compiler instrumentation.
+
+	  This limits coverage of KASAN and KCSAN to files that are supported by
+	  *both* KASAN and KCSAN.
+
+	  This is only useful if you want to run both the KASAN and KCSAN
+	  subsystems at the same time.
+
 endchoice
 
 config KASAN_STACK
 	bool "Stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST
 	depends on KASAN_GENERIC || KASAN_SW_TAGS
 	depends on !ARCH_DISABLE_KASAN_INLINE
+	depends on !KASAN_KCSAN
 	default y if CC_IS_GCC
 	help
 	  Disables stack instrumentation and thus KASAN's ability to detect
diff --git a/lib/Kconfig.kcsan b/lib/Kconfig.kcsan
index 609ddfc73de5..86bf8f2da0a8 100644
--- a/lib/Kconfig.kcsan
+++ b/lib/Kconfig.kcsan
@@ -13,7 +13,7 @@ config HAVE_KCSAN_COMPILER
 menuconfig KCSAN
 	bool "KCSAN: dynamic data race detector"
 	depends on HAVE_ARCH_KCSAN && HAVE_KCSAN_COMPILER
-	depends on DEBUG_KERNEL && !KASAN
+	depends on DEBUG_KERNEL
 	select CONSTRUCTORS
 	select STACKTRACE
 	help
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 129178be5e64..ec191ff1fc83 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -335,17 +335,6 @@ static __always_inline bool addr_has_metadata(const void *addr)
 }
 #endif
 
-/**
- * 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
- * @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,
-				unsigned long ret_ip);
-
 #else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
 static __always_inline bool addr_has_metadata(const void *addr)
diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c
index 5f922dd38ffa..c4826c67aa33 100644
--- a/mm/kasan/kasan_test_c.c
+++ b/mm/kasan/kasan_test_c.c
@@ -154,6 +154,8 @@ static void kasan_test_exit(struct kunit *test)
 #define KASAN_TEST_NEEDS_CHECKED_MEMINTRINSICS(test) do {		\
 	if (IS_ENABLED(CONFIG_KASAN_HW_TAGS))				\
 		break;  /* No compiler instrumentation. */		\
+	if (IS_ENABLED(CONFIG_KASAN_KCSAN))				\
+		kunit_skip((test), "No checked mem*() with KCSAN");	\
 	if (IS_ENABLED(CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX))	\
 		break;  /* Should always be instrumented! */		\
 	if (IS_ENABLED(CONFIG_GENERIC_ENTRY))				\
@@ -1453,6 +1455,7 @@ static void kasan_global_oob_right(struct kunit *test)
 
 	/* Only generic mode instruments globals. */
 	KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_GENERIC);
+	KASAN_TEST_NEEDS_CONFIG_OFF(test, CONFIG_KASAN_KCSAN);
 
 	KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p);
 }
@@ -1468,6 +1471,7 @@ static void kasan_global_oob_left(struct kunit *test)
 	 */
 	KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_CC_IS_CLANG);
 	KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_GENERIC);
+	KASAN_TEST_NEEDS_CONFIG_OFF(test, CONFIG_KASAN_KCSAN);
 	KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p);
 }
 
diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index d2c70cd2afb1..136be8e6c98d 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -38,7 +38,8 @@ bool __kasan_check_write(const volatile void *p, unsigned int size)
 }
 EXPORT_SYMBOL(__kasan_check_write);
 
-#if !defined(CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX) && !defined(CONFIG_GENERIC_ENTRY)
+#if !defined(CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX) && \
+		!defined(CONFIG_GENERIC_ENTRY) && !defined(CONFIG_KASAN_KCSAN)
 /*
  * CONFIG_GENERIC_ENTRY relies on compiler emitted mem*() calls to not be
  * instrumented. KASAN enabled toolchains should emit __asan_mem*() functions
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 017c9801b6bb..2572fcc0bf50 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -56,10 +56,13 @@ is-kasan-compatible = $(patsubst n%,, \
 	$(KASAN_SANITIZE_$(target-stem).o)$(KASAN_SANITIZE)$(is-kernel-object))
 ifeq ($(CONFIG_KASAN),y)
 ifneq ($(CONFIG_KASAN_HW_TAGS),y)
+# Disable ASAN instrumentation if KASAN is running off the KCSAN hooks.
+ifneq ($(CONFIG_KASAN_KCSAN),y)
 _c_flags += $(if $(is-kasan-compatible), $(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE))
 _rust_flags += $(if $(is-kasan-compatible), $(RUSTFLAGS_KASAN))
 endif
 endif
+endif
 
 ifeq ($(CONFIG_KMSAN),y)
 _c_flags += $(if $(patsubst n%,, \
@@ -95,7 +98,8 @@ endif
 is-kcsan-compatible = $(patsubst n%,, \
 	$(KCSAN_SANITIZE_$(target-stem).o)$(KCSAN_SANITIZE)$(is-kernel-object))
 ifeq ($(CONFIG_KCSAN),y)
-_c_flags += $(if $(is-kcsan-compatible), $(CFLAGS_KCSAN))
+enable-kcsan-instr = $(and $(is-kcsan-compatible), $(if $(CONFIG_KASAN_KCSAN),$(is-kasan-compatible),y))
+_c_flags += $(if $(enable-kcsan-instr), $(CFLAGS_KCSAN))
 # Some uninstrumented files provide implied barriers required to avoid false
 # positives: set KCSAN_INSTRUMENT_BARRIERS for barrier instrumentation only.
 _c_flags += $(if $(patsubst n%,, \

-- 
2.50.1.565.gc32cd1483b-goog


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ