From 19714385e06f5c9281455c5ce69368f05885cdc8 Mon Sep 17 00:00:00 2001 From: Wesley Norris Date: Sun, 11 Sep 2022 19:36:19 -0400 Subject: [PATCH] Add `kernel-address` sanitizer support for freestanding targets --- compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 4 ++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 2 + .../rustc_codegen_ssa/src/codegen_attrs.rs | 3 +- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 10 ++-- compiler/rustc_session/src/config.rs | 8 +++- compiler/rustc_session/src/options.rs | 3 +- compiler/rustc_session/src/session.rs | 4 +- .../src/spec/aarch64_unknown_none.rs | 2 +- compiler/rustc_target/src/spec/mod.rs | 4 ++ .../src/spec/riscv64gc_unknown_none_elf.rs | 3 ++ .../src/spec/riscv64imac_unknown_none_elf.rs | 3 +- .../src/spec/x86_64_unknown_none.rs | 2 +- .../src/compiler-flags/sanitizer.md | 20 ++++++++ src/tools/compiletest/src/header.rs | 2 + src/tools/compiletest/src/util.rs | 7 +++ .../sanitizer-kasan-emits-instrumentation.rs | 47 +++++++++++++++++++ tests/ui/sanitize/cfg-kasan.rs | 28 +++++++++++ 18 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 tests/codegen/sanitizer-kasan-emits-instrumentation.rs create mode 100644 tests/ui/sanitize/cfg-kasan.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 7a4ec494c8e7f..651d644ebb63d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -62,7 +62,7 @@ pub fn sanitize_attrs<'ll>( ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; - if enabled.contains(SanitizerSet::ADDRESS) { + if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } if enabled.contains(SanitizerSet::MEMORY) { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 38f8733763dfa..a939a2941111b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -446,6 +446,10 @@ pub(crate) unsafe fn llvm_optimize( sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS), sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS), + sanitize_kernel_address: config.sanitizer.contains(SanitizerSet::KERNELADDRESS), + sanitize_kernel_address_recover: config + .sanitizer_recover + .contains(SanitizerSet::KERNELADDRESS), }) } else { None diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8b4861962b229..7aab666fc5e8c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -482,6 +482,8 @@ pub struct SanitizerOptions { pub sanitize_thread: bool, pub sanitize_hwaddress: bool, pub sanitize_hwaddress_recover: bool, + pub sanitize_kernel_address: bool, + pub sanitize_kernel_address_recover: bool, } /// LLVMRelocMode diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 3ebbb2cbdfb8e..7d5c0048626c9 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -295,7 +295,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { if let Some(list) = attr.meta_item_list() { for item in list.iter() { if item.has_name(sym::address) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; + codegen_fn_attrs.no_sanitize |= + SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS; } else if item.has_name(sym::cfi) { codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; } else if item.has_name(sym::kcfi) { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index fbc1d8ef310c6..0a42265a6baaf 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -594,6 +594,8 @@ struct LLVMRustSanitizerOptions { bool SanitizeThread; bool SanitizeHWAddress; bool SanitizeHWAddressRecover; + bool SanitizeKernelAddress; + bool SanitizeKernelAddressRecover; }; extern "C" LLVMRustResult @@ -765,15 +767,17 @@ LLVMRustOptimize( ); } - if (SanitizerOptions->SanitizeAddress) { + if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) { OptimizerLastEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { + auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; #if LLVM_VERSION_LT(15, 0) MPM.addPass(RequireAnalysisPass()); #endif AddressSanitizerOptions opts = AddressSanitizerOptions{ - /*CompileKernel=*/false, - SanitizerOptions->SanitizeAddressRecover, + CompileKernel, + SanitizerOptions->SanitizeAddressRecover + || SanitizerOptions->SanitizeKernelAddressRecover, /*UseAfterScope=*/true, AsanDetectStackUseAfterReturnMode::Runtime, }; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e8bc19f88e3e3..c687248285eeb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1022,7 +1022,13 @@ fn default_configuration(sess: &Session) -> CrateConfig { let panic_strategy = sess.panic_strategy(); ret.insert((sym::panic, Some(panic_strategy.desc_symbol()))); - for s in sess.opts.unstable_opts.sanitizer { + for mut s in sess.opts.unstable_opts.sanitizer { + // KASAN should use the same attribute name as ASAN, as it's still ASAN + // under the hood + if s == SanitizerSet::KERNELADDRESS { + s = SanitizerSet::ADDRESS; + } + let symbol = Symbol::intern(&s.to_string()); ret.insert((sym::sanitize, Some(symbol))); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 81f7f6d72ae4a..dd61a30d67a60 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -370,7 +370,7 @@ mod desc { pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_oom_strategy: &str = "either `panic` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -684,6 +684,7 @@ mod parse { "address" => SanitizerSet::ADDRESS, "cfi" => SanitizerSet::CFI, "kcfi" => SanitizerSet::KCFI, + "kernel-address" => SanitizerSet::KERNELADDRESS, "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "memtag" => SanitizerSet::MEMTAG, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index e608b9fe0b3fe..3dc09854b3cbc 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -954,10 +954,10 @@ impl Session { /// Checks if LLVM lifetime markers should be emitted. pub fn emit_lifetime_markers(&self) -> bool { self.opts.optimize != config::OptLevel::No - // AddressSanitizer uses lifetimes to detect use after scope bugs. + // AddressSanitizer and KernelAddressSanitizer uses lifetimes to detect use after scope bugs. // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) } pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index 30fbe6f3c153a..be27302f7f52c 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -15,7 +15,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,+neon,+fp-armv8".into(), - supported_sanitizers: SanitizerSet::KCFI, + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index bc1920e34249a..556f168a0b172 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -812,6 +812,7 @@ bitflags::bitflags! { const MEMTAG = 1 << 6; const SHADOWCALLSTACK = 1 << 7; const KCFI = 1 << 8; + const KERNELADDRESS = 1 << 9; } } @@ -824,6 +825,7 @@ impl SanitizerSet { SanitizerSet::ADDRESS => "address", SanitizerSet::CFI => "cfi", SanitizerSet::KCFI => "kcfi", + SanitizerSet::KERNELADDRESS => "kernel-address", SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::MEMTAG => "memtag", @@ -866,6 +868,7 @@ impl IntoIterator for SanitizerSet { SanitizerSet::SHADOWCALLSTACK, SanitizerSet::THREAD, SanitizerSet::HWADDRESS, + SanitizerSet::KERNELADDRESS, ] .iter() .copied() @@ -2339,6 +2342,7 @@ impl Target { Some("address") => SanitizerSet::ADDRESS, Some("cfi") => SanitizerSet::CFI, Some("kcfi") => SanitizerSet::KCFI, + Some("kernel-address") => SanitizerSet::KERNELADDRESS, Some("leak") => SanitizerSet::LEAK, Some("memory") => SanitizerSet::MEMORY, Some("memtag") => SanitizerSet::MEMTAG, diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs index 409b0b2696152..ab3c14e3fe73d 100644 --- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -1,6 +1,8 @@ use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; use crate::spec::{RelocModel, Target, TargetOptions}; +use super::SanitizerSet; + pub fn target() -> Target { Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), @@ -20,6 +22,7 @@ pub fn target() -> Target { code_model: Some(CodeModel::Medium), emit_debug_gdb_scripts: false, eh_frame_header: false, + supported_sanitizers: SanitizerSet::KERNELADDRESS, ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs index 87aba9171b416..0f1821c9985b0 100644 --- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -1,5 +1,5 @@ use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; -use crate::spec::{RelocModel, Target, TargetOptions}; +use crate::spec::{RelocModel, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -19,6 +19,7 @@ pub fn target() -> Target { code_model: Some(CodeModel::Medium), emit_debug_gdb_scripts: false, eh_frame_header: false, + supported_sanitizers: SanitizerSet::KERNELADDRESS, ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs index 32060c35c11b8..43c5ce78ce34e 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs @@ -20,7 +20,7 @@ pub fn target() -> Target { features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" .into(), - supported_sanitizers: SanitizerSet::KCFI, + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, disable_redzone: true, panic_strategy: PanicStrategy::Abort, code_model: Some(CodeModel::Kernel), diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 70c3a445b864f..262cef3454ad3 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -531,6 +531,24 @@ LLVM KCFI is supported on the following targets: See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more details. +# KernelAddressSanitizer + +KernelAddressSanitizer (KASAN) is a freestanding version of AddressSanitizer +which is suitable for detecting memory errors in programs which do not have a +runtime environment, such as operating system kernels. KernelAddressSanitizer +requires manual implementation of the underlying functions used for tracking +KernelAddressSanitizer state. + +KernelAddressSanitizer is supported on the following targets: + +* `aarch64-unknown-none` +* `riscv64gc-unknown-none-elf` +* `riscv64imac-unknown-none-elf` +* `x86_64-unknown-none` + +See the [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan] for +more details. + # LeakSanitizer LeakSanitizer is run-time memory leak detector. @@ -714,6 +732,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [AddressSanitizer in Clang][clang-asan] * [ControlFlowIntegrity in Clang][clang-cfi] * [HWAddressSanitizer in Clang][clang-hwasan] +* [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan] * [LeakSanitizer in Clang][clang-lsan] * [MemorySanitizer in Clang][clang-msan] * [MemTagSanitizer in LLVM][llvm-memtag] @@ -727,4 +746,5 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html +[linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html [llvm-memtag]: https://llvm.org/docs/MemTagSanitizer.html diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index e11ebca6ea9af..d9b39927ca4bc 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -935,6 +935,7 @@ pub fn make_test_description( let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target); let has_kcfi = util::KCFI_SUPPORTED_TARGETS.contains(&&*config.target); + let has_kasan = util::KASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); @@ -1010,6 +1011,7 @@ pub fn make_test_description( reason!(!has_asan && config.parse_name_directive(ln, "needs-sanitizer-address")); reason!(!has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi")); reason!(!has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi")); + reason!(!has_kasan && config.parse_name_directive(ln, "needs-sanitizer-kasan")); reason!(!has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak")); reason!(!has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory")); reason!(!has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread")); diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 67f49bb6397c2..0ce9f2622e113 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -45,6 +45,13 @@ pub const CFI_SUPPORTED_TARGETS: &[&str] = &[ pub const KCFI_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-none", "x86_64-linux-none"]; +pub const KASAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-unknown-none", + "riscv64gc-unknown-none-elf", + "riscv64imac-unknown-none-elf", + "x86_64-unknown-none", +]; + pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[ // FIXME: currently broken, see #88132 // "aarch64-apple-darwin", diff --git a/tests/codegen/sanitizer-kasan-emits-instrumentation.rs b/tests/codegen/sanitizer-kasan-emits-instrumentation.rs new file mode 100644 index 0000000000000..d6e3f2719df70 --- /dev/null +++ b/tests/codegen/sanitizer-kasan-emits-instrumentation.rs @@ -0,0 +1,47 @@ +// Verifies that `-Zsanitizer=kernel-address` emits sanitizer instrumentation. + +// compile-flags: -Zsanitizer=kernel-address +// revisions: aarch64 riscv64imac riscv64gc x86_64 +//[aarch64] compile-flags: --target aarch64-unknown-none +//[aarch64] needs-llvm-components: aarch64 +//[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf +//[riscv64imac] needs-llvm-components: riscv +//[riscv64imac] min-llvm-version: 16 +//[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf +//[riscv64gc] needs-llvm-components: riscv +//[riscv64gc] min-llvm-version: 16 +//[x86_64] compile-flags: --target x86_64-unknown-none +//[x86_64] needs-llvm-components: x86 + +#![crate_type = "rlib"] +#![feature(no_core, no_sanitize, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +trait Copy {} + +impl Copy for u8 {} + +// CHECK-LABEL: ; sanitizer_kasan_emits_instrumentation::unsanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK: start: +// CHECK-NOT: call void @__asan_report_load +// CHECK: } +#[no_sanitize(address)] +pub fn unsanitized(b: &mut u8) -> u8 { + *b +} + +// CHECK-LABEL: ; sanitizer_kasan_emits_instrumentation::sanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK: sanitize_address +// CHECK: start: +// CHECK: call void @__asan_report_load +// CHECK: } +pub fn sanitized(b: &mut u8) -> u8 { + *b +} diff --git a/tests/ui/sanitize/cfg-kasan.rs b/tests/ui/sanitize/cfg-kasan.rs new file mode 100644 index 0000000000000..d721011f3ee51 --- /dev/null +++ b/tests/ui/sanitize/cfg-kasan.rs @@ -0,0 +1,28 @@ +// Verifies that when compiling with -Zsanitizer=kernel-address, +// the `#[cfg(sanitize = "address")]` attribute is configured. + +// check-pass +// compile-flags: -Zsanitizer=kernel-address --cfg kernel_address +// revisions: aarch64 riscv64imac riscv64gc x86_64 +//[aarch64] compile-flags: --target aarch64-unknown-none +//[aarch64] needs-llvm-components: aarch64 +//[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf +//[riscv64imac] needs-llvm-components: riscv +//[riscv64imac] min-llvm-version: 16 +//[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf +//[riscv64gc] needs-llvm-components: riscv +//[riscv64gc] min-llvm-version: 16 +//[x86_64] compile-flags: --target x86_64-unknown-none +//[x86_64] needs-llvm-components: x86 + +#![crate_type = "rlib"] +#![feature(cfg_sanitize, no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +const _: fn() -> () = main; + +#[cfg(all(sanitize = "address", kernel_address))] +fn main() {}