Rust supports ThreadSanitizer instrumentation via LLVM, which is what KCSAN relies on; however, Rust code is not currently instrumented.
Pass -Zsanitizer=thread and the same TSan backend options used for C code to rustc. Wire up KCSAN_SANITIZE in scripts/Makefile.lib for parity with C (though rustc currently rejects linking crates with mismatched instrumentation). rustc only accepts -Zsanitizer=thread for targets that declare support for it. Of the targets used by the kernel, this is only possible for the generated x86_64 target specification, so restrict instrumentation of Rust code to x86_64 for now via HAVE_KCSAN_RUSTC. On other architectures Rust code remains uninstrumented as before. All TSan options used are supported by the LLVM versions of the minimum supported rustc, so no rustc-option checks are needed (they would also fail, because x86_64-unknown-none used for option checks does not support -Zsanitizer=thread). Unlike Clang and GCC, an equivalent to -fno-optimize-sibling-calls is omitted for Rust because rustc provides no corresponding option. While disabling tail-call optimizations helps preserve complete stacks traces in reports, it is optional and not required for KCSAN to function. With this enabled we can detect data races in kernel Rust code and between Rust and C code. Acked-by: Miguel Ojeda <[email protected]> Acked-by: Boqun Feng <[email protected]> Signed-off-by: Marco Elver <[email protected]> --- v2: * Add note on -fno-optimize-sibling-calls to commit message. --- Documentation/dev-tools/kcsan.rst | 7 +++++++ lib/Kconfig.kcsan | 7 +++++++ scripts/Makefile.kcsan | 13 +++++++++++++ scripts/Makefile.lib | 3 +++ scripts/generate_rust_target.rs | 2 +- 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/dev-tools/kcsan.rst b/Documentation/dev-tools/kcsan.rst index 8575178aa87f..3bb8754f844b 100644 --- a/Documentation/dev-tools/kcsan.rst +++ b/Documentation/dev-tools/kcsan.rst @@ -14,6 +14,9 @@ Usage KCSAN is supported by both GCC and Clang. With GCC we require version 11 or later, and with Clang also require version 11 or later. +Rust code is instrumented as well on architectures whose Rust target supports +it (currently only x86_64; see ``CONFIG_HAVE_KCSAN_RUSTC``). + To enable KCSAN configure the kernel with:: CONFIG_KCSAN = y @@ -121,6 +124,10 @@ the below options are available: KCSAN_SANITIZE := n + Note that ``rustc`` rejects linking crates built with and without KCSAN + instrumentation, so ``KCSAN_SANITIZE`` and ``KCSAN_SANITIZE_<target>.o`` + cannot currently be used for Rust code. + .. _"Marking Shared-Memory Accesses" in the LKMM: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/memory-model/Documentation/access-marking.txt Furthermore, it is possible to tell KCSAN to show or hide entire classes of diff --git a/lib/Kconfig.kcsan b/lib/Kconfig.kcsan index 4ce4b0c0109c..f0096e1c8b61 100644 --- a/lib/Kconfig.kcsan +++ b/lib/Kconfig.kcsan @@ -10,6 +10,13 @@ config HAVE_KCSAN_COMPILER For the list of compilers that support KCSAN, please see <file:Documentation/dev-tools/kcsan.rst>. +config HAVE_KCSAN_RUSTC + def_bool RUST && X86_64 + help + rustc only accepts -Zsanitizer=thread for targets that declare + support for it; of the kernel's Rust targets, only x86_64 does. + Elsewhere, Rust code is not instrumented. + menuconfig KCSAN bool "KCSAN: dynamic data race detector" depends on HAVE_ARCH_KCSAN && HAVE_KCSAN_COMPILER diff --git a/scripts/Makefile.kcsan b/scripts/Makefile.kcsan index 19f693b68a96..01af6fd71006 100644 --- a/scripts/Makefile.kcsan +++ b/scripts/Makefile.kcsan @@ -24,3 +24,16 @@ kcsan-cflags += $(call cc-option,$(call cc-param,tsan-instrument-func-entry-exit endif export CFLAGS_KCSAN := $(kcsan-cflags) + +ifdef CONFIG_HAVE_KCSAN_RUSTC +# The minimum supported rustc's LLVM supports all of these options. +rustc-param = -Cllvm-args=-$(1) +kcsan-rustflags := -Zsanitizer=thread \ + $(call rustc-param,tsan-compound-read-before-write=1) \ + $(call rustc-param,tsan-distinguish-volatile=1) +ifndef CONFIG_KCSAN_WEAK_MEMORY +kcsan-rustflags += $(call rustc-param,tsan-instrument-func-entry-exit=0) +endif +endif + +export RUSTFLAGS_KCSAN := $(kcsan-rustflags) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 0a4fdd8bd975..78f1c23e54be 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -98,6 +98,9 @@ ifeq ($(CONFIG_KCSAN),y) _c_flags += $(if $(patsubst n%,, \ $(KCSAN_SANITIZE_$(target-stem).o)$(KCSAN_SANITIZE)$(is-kernel-object)), \ $(CFLAGS_KCSAN)) +_rust_flags += $(if $(patsubst n%,, \ + $(KCSAN_SANITIZE_$(target-stem).o)$(KCSAN_SANITIZE)$(is-kernel-object)), \ + $(RUSTFLAGS_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%,, \ diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 7687b0dd5474..4b02bffa6b46 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -231,7 +231,7 @@ fn main() { } ts.push("features", features); ts.push("llvm-target", "x86_64-linux-gnu"); - ts.push("supported-sanitizers", ["kcfi", "kernel-address"]); + ts.push("supported-sanitizers", ["kcfi", "kernel-address", "thread"]); if cfg.rustc_version_atleast(1, 91, 0) { ts.push("target-pointer-width", 64); } else { -- 2.56.0.385.gd3acb90ef8-goog

