Add kernel-address sanitizer support for freestanding targets

This commit is contained in:
Wesley Norris 2022-09-11 19:36:19 -04:00
parent 0416b1a6f6
commit 19714385e0
18 changed files with 142 additions and 12 deletions

View File

@ -62,7 +62,7 @@ pub fn sanitize_attrs<'ll>(
) -> SmallVec<[&'ll Attribute; 4]> { ) -> SmallVec<[&'ll Attribute; 4]> {
let mut attrs = SmallVec::new(); let mut attrs = SmallVec::new();
let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; 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)); attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
} }
if enabled.contains(SanitizerSet::MEMORY) { if enabled.contains(SanitizerSet::MEMORY) {

View File

@ -446,6 +446,10 @@ pub(crate) unsafe fn llvm_optimize(
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS), sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
sanitize_hwaddress_recover: config.sanitizer_recover.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 { } else {
None None

View File

@ -482,6 +482,8 @@ pub struct SanitizerOptions {
pub sanitize_thread: bool, pub sanitize_thread: bool,
pub sanitize_hwaddress: bool, pub sanitize_hwaddress: bool,
pub sanitize_hwaddress_recover: bool, pub sanitize_hwaddress_recover: bool,
pub sanitize_kernel_address: bool,
pub sanitize_kernel_address_recover: bool,
} }
/// LLVMRelocMode /// LLVMRelocMode

View File

@ -295,7 +295,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
if let Some(list) = attr.meta_item_list() { if let Some(list) = attr.meta_item_list() {
for item in list.iter() { for item in list.iter() {
if item.has_name(sym::address) { 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) { } else if item.has_name(sym::cfi) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
} else if item.has_name(sym::kcfi) { } else if item.has_name(sym::kcfi) {

View File

@ -594,6 +594,8 @@ struct LLVMRustSanitizerOptions {
bool SanitizeThread; bool SanitizeThread;
bool SanitizeHWAddress; bool SanitizeHWAddress;
bool SanitizeHWAddressRecover; bool SanitizeHWAddressRecover;
bool SanitizeKernelAddress;
bool SanitizeKernelAddressRecover;
}; };
extern "C" LLVMRustResult extern "C" LLVMRustResult
@ -765,15 +767,17 @@ LLVMRustOptimize(
); );
} }
if (SanitizerOptions->SanitizeAddress) { if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) {
OptimizerLastEPCallbacks.push_back( OptimizerLastEPCallbacks.push_back(
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
auto CompileKernel = SanitizerOptions->SanitizeKernelAddress;
#if LLVM_VERSION_LT(15, 0) #if LLVM_VERSION_LT(15, 0)
MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
#endif #endif
AddressSanitizerOptions opts = AddressSanitizerOptions{ AddressSanitizerOptions opts = AddressSanitizerOptions{
/*CompileKernel=*/false, CompileKernel,
SanitizerOptions->SanitizeAddressRecover, SanitizerOptions->SanitizeAddressRecover
|| SanitizerOptions->SanitizeKernelAddressRecover,
/*UseAfterScope=*/true, /*UseAfterScope=*/true,
AsanDetectStackUseAfterReturnMode::Runtime, AsanDetectStackUseAfterReturnMode::Runtime,
}; };

View File

@ -1022,7 +1022,13 @@ fn default_configuration(sess: &Session) -> CrateConfig {
let panic_strategy = sess.panic_strategy(); let panic_strategy = sess.panic_strategy();
ret.insert((sym::panic, Some(panic_strategy.desc_symbol()))); 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()); let symbol = Symbol::intern(&s.to_string());
ret.insert((sym::sanitize, Some(symbol))); ret.insert((sym::sanitize, Some(symbol)));
} }

View File

@ -370,7 +370,7 @@ mod desc {
pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
pub const parse_oom_strategy: &str = "either `panic` or `abort`"; 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_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_sanitizer_memory_track_origins: &str = "0, 1, or 2";
pub const parse_cfguard: &str = pub const parse_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@ -684,6 +684,7 @@ mod parse {
"address" => SanitizerSet::ADDRESS, "address" => SanitizerSet::ADDRESS,
"cfi" => SanitizerSet::CFI, "cfi" => SanitizerSet::CFI,
"kcfi" => SanitizerSet::KCFI, "kcfi" => SanitizerSet::KCFI,
"kernel-address" => SanitizerSet::KERNELADDRESS,
"leak" => SanitizerSet::LEAK, "leak" => SanitizerSet::LEAK,
"memory" => SanitizerSet::MEMORY, "memory" => SanitizerSet::MEMORY,
"memtag" => SanitizerSet::MEMTAG, "memtag" => SanitizerSet::MEMTAG,

View File

@ -954,10 +954,10 @@ impl Session {
/// Checks if LLVM lifetime markers should be emitted. /// Checks if LLVM lifetime markers should be emitted.
pub fn emit_lifetime_markers(&self) -> bool { pub fn emit_lifetime_markers(&self) -> bool {
self.opts.optimize != config::OptLevel::No 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. // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
// HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. // 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 { pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {

View File

@ -15,7 +15,7 @@ pub fn target() -> Target {
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
linker: Some("rust-lld".into()), linker: Some("rust-lld".into()),
features: "+v8a,+strict-align,+neon,+fp-armv8".into(), features: "+v8a,+strict-align,+neon,+fp-armv8".into(),
supported_sanitizers: SanitizerSet::KCFI, supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
relocation_model: RelocModel::Static, relocation_model: RelocModel::Static,
disable_redzone: true, disable_redzone: true,
max_atomic_width: Some(128), max_atomic_width: Some(128),

View File

@ -812,6 +812,7 @@ bitflags::bitflags! {
const MEMTAG = 1 << 6; const MEMTAG = 1 << 6;
const SHADOWCALLSTACK = 1 << 7; const SHADOWCALLSTACK = 1 << 7;
const KCFI = 1 << 8; const KCFI = 1 << 8;
const KERNELADDRESS = 1 << 9;
} }
} }
@ -824,6 +825,7 @@ impl SanitizerSet {
SanitizerSet::ADDRESS => "address", SanitizerSet::ADDRESS => "address",
SanitizerSet::CFI => "cfi", SanitizerSet::CFI => "cfi",
SanitizerSet::KCFI => "kcfi", SanitizerSet::KCFI => "kcfi",
SanitizerSet::KERNELADDRESS => "kernel-address",
SanitizerSet::LEAK => "leak", SanitizerSet::LEAK => "leak",
SanitizerSet::MEMORY => "memory", SanitizerSet::MEMORY => "memory",
SanitizerSet::MEMTAG => "memtag", SanitizerSet::MEMTAG => "memtag",
@ -866,6 +868,7 @@ impl IntoIterator for SanitizerSet {
SanitizerSet::SHADOWCALLSTACK, SanitizerSet::SHADOWCALLSTACK,
SanitizerSet::THREAD, SanitizerSet::THREAD,
SanitizerSet::HWADDRESS, SanitizerSet::HWADDRESS,
SanitizerSet::KERNELADDRESS,
] ]
.iter() .iter()
.copied() .copied()
@ -2339,6 +2342,7 @@ impl Target {
Some("address") => SanitizerSet::ADDRESS, Some("address") => SanitizerSet::ADDRESS,
Some("cfi") => SanitizerSet::CFI, Some("cfi") => SanitizerSet::CFI,
Some("kcfi") => SanitizerSet::KCFI, Some("kcfi") => SanitizerSet::KCFI,
Some("kernel-address") => SanitizerSet::KERNELADDRESS,
Some("leak") => SanitizerSet::LEAK, Some("leak") => SanitizerSet::LEAK,
Some("memory") => SanitizerSet::MEMORY, Some("memory") => SanitizerSet::MEMORY,
Some("memtag") => SanitizerSet::MEMTAG, Some("memtag") => SanitizerSet::MEMTAG,

View File

@ -1,6 +1,8 @@
use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
use crate::spec::{RelocModel, Target, TargetOptions}; use crate::spec::{RelocModel, Target, TargetOptions};
use super::SanitizerSet;
pub fn target() -> Target { pub fn target() -> Target {
Target { Target {
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), 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), code_model: Some(CodeModel::Medium),
emit_debug_gdb_scripts: false, emit_debug_gdb_scripts: false,
eh_frame_header: false, eh_frame_header: false,
supported_sanitizers: SanitizerSet::KERNELADDRESS,
..Default::default() ..Default::default()
}, },
} }

View File

@ -1,5 +1,5 @@
use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; 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 { pub fn target() -> Target {
Target { Target {
@ -19,6 +19,7 @@ pub fn target() -> Target {
code_model: Some(CodeModel::Medium), code_model: Some(CodeModel::Medium),
emit_debug_gdb_scripts: false, emit_debug_gdb_scripts: false,
eh_frame_header: false, eh_frame_header: false,
supported_sanitizers: SanitizerSet::KERNELADDRESS,
..Default::default() ..Default::default()
}, },
} }

View File

@ -20,7 +20,7 @@ pub fn target() -> Target {
features: features:
"-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
.into(), .into(),
supported_sanitizers: SanitizerSet::KCFI, supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
disable_redzone: true, disable_redzone: true,
panic_strategy: PanicStrategy::Abort, panic_strategy: PanicStrategy::Abort,
code_model: Some(CodeModel::Kernel), code_model: Some(CodeModel::Kernel),

View File

@ -531,6 +531,24 @@ LLVM KCFI is supported on the following targets:
See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more
details. 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
LeakSanitizer is run-time memory leak detector. 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] * [AddressSanitizer in Clang][clang-asan]
* [ControlFlowIntegrity in Clang][clang-cfi] * [ControlFlowIntegrity in Clang][clang-cfi]
* [HWAddressSanitizer in Clang][clang-hwasan] * [HWAddressSanitizer in Clang][clang-hwasan]
* [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan]
* [LeakSanitizer in Clang][clang-lsan] * [LeakSanitizer in Clang][clang-lsan]
* [MemorySanitizer in Clang][clang-msan] * [MemorySanitizer in Clang][clang-msan]
* [MemTagSanitizer in LLVM][llvm-memtag] * [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-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.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 [llvm-memtag]: https://llvm.org/docs/MemTagSanitizer.html

View File

@ -935,6 +935,7 @@ pub fn make_test_description<R: Read>(
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_cfi = util::CFI_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_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_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_msan = util::MSAN_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); let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
@ -1010,6 +1011,7 @@ pub fn make_test_description<R: Read>(
reason!(!has_asan && config.parse_name_directive(ln, "needs-sanitizer-address")); reason!(!has_asan && config.parse_name_directive(ln, "needs-sanitizer-address"));
reason!(!has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi")); reason!(!has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi"));
reason!(!has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi")); 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_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak"));
reason!(!has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory")); reason!(!has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory"));
reason!(!has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread")); reason!(!has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread"));

View File

@ -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 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] = &[ pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
// FIXME: currently broken, see #88132 // FIXME: currently broken, see #88132
// "aarch64-apple-darwin", // "aarch64-apple-darwin",

View File

@ -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
}

View File

@ -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() {}