Skip to content

Commit

Permalink
Add initial support for DataFlowSanitizer
Browse files Browse the repository at this point in the history
Adds initial support for DataFlowSanitizer to the Rust compiler. It
currently supports `-Zsanitizer-dataflow-abilist`. Additional options
for it can be passed to LLVM command line argument processor via LLVM
arguments using `llvm-args` codegen option (e.g.,
`-Cllvm-args=-dfsan-combine-pointer-labels-on-load=false`).
  • Loading branch information
rcvalle committed Feb 27, 2024
1 parent fc3800f commit 826f1d7
Show file tree
Hide file tree
Showing 20 changed files with 199 additions and 10 deletions.
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_abilist_ptrs: 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_abilist_ptrs.as_ptr(),
sanitize_dataflow_abilist_len: sanitize_dataflow_abilist_ptrs.len(),
sanitize_kcfi: config.sanitizer.contains(SanitizerSet::KCFI),
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,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,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,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");
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub struct ModuleConfig {

pub sanitizer: SanitizerSet,
pub sanitizer_recover: SanitizerSet,
pub sanitizer_dataflow_abilist: Vec<String>,
pub sanitizer_memory_track_origins: usize,

// Flags indicating which outputs to produce.
Expand Down Expand Up @@ -197,6 +198,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()
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,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![String::from("/rustc/abc")]);
tracked!(sanitizer_memory_track_origins, 2);
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
tracked!(saturating_float_casts, Some(true));
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
#if LLVM_VERSION_GE(19, 0)
#include "llvm/Support/PGOOptions.h"
Expand Down Expand Up @@ -686,6 +687,9 @@ struct LLVMRustSanitizerOptions {
bool SanitizeAddress;
bool SanitizeAddressRecover;
bool SanitizeCFI;
bool SanitizeDataFlow;
char **SanitizeDataFlowABIList;
size_t SanitizeDataFlowABIListLen;
bool SanitizeKCFI;
bool SanitizeMemory;
bool SanitizeMemoryRecover;
Expand Down Expand Up @@ -883,6 +887,18 @@ LLVMRustOptimize(
}

if (SanitizerOptions) {
if (SanitizerOptions->SanitizeDataFlow) {
std::vector<std::string> 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,
Expand Down
20 changes: 18 additions & 2 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ mod desc {
pub const parse_list: &str = "a space-separated list of strings";
pub const parse_list_with_polarity: &str =
"a comma-separated list of strings, with elements beginning with + or -";
pub const parse_opt_comma_list: &str = "a comma-separated list of strings";
pub const parse_comma_list: &str = "a comma-separated list of strings";
pub const parse_opt_comma_list: &str = parse_comma_list;
pub const parse_number: &str = "a number";
pub const parse_opt_number: &str = parse_number;
pub const parse_threads: &str = parse_number;
Expand All @@ -381,7 +382,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`";
Expand Down Expand Up @@ -602,6 +603,18 @@ mod parse {
}
}

pub(crate) fn parse_comma_list(slot: &mut Vec<String>, v: Option<&str>) -> bool {
match v {
Some(s) => {
let mut v: Vec<_> = s.split(',').map(|s| s.to_string()).collect();
v.sort_unstable();
*slot = v;
true
}
None => false,
}
}

pub(crate) fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) -> bool {
match v {
Some(s) => {
Expand Down Expand Up @@ -718,6 +731,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,
Expand Down Expand Up @@ -1846,6 +1860,8 @@ written to standard error output)"),
"enable generalizing pointer types (default: no)"),
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable normalizing integer types (default: no)"),
sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED],
"additional ABI list files that control how shadow parameters are passed (comma 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],
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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",
Expand Down Expand Up @@ -2790,6 +2792,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,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"])
Expand Down
34 changes: 30 additions & 4 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ This feature allows for use of one of following sanitizers:
* Those that apart from testing, may be used in production:
* [ControlFlowIntegrity](#controlflowintegrity) LLVM Control Flow Integrity
(CFI) provides forward-edge control flow protection.
* [DataFlowSanitizer](#dataflowsanitizer) a generic dynamic data flow analysis
framework.
* [KernelControlFlowIntegrity](#kernelcontrolflowintegrity) LLVM Kernel
Control Flow Integrity (KCFI) provides forward-edge control flow protection
for operating systems kernels.
Expand All @@ -39,14 +41,21 @@ This feature allows for use of one of following sanitizers:
* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow
protection (aarch64 only).

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
```

Additional options for sanitizers can be passed to LLVM command line argument
processor via LLVM arguments using `llvm-args` codegen option (e.g.,
`-Cllvm-args=-dfsan-combine-pointer-labels-on-load=false`). See the sanitizer
documentation for more information about additional options.

# AddressSanitizer

AddressSanitizer is a memory error detector. It can detect the following types
Expand Down Expand Up @@ -639,6 +648,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
Expand Down Expand Up @@ -849,6 +873,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]
Expand All @@ -858,6 +883,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
Expand Down
1 change: 1 addition & 0 deletions src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ impl PanicStrategy {
pub enum Sanitizer {
Address,
Cfi,
Dataflow,
Kcfi,
KernelAddress,
Leak,
Expand Down
7 changes: 7 additions & 0 deletions src/tools/compiletest/src/header/needs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down
6 changes: 5 additions & 1 deletion src/tools/rustfmt/config_proc_macro/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
match &attr.meta {
syn::Meta::NameValue(syn::MetaNameValue {
path,
value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }),
value:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit_str),
..
}),
..
}) if path.is_ident(name) => Some(lit_str.value()),
_ => None,
Expand Down
10 changes: 10 additions & 0 deletions tests/codegen/sanitizer/dataflow-instrument-functions.rs
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/ui/check-cfg/well-known-values.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,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 <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration

warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
fun:__rdl_alloc=uninstrumented
fun:__rdl_alloc_zeroed=uninstrumented
fun:__rdl_dealloc=uninstrumented
fun:__rdl_realloc=uninstrumented
fun:__rg_oom=uninstrumented
fun:__rust_alloc=uninstrumented
fun:__rust_alloc_error_handler=uninstrumented
fun:__rust_alloc_zeroed=uninstrumented
fun:__rust_dealloc=uninstrumented
fun:__rust_realloc=uninstrumented
fun:_ZN4core*=uninstrumented
fun:_ZN3std*=uninstrumented
fun:rust_eh_personality=uninstrumented
11 changes: 11 additions & 0 deletions tests/ui/sanitize/sanitizer-dataflow-abilist-rust.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fun:__rdl_alloc=uninstrumented
fun:__rdl_alloc_zeroed=uninstrumented
fun:__rdl_dealloc=uninstrumented
fun:__rdl_realloc=uninstrumented
fun:__rg_oom=uninstrumented
fun:__rust_alloc=uninstrumented
fun:__rust_alloc_error_handler=uninstrumented
fun:__rust_alloc_zeroed=uninstrumented
fun:__rust_dealloc=uninstrumented
fun:__rust_realloc=uninstrumented
fun:rust_eh_personality=uninstrumented
Loading

0 comments on commit 826f1d7

Please sign in to comment.