Skip to content

Commit fabfd1f

Browse files
committed
Auto merge of #99679 - repnop:kernel-address-sanitizer, r=cuviper
Add `kernel-address` sanitizer support for freestanding targets This PR adds support for KASan (kernel address sanitizer) instrumentation in freestanding targets. I included the minimal set of `x86_64-unknown-none`, `riscv64{imac, gc}-unknown-none-elf`, and `aarch64-unknown-none` but there's likely other targets it can be added to. (`linux_kernel_base.rs`?) KASan uses the address sanitizer attributes but has the `CompileKernel` parameter set to `true` in the pass creation.
2 parents a9842c7 + 1971438 commit fabfd1f

File tree

18 files changed

+142
-12
lines changed

18 files changed

+142
-12
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub fn sanitize_attrs<'ll>(
6262
) -> SmallVec<[&'ll Attribute; 4]> {
6363
let mut attrs = SmallVec::new();
6464
let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
65-
if enabled.contains(SanitizerSet::ADDRESS) {
65+
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
6666
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
6767
}
6868
if enabled.contains(SanitizerSet::MEMORY) {

compiler/rustc_codegen_llvm/src/back/write.rs

+4
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ pub(crate) unsafe fn llvm_optimize(
442442
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
443443
sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS),
444444
sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS),
445+
sanitize_kernel_address: config.sanitizer.contains(SanitizerSet::KERNELADDRESS),
446+
sanitize_kernel_address_recover: config
447+
.sanitizer_recover
448+
.contains(SanitizerSet::KERNELADDRESS),
445449
})
446450
} else {
447451
None

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ pub struct SanitizerOptions {
482482
pub sanitize_thread: bool,
483483
pub sanitize_hwaddress: bool,
484484
pub sanitize_hwaddress_recover: bool,
485+
pub sanitize_kernel_address: bool,
486+
pub sanitize_kernel_address_recover: bool,
485487
}
486488

487489
/// LLVMRelocMode

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
295295
if let Some(list) = attr.meta_item_list() {
296296
for item in list.iter() {
297297
if item.has_name(sym::address) {
298-
codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
298+
codegen_fn_attrs.no_sanitize |=
299+
SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS;
299300
} else if item.has_name(sym::cfi) {
300301
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
301302
} else if item.has_name(sym::kcfi) {

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,8 @@ struct LLVMRustSanitizerOptions {
594594
bool SanitizeThread;
595595
bool SanitizeHWAddress;
596596
bool SanitizeHWAddressRecover;
597+
bool SanitizeKernelAddress;
598+
bool SanitizeKernelAddressRecover;
597599
};
598600

599601
extern "C" LLVMRustResult
@@ -765,15 +767,17 @@ LLVMRustOptimize(
765767
);
766768
}
767769

768-
if (SanitizerOptions->SanitizeAddress) {
770+
if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) {
769771
OptimizerLastEPCallbacks.push_back(
770772
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
773+
auto CompileKernel = SanitizerOptions->SanitizeKernelAddress;
771774
#if LLVM_VERSION_LT(15, 0)
772775
MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
773776
#endif
774777
AddressSanitizerOptions opts = AddressSanitizerOptions{
775-
/*CompileKernel=*/false,
776-
SanitizerOptions->SanitizeAddressRecover,
778+
CompileKernel,
779+
SanitizerOptions->SanitizeAddressRecover
780+
|| SanitizerOptions->SanitizeKernelAddressRecover,
777781
/*UseAfterScope=*/true,
778782
AsanDetectStackUseAfterReturnMode::Runtime,
779783
};

compiler/rustc_session/src/config.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1022,7 +1022,13 @@ fn default_configuration(sess: &Session) -> CrateConfig {
10221022
let panic_strategy = sess.panic_strategy();
10231023
ret.insert((sym::panic, Some(panic_strategy.desc_symbol())));
10241024

1025-
for s in sess.opts.unstable_opts.sanitizer {
1025+
for mut s in sess.opts.unstable_opts.sanitizer {
1026+
// KASAN should use the same attribute name as ASAN, as it's still ASAN
1027+
// under the hood
1028+
if s == SanitizerSet::KERNELADDRESS {
1029+
s = SanitizerSet::ADDRESS;
1030+
}
1031+
10261032
let symbol = Symbol::intern(&s.to_string());
10271033
ret.insert((sym::sanitize, Some(symbol)));
10281034
}

compiler/rustc_session/src/options.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ mod desc {
370370
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
371371
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
372372
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
373-
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
373+
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
374374
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
375375
pub const parse_cfguard: &str =
376376
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@@ -684,6 +684,7 @@ mod parse {
684684
"address" => SanitizerSet::ADDRESS,
685685
"cfi" => SanitizerSet::CFI,
686686
"kcfi" => SanitizerSet::KCFI,
687+
"kernel-address" => SanitizerSet::KERNELADDRESS,
687688
"leak" => SanitizerSet::LEAK,
688689
"memory" => SanitizerSet::MEMORY,
689690
"memtag" => SanitizerSet::MEMTAG,

compiler/rustc_session/src/session.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -954,10 +954,10 @@ impl Session {
954954
/// Checks if LLVM lifetime markers should be emitted.
955955
pub fn emit_lifetime_markers(&self) -> bool {
956956
self.opts.optimize != config::OptLevel::No
957-
// AddressSanitizer uses lifetimes to detect use after scope bugs.
957+
// AddressSanitizer and KernelAddressSanitizer uses lifetimes to detect use after scope bugs.
958958
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
959959
// HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
960-
|| self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
960+
|| self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
961961
}
962962

963963
pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {

compiler/rustc_target/src/spec/aarch64_unknown_none.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn target() -> Target {
1515
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
1616
linker: Some("rust-lld".into()),
1717
features: "+v8a,+strict-align,+neon,+fp-armv8".into(),
18-
supported_sanitizers: SanitizerSet::KCFI,
18+
supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
1919
relocation_model: RelocModel::Static,
2020
disable_redzone: true,
2121
max_atomic_width: Some(128),

compiler/rustc_target/src/spec/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ bitflags::bitflags! {
812812
const MEMTAG = 1 << 6;
813813
const SHADOWCALLSTACK = 1 << 7;
814814
const KCFI = 1 << 8;
815+
const KERNELADDRESS = 1 << 9;
815816
}
816817
}
817818

@@ -824,6 +825,7 @@ impl SanitizerSet {
824825
SanitizerSet::ADDRESS => "address",
825826
SanitizerSet::CFI => "cfi",
826827
SanitizerSet::KCFI => "kcfi",
828+
SanitizerSet::KERNELADDRESS => "kernel-address",
827829
SanitizerSet::LEAK => "leak",
828830
SanitizerSet::MEMORY => "memory",
829831
SanitizerSet::MEMTAG => "memtag",
@@ -866,6 +868,7 @@ impl IntoIterator for SanitizerSet {
866868
SanitizerSet::SHADOWCALLSTACK,
867869
SanitizerSet::THREAD,
868870
SanitizerSet::HWADDRESS,
871+
SanitizerSet::KERNELADDRESS,
869872
]
870873
.iter()
871874
.copied()
@@ -2341,6 +2344,7 @@ impl Target {
23412344
Some("address") => SanitizerSet::ADDRESS,
23422345
Some("cfi") => SanitizerSet::CFI,
23432346
Some("kcfi") => SanitizerSet::KCFI,
2347+
Some("kernel-address") => SanitizerSet::KERNELADDRESS,
23442348
Some("leak") => SanitizerSet::LEAK,
23452349
Some("memory") => SanitizerSet::MEMORY,
23462350
Some("memtag") => SanitizerSet::MEMTAG,

compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
22
use crate::spec::{RelocModel, Target, TargetOptions};
33

4+
use super::SanitizerSet;
5+
46
pub fn target() -> Target {
57
Target {
68
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
@@ -20,6 +22,7 @@ pub fn target() -> Target {
2022
code_model: Some(CodeModel::Medium),
2123
emit_debug_gdb_scripts: false,
2224
eh_frame_header: false,
25+
supported_sanitizers: SanitizerSet::KERNELADDRESS,
2326
..Default::default()
2427
},
2528
}

compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::spec::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
2-
use crate::spec::{RelocModel, Target, TargetOptions};
2+
use crate::spec::{RelocModel, SanitizerSet, Target, TargetOptions};
33

44
pub fn target() -> Target {
55
Target {
@@ -19,6 +19,7 @@ pub fn target() -> Target {
1919
code_model: Some(CodeModel::Medium),
2020
emit_debug_gdb_scripts: false,
2121
eh_frame_header: false,
22+
supported_sanitizers: SanitizerSet::KERNELADDRESS,
2223
..Default::default()
2324
},
2425
}

compiler/rustc_target/src/spec/x86_64_unknown_none.rs

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

src/doc/unstable-book/src/compiler-flags/sanitizer.md

+20
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,24 @@ LLVM KCFI is supported on the following targets:
531531
See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more
532532
details.
533533
534+
# KernelAddressSanitizer
535+
536+
KernelAddressSanitizer (KASAN) is a freestanding version of AddressSanitizer
537+
which is suitable for detecting memory errors in programs which do not have a
538+
runtime environment, such as operating system kernels. KernelAddressSanitizer
539+
requires manual implementation of the underlying functions used for tracking
540+
KernelAddressSanitizer state.
541+
542+
KernelAddressSanitizer is supported on the following targets:
543+
544+
* `aarch64-unknown-none`
545+
* `riscv64gc-unknown-none-elf`
546+
* `riscv64imac-unknown-none-elf`
547+
* `x86_64-unknown-none`
548+
549+
See the [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan] for
550+
more details.
551+
534552
# LeakSanitizer
535553
536554
LeakSanitizer is run-time memory leak detector.
@@ -714,6 +732,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
714732
* [AddressSanitizer in Clang][clang-asan]
715733
* [ControlFlowIntegrity in Clang][clang-cfi]
716734
* [HWAddressSanitizer in Clang][clang-hwasan]
735+
* [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan]
717736
* [LeakSanitizer in Clang][clang-lsan]
718737
* [MemorySanitizer in Clang][clang-msan]
719738
* [MemTagSanitizer in LLVM][llvm-memtag]
@@ -727,4 +746,5 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
727746
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
728747
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
729748
[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
749+
[linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html
730750
[llvm-memtag]: https://llvm.org/docs/MemTagSanitizer.html

src/tools/compiletest/src/header.rs

+2
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,7 @@ pub fn make_test_description<R: Read>(
935935
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
936936
let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
937937
let has_kcfi = util::KCFI_SUPPORTED_TARGETS.contains(&&*config.target);
938+
let has_kasan = util::KASAN_SUPPORTED_TARGETS.contains(&&*config.target);
938939
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
939940
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
940941
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
@@ -1010,6 +1011,7 @@ pub fn make_test_description<R: Read>(
10101011
reason!(!has_asan && config.parse_name_directive(ln, "needs-sanitizer-address"));
10111012
reason!(!has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi"));
10121013
reason!(!has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi"));
1014+
reason!(!has_kasan && config.parse_name_directive(ln, "needs-sanitizer-kasan"));
10131015
reason!(!has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak"));
10141016
reason!(!has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory"));
10151017
reason!(!has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread"));

src/tools/compiletest/src/util.rs

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ pub const CFI_SUPPORTED_TARGETS: &[&str] = &[
4545

4646
pub const KCFI_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-none", "x86_64-linux-none"];
4747

48+
pub const KASAN_SUPPORTED_TARGETS: &[&str] = &[
49+
"aarch64-unknown-none",
50+
"riscv64gc-unknown-none-elf",
51+
"riscv64imac-unknown-none-elf",
52+
"x86_64-unknown-none",
53+
];
54+
4855
pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
4956
// FIXME: currently broken, see #88132
5057
// "aarch64-apple-darwin",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Verifies that `-Zsanitizer=kernel-address` emits sanitizer instrumentation.
2+
3+
// compile-flags: -Zsanitizer=kernel-address
4+
// revisions: aarch64 riscv64imac riscv64gc x86_64
5+
//[aarch64] compile-flags: --target aarch64-unknown-none
6+
//[aarch64] needs-llvm-components: aarch64
7+
//[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf
8+
//[riscv64imac] needs-llvm-components: riscv
9+
//[riscv64imac] min-llvm-version: 16
10+
//[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf
11+
//[riscv64gc] needs-llvm-components: riscv
12+
//[riscv64gc] min-llvm-version: 16
13+
//[x86_64] compile-flags: --target x86_64-unknown-none
14+
//[x86_64] needs-llvm-components: x86
15+
16+
#![crate_type = "rlib"]
17+
#![feature(no_core, no_sanitize, lang_items)]
18+
#![no_core]
19+
20+
#[lang = "sized"]
21+
trait Sized {}
22+
23+
#[lang = "copy"]
24+
trait Copy {}
25+
26+
impl Copy for u8 {}
27+
28+
// CHECK-LABEL: ; sanitizer_kasan_emits_instrumentation::unsanitized
29+
// CHECK-NEXT: ; Function Attrs:
30+
// CHECK-NOT: sanitize_address
31+
// CHECK: start:
32+
// CHECK-NOT: call void @__asan_report_load
33+
// CHECK: }
34+
#[no_sanitize(address)]
35+
pub fn unsanitized(b: &mut u8) -> u8 {
36+
*b
37+
}
38+
39+
// CHECK-LABEL: ; sanitizer_kasan_emits_instrumentation::sanitized
40+
// CHECK-NEXT: ; Function Attrs:
41+
// CHECK: sanitize_address
42+
// CHECK: start:
43+
// CHECK: call void @__asan_report_load
44+
// CHECK: }
45+
pub fn sanitized(b: &mut u8) -> u8 {
46+
*b
47+
}

tests/ui/sanitize/cfg-kasan.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Verifies that when compiling with -Zsanitizer=kernel-address,
2+
// the `#[cfg(sanitize = "address")]` attribute is configured.
3+
4+
// check-pass
5+
// compile-flags: -Zsanitizer=kernel-address --cfg kernel_address
6+
// revisions: aarch64 riscv64imac riscv64gc x86_64
7+
//[aarch64] compile-flags: --target aarch64-unknown-none
8+
//[aarch64] needs-llvm-components: aarch64
9+
//[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf
10+
//[riscv64imac] needs-llvm-components: riscv
11+
//[riscv64imac] min-llvm-version: 16
12+
//[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf
13+
//[riscv64gc] needs-llvm-components: riscv
14+
//[riscv64gc] min-llvm-version: 16
15+
//[x86_64] compile-flags: --target x86_64-unknown-none
16+
//[x86_64] needs-llvm-components: x86
17+
18+
#![crate_type = "rlib"]
19+
#![feature(cfg_sanitize, no_core, lang_items)]
20+
#![no_core]
21+
22+
#[lang = "sized"]
23+
trait Sized {}
24+
25+
const _: fn() -> () = main;
26+
27+
#[cfg(all(sanitize = "address", kernel_address))]
28+
fn main() {}

0 commit comments

Comments
 (0)