From 6df11f16461910efe2b56aa85437af034f135602 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Thu, 1 Feb 2024 13:16:30 -0800 Subject: [PATCH] Add initial support for DataFlowSanitizer Adds initial support for DataFlowSanitizer to the Rust compiler. It currenctly supports `-Zsanitizer-dataflow-abilist` and other options can be specified using `-C llvm-args=`. --- compiler/rustc_codegen_llvm/src/back/write.rs | 10 +++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 ++ compiler/rustc_codegen_ssa/src/back/link.rs | 3 ++ compiler/rustc_codegen_ssa/src/back/write.rs | 5 ++++ compiler/rustc_interface/src/tests.rs | 1 + .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 16 +++++++++++ compiler/rustc_session/src/options.rs | 5 +++- compiler/rustc_target/src/spec/mod.rs | 3 ++ .../spec/targets/x86_64_unknown_linux_gnu.rs | 1 + src/bootstrap/configure.py | 2 +- src/bootstrap/src/core/build_steps/llvm.rs | 2 +- .../src/compiler-flags/sanitizer.md | 28 ++++++++++++++++--- src/tools/compiletest/src/common.rs | 1 + src/tools/compiletest/src/header/needs.rs | 7 +++++ .../dataflow-instrument-functions.rs | 10 +++++++ tests/ui/check-cfg/well-known-values.stderr | 2 +- 16 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 tests/codegen/sanitizer/dataflow-instrument-functions.rs diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 27cb0366f17dc..3ab01a6acf7fe 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -519,12 +519,22 @@ pub(crate) unsafe fn llvm_optimize( let pgo_sample_use_path = get_pgo_sample_use_path(config); let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO; let instr_profile_output_path = get_instr_profile_output_path(config); + let sanitize_dataflow_abilist: Vec<_> = config + .sanitizer_dataflow_abilist + .iter() + .map(|file| CString::new(file.as_str()).unwrap()) + .collect(); + let sanitize_dataflow_abilist1: Vec<_> = + sanitize_dataflow_abilist.iter().map(|file| file.as_ptr()).collect(); // Sanitizer instrumentation is only inserted during the pre-link optimization stage. let sanitizer_options = if !is_lto { Some(llvm::SanitizerOptions { sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS), sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS), sanitize_cfi: config.sanitizer.contains(SanitizerSet::CFI), + sanitize_dataflow: config.sanitizer.contains(SanitizerSet::DATAFLOW), + sanitize_dataflow_abilist: sanitize_dataflow_abilist1.as_ptr(), + sanitize_dataflow_abilist_len: sanitize_dataflow_abilist1.len(), sanitize_kcfi: config.sanitizer.contains(SanitizerSet::KCFI), sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY), sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ee73c6b4756f0..ca0c60ef8bfb2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -482,6 +482,9 @@ pub struct SanitizerOptions { pub sanitize_address: bool, pub sanitize_address_recover: bool, pub sanitize_cfi: bool, + pub sanitize_dataflow: bool, + pub sanitize_dataflow_abilist: *const *const c_char, + pub sanitize_dataflow_abilist_len: size_t, pub sanitize_kcfi: bool, pub sanitize_memory: bool, pub sanitize_memory_recover: bool, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f098fc9cb5970..a8bfb037d0694 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1222,6 +1222,9 @@ fn add_sanitizer_libraries( if sanitizer.contains(SanitizerSet::ADDRESS) { link_sanitizer_runtime(sess, flavor, linker, "asan"); } + if sanitizer.contains(SanitizerSet::DATAFLOW) { + link_sanitizer_runtime(sess, flavor, linker, "dfsan"); + } if sanitizer.contains(SanitizerSet::LEAK) { link_sanitizer_runtime(sess, flavor, linker, "lsan"); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 06edb79453727..8610ccaad132e 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -96,6 +96,7 @@ pub struct ModuleConfig { pub sanitizer: SanitizerSet, pub sanitizer_recover: SanitizerSet, + pub sanitizer_dataflow_abilist: Vec, pub sanitizer_memory_track_origins: usize, // Flags indicating which outputs to produce. @@ -198,6 +199,10 @@ impl ModuleConfig { ), sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()), + sanitizer_dataflow_abilist: if_regular!( + sess.opts.unstable_opts.sanitizer_dataflow_abilist.clone(), + Vec::new() + ), sanitizer_recover: if_regular!( sess.opts.unstable_opts.sanitizer_recover, SanitizerSet::empty() diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 2d4963a8b901a..e84df7ae8872a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -810,6 +810,7 @@ fn test_unstable_options_tracking_hash() { tracked!(sanitizer_cfi_canonical_jump_tables, None); tracked!(sanitizer_cfi_generalize_pointers, Some(true)); tracked!(sanitizer_cfi_normalize_integers, Some(true)); + tracked!(sanitizer_dataflow_abilist, Vec::::new()); tracked!(sanitizer_memory_track_origins, 2); tracked!(sanitizer_recover, SanitizerSet::ADDRESS); tracked!(saturating_float_casts, Some(true)); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 6114f7c867807..7f0731fe0c444 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -42,6 +42,7 @@ #endif #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" +#include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" @@ -683,6 +684,9 @@ struct LLVMRustSanitizerOptions { bool SanitizeAddress; bool SanitizeAddressRecover; bool SanitizeCFI; + bool SanitizeDataFlow; + char** SanitizeDataFlowABIList; + size_t SanitizeDataFlowABIListLen; bool SanitizeKCFI; bool SanitizeMemory; bool SanitizeMemoryRecover; @@ -868,6 +872,18 @@ LLVMRustOptimize( } if (SanitizerOptions) { + if (SanitizerOptions->SanitizeDataFlow) { + std::vector ABIListFiles( + SanitizerOptions->SanitizeDataFlowABIList, + SanitizerOptions->SanitizeDataFlowABIList + + SanitizerOptions->SanitizeDataFlowABIListLen); + OptimizerLastEPCallbacks.push_back( + [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) { + MPM.addPass(DataFlowSanitizerPass(ABIListFiles)); + } + ); + } + if (SanitizerOptions->SanitizeMemory) { MemorySanitizerOptions Options( SanitizerOptions->SanitizeMemoryTrackOrigins, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index d8d201d5f244d..2e26b96031904 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -381,7 +381,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`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `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`"; @@ -718,6 +718,7 @@ mod parse { *slot |= match s { "address" => SanitizerSet::ADDRESS, "cfi" => SanitizerSet::CFI, + "dataflow" => SanitizerSet::DATAFLOW, "kcfi" => SanitizerSet::KCFI, "kernel-address" => SanitizerSet::KERNELADDRESS, "leak" => SanitizerSet::LEAK, @@ -1841,6 +1842,8 @@ written to standard error output)"), "enable generalizing pointer types (default: no)"), sanitizer_cfi_normalize_integers: Option = (None, parse_opt_bool, [TRACKED], "enable normalizing integer types (default: no)"), + sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_list, [UNTRACKED], + "additional ABI list files that control how shadow parameters are passed (space separated)"), sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], "enable origins tracking in MemorySanitizer"), sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ead3be7fd529b..7eb674076006b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1221,6 +1221,7 @@ bitflags::bitflags! { const KCFI = 1 << 8; const KERNELADDRESS = 1 << 9; const SAFESTACK = 1 << 10; + const DATAFLOW = 1 << 11; } } rustc_data_structures::external_bitflags_debug! { SanitizerSet } @@ -1233,6 +1234,7 @@ impl SanitizerSet { Some(match self { SanitizerSet::ADDRESS => "address", SanitizerSet::CFI => "cfi", + SanitizerSet::DATAFLOW => "dataflow", SanitizerSet::KCFI => "kcfi", SanitizerSet::KERNELADDRESS => "kernel-address", SanitizerSet::LEAK => "leak", @@ -2782,6 +2784,7 @@ impl Target { base.$key_name |= match s.as_str() { Some("address") => SanitizerSet::ADDRESS, Some("cfi") => SanitizerSet::CFI, + Some("dataflow") => SanitizerSet::DATAFLOW, Some("kcfi") => SanitizerSet::KCFI, Some("kernel-address") => SanitizerSet::KERNELADDRESS, Some("leak") => SanitizerSet::LEAK, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 2296b58f45dc0..1510f3b390cd2 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -10,6 +10,7 @@ pub fn target() -> Target { base.static_position_independent_executables = true; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI + | SanitizerSet::DATAFLOW | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::SAFESTACK diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index d34c19a47e3fb..8b65e8ff9c3e1 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -48,7 +48,7 @@ def v(*args): o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") o("vendor", "build.vendor", "enable usage of vendored Rust crates") -o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)") +o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)") o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") o("profiler", "build.profiler", "build the profiler runtime") diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 4b2d3e9ab4b75..c357d0532eb64 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1093,7 +1093,7 @@ fn supported_sanitizers( "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]), "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]), "x86_64-unknown-linux-gnu" => { - common_libs("linux", "x86_64", &["asan", "lsan", "msan", "safestack", "tsan"]) + common_libs("linux", "x86_64", &["asan", "dfsan", "lsan", "msan", "safestack", "tsan"]) } "x86_64-unknown-linux-musl" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 502853f39ae41..1bda078043ade 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -12,6 +12,7 @@ This feature allows for use of one of following sanitizers: * [AddressSanitizer](#addresssanitizer) a fast memory error detector. * [ControlFlowIntegrity](#controlflowintegrity) LLVM Control Flow Integrity (CFI) provides forward-edge control flow protection. +* [DataFlowSanitizer](#dataflowsanitizer) a generic dynamic data flow analysis framework. * [HWAddressSanitizer](#hwaddresssanitizer) a memory error detector similar to AddressSanitizer, but based on partial hardware assistance. * [KernelControlFlowIntegrity](#kernelcontrolflowintegrity) LLVM Kernel Control @@ -25,10 +26,12 @@ This feature allows for use of one of following sanitizers: * [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection (aarch64 only). * [ThreadSanitizer](#threadsanitizer) a fast data race detector. -To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`, -`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`, -`-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or `-Zsanitizer=thread`. -You might also need the `--target` and `build-std` flags. Example: +To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=cfi`, +`-Zsanitizer=dataflow`,`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, +`-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or +`-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. + +Example: ```shell $ RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std --target x86_64-unknown-linux-gnu ``` @@ -625,6 +628,21 @@ LLVM KCFI is supported on the following targets: See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more details. +# DataFlowSanitizer + +DataFlowSanitizer is a generalised dynamic data flow analysis. + +Unlike other Sanitizer tools, this tool is not designed to detect a specific +class of bugs on its own. Instead, it provides a generic dynamic data flow +analysis framework to be used by clients to help detect application-specific +issues within their own code. + +DataFlowSanitizer is supported on the following targets: + +* `x86_64-unknown-linux-gnu` + +See the [Clang DataFlowSanitizer documentation][clang-dataflow] for more details. + # KernelAddressSanitizer KernelAddressSanitizer (KASAN) is a freestanding version of AddressSanitizer @@ -835,6 +853,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [Sanitizers project page](https://github.com/google/sanitizers/wiki/) * [AddressSanitizer in Clang][clang-asan] * [ControlFlowIntegrity in Clang][clang-cfi] +* [DataFlowSanitizer in Clang][clang-dataflow] * [HWAddressSanitizer in Clang][clang-hwasan] * [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan] * [LeakSanitizer in Clang][clang-lsan] @@ -844,6 +863,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html [clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html +[clang-dataflow]: https://clang.llvm.org/docs/DataFlowSanitizer.html [clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html [clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 49f1226e2cc38..bfe6c959e7ce5 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -156,6 +156,7 @@ impl PanicStrategy { pub enum Sanitizer { Address, Cfi, + Dataflow, Kcfi, KernelAddress, Leak, diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index 9b22b2112a8c0..3978658815053 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -29,6 +29,11 @@ pub(super) fn handle_needs( condition: cache.sanitizer_cfi, ignore_reason: "ignored on targets without CFI sanitizer", }, + Need { + name: "needs-sanitizer-dataflow", + condition: cache.sanitizer_dataflow, + ignore_reason: "ignored on targets without dataflow sanitizer", + }, Need { name: "needs-sanitizer-kcfi", condition: cache.sanitizer_kcfi, @@ -190,6 +195,7 @@ pub(super) struct CachedNeedsConditions { sanitizer_support: bool, sanitizer_address: bool, sanitizer_cfi: bool, + sanitizer_dataflow: bool, sanitizer_kcfi: bool, sanitizer_kasan: bool, sanitizer_leak: bool, @@ -229,6 +235,7 @@ impl CachedNeedsConditions { sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), sanitizer_address: sanitizers.contains(&Sanitizer::Address), sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi), + sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow), sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi), sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress), sanitizer_leak: sanitizers.contains(&Sanitizer::Leak), diff --git a/tests/codegen/sanitizer/dataflow-instrument-functions.rs b/tests/codegen/sanitizer/dataflow-instrument-functions.rs new file mode 100644 index 0000000000000..368b89653ea8d --- /dev/null +++ b/tests/codegen/sanitizer/dataflow-instrument-functions.rs @@ -0,0 +1,10 @@ +// Verifies that functions are instrumented. +// +// needs-sanitizer-dataflow +// compile-flags: -Copt-level=0 -Zsanitizer=dataflow + +#![crate_type="lib"] + +pub fn foo() { +} +// CHECK: define{{.*}}foo{{.*}}.dfsan diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index d7d538c0b9e3e..29c377a6238ca 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -89,7 +89,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | sanitize = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `sanitize` are: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread` + = note: expected values for `sanitize` are: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`