Skip to content

Commit 02102b6

Browse files
committed
Add initial support for DataFlowSanitizer
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`).
1 parent fc3800f commit 02102b6

File tree

20 files changed

+681
-10
lines changed

20 files changed

+681
-10
lines changed

compiler/rustc_codegen_llvm/src/back/write.rs

+10
Original file line numberDiff line numberDiff line change
@@ -519,12 +519,22 @@ pub(crate) unsafe fn llvm_optimize(
519519
let pgo_sample_use_path = get_pgo_sample_use_path(config);
520520
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
521521
let instr_profile_output_path = get_instr_profile_output_path(config);
522+
let sanitize_dataflow_abilist: Vec<_> = config
523+
.sanitizer_dataflow_abilist
524+
.iter()
525+
.map(|file| CString::new(file.as_str()).unwrap())
526+
.collect();
527+
let sanitize_dataflow_abilist_ptrs: Vec<_> =
528+
sanitize_dataflow_abilist.iter().map(|file| file.as_ptr()).collect();
522529
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
523530
let sanitizer_options = if !is_lto {
524531
Some(llvm::SanitizerOptions {
525532
sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS),
526533
sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS),
527534
sanitize_cfi: config.sanitizer.contains(SanitizerSet::CFI),
535+
sanitize_dataflow: config.sanitizer.contains(SanitizerSet::DATAFLOW),
536+
sanitize_dataflow_abilist: sanitize_dataflow_abilist_ptrs.as_ptr(),
537+
sanitize_dataflow_abilist_len: sanitize_dataflow_abilist_ptrs.len(),
528538
sanitize_kcfi: config.sanitizer.contains(SanitizerSet::KCFI),
529539
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
530540
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+3
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,9 @@ pub struct SanitizerOptions {
480480
pub sanitize_address: bool,
481481
pub sanitize_address_recover: bool,
482482
pub sanitize_cfi: bool,
483+
pub sanitize_dataflow: bool,
484+
pub sanitize_dataflow_abilist: *const *const c_char,
485+
pub sanitize_dataflow_abilist_len: size_t,
483486
pub sanitize_kcfi: bool,
484487
pub sanitize_memory: bool,
485488
pub sanitize_memory_recover: bool,

compiler/rustc_codegen_ssa/src/back/link.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,9 @@ fn add_sanitizer_libraries(
12181218
if sanitizer.contains(SanitizerSet::ADDRESS) {
12191219
link_sanitizer_runtime(sess, flavor, linker, "asan");
12201220
}
1221+
if sanitizer.contains(SanitizerSet::DATAFLOW) {
1222+
link_sanitizer_runtime(sess, flavor, linker, "dfsan");
1223+
}
12211224
if sanitizer.contains(SanitizerSet::LEAK) {
12221225
link_sanitizer_runtime(sess, flavor, linker, "lsan");
12231226
}

compiler/rustc_codegen_ssa/src/back/write.rs

+5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub struct ModuleConfig {
9595

9696
pub sanitizer: SanitizerSet,
9797
pub sanitizer_recover: SanitizerSet,
98+
pub sanitizer_dataflow_abilist: Vec<String>,
9899
pub sanitizer_memory_track_origins: usize,
99100

100101
// Flags indicating which outputs to produce.
@@ -197,6 +198,10 @@ impl ModuleConfig {
197198
),
198199

199200
sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()),
201+
sanitizer_dataflow_abilist: if_regular!(
202+
sess.opts.unstable_opts.sanitizer_dataflow_abilist.clone(),
203+
Vec::new()
204+
),
200205
sanitizer_recover: if_regular!(
201206
sess.opts.unstable_opts.sanitizer_recover,
202207
SanitizerSet::empty()

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,7 @@ fn test_unstable_options_tracking_hash() {
811811
tracked!(sanitizer_cfi_canonical_jump_tables, None);
812812
tracked!(sanitizer_cfi_generalize_pointers, Some(true));
813813
tracked!(sanitizer_cfi_normalize_integers, Some(true));
814+
tracked!(sanitizer_dataflow_abilist, vec![String::from("/rustc/abc")]);
814815
tracked!(sanitizer_memory_track_origins, 2);
815816
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
816817
tracked!(saturating_float_casts, Some(true));

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#endif
4343
#include "llvm/Transforms/Instrumentation.h"
4444
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
45+
#include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h"
4546
#include "llvm/Support/TimeProfiler.h"
4647
#if LLVM_VERSION_GE(19, 0)
4748
#include "llvm/Support/PGOOptions.h"
@@ -686,6 +687,9 @@ struct LLVMRustSanitizerOptions {
686687
bool SanitizeAddress;
687688
bool SanitizeAddressRecover;
688689
bool SanitizeCFI;
690+
bool SanitizeDataFlow;
691+
char **SanitizeDataFlowABIList;
692+
size_t SanitizeDataFlowABIListLen;
689693
bool SanitizeKCFI;
690694
bool SanitizeMemory;
691695
bool SanitizeMemoryRecover;
@@ -883,6 +887,18 @@ LLVMRustOptimize(
883887
}
884888

885889
if (SanitizerOptions) {
890+
if (SanitizerOptions->SanitizeDataFlow) {
891+
std::vector<std::string> ABIListFiles(
892+
SanitizerOptions->SanitizeDataFlowABIList,
893+
SanitizerOptions->SanitizeDataFlowABIList +
894+
SanitizerOptions->SanitizeDataFlowABIListLen);
895+
OptimizerLastEPCallbacks.push_back(
896+
[ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) {
897+
MPM.addPass(DataFlowSanitizerPass(ABIListFiles));
898+
}
899+
);
900+
}
901+
886902
if (SanitizerOptions->SanitizeMemory) {
887903
MemorySanitizerOptions Options(
888904
SanitizerOptions->SanitizeMemoryTrackOrigins,

compiler/rustc_session/src/options.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,8 @@ mod desc {
371371
pub const parse_list: &str = "a space-separated list of strings";
372372
pub const parse_list_with_polarity: &str =
373373
"a comma-separated list of strings, with elements beginning with + or -";
374-
pub const parse_opt_comma_list: &str = "a comma-separated list of strings";
374+
pub const parse_comma_list: &str = "a comma-separated list of strings";
375+
pub const parse_opt_comma_list: &str = parse_comma_list;
375376
pub const parse_number: &str = "a number";
376377
pub const parse_opt_number: &str = parse_number;
377378
pub const parse_threads: &str = parse_number;
@@ -381,7 +382,7 @@ mod desc {
381382
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
382383
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
383384
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
384-
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`";
385+
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`";
385386
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
386387
pub const parse_cfguard: &str =
387388
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@@ -602,6 +603,18 @@ mod parse {
602603
}
603604
}
604605

606+
pub(crate) fn parse_comma_list(slot: &mut Vec<String>, v: Option<&str>) -> bool {
607+
match v {
608+
Some(s) => {
609+
let mut v: Vec<_> = s.split(',').map(|s| s.to_string()).collect();
610+
v.sort_unstable();
611+
*slot = v;
612+
true
613+
}
614+
None => false,
615+
}
616+
}
617+
605618
pub(crate) fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) -> bool {
606619
match v {
607620
Some(s) => {
@@ -718,6 +731,7 @@ mod parse {
718731
*slot |= match s {
719732
"address" => SanitizerSet::ADDRESS,
720733
"cfi" => SanitizerSet::CFI,
734+
"dataflow" => SanitizerSet::DATAFLOW,
721735
"kcfi" => SanitizerSet::KCFI,
722736
"kernel-address" => SanitizerSet::KERNELADDRESS,
723737
"leak" => SanitizerSet::LEAK,
@@ -1846,6 +1860,8 @@ written to standard error output)"),
18461860
"enable generalizing pointer types (default: no)"),
18471861
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED],
18481862
"enable normalizing integer types (default: no)"),
1863+
sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED],
1864+
"additional ABI list files that control how shadow parameters are passed (comma separated)"),
18491865
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
18501866
"enable origins tracking in MemorySanitizer"),
18511867
sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],

compiler/rustc_target/src/spec/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,7 @@ bitflags::bitflags! {
12211221
const KCFI = 1 << 8;
12221222
const KERNELADDRESS = 1 << 9;
12231223
const SAFESTACK = 1 << 10;
1224+
const DATAFLOW = 1 << 11;
12241225
}
12251226
}
12261227
rustc_data_structures::external_bitflags_debug! { SanitizerSet }
@@ -1233,6 +1234,7 @@ impl SanitizerSet {
12331234
Some(match self {
12341235
SanitizerSet::ADDRESS => "address",
12351236
SanitizerSet::CFI => "cfi",
1237+
SanitizerSet::DATAFLOW => "dataflow",
12361238
SanitizerSet::KCFI => "kcfi",
12371239
SanitizerSet::KERNELADDRESS => "kernel-address",
12381240
SanitizerSet::LEAK => "leak",
@@ -2790,6 +2792,7 @@ impl Target {
27902792
base.$key_name |= match s.as_str() {
27912793
Some("address") => SanitizerSet::ADDRESS,
27922794
Some("cfi") => SanitizerSet::CFI,
2795+
Some("dataflow") => SanitizerSet::DATAFLOW,
27932796
Some("kcfi") => SanitizerSet::KCFI,
27942797
Some("kernel-address") => SanitizerSet::KERNELADDRESS,
27952798
Some("leak") => SanitizerSet::LEAK,

compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub fn target() -> Target {
1010
base.static_position_independent_executables = true;
1111
base.supported_sanitizers = SanitizerSet::ADDRESS
1212
| SanitizerSet::CFI
13+
| SanitizerSet::DATAFLOW
1314
| SanitizerSet::LEAK
1415
| SanitizerSet::MEMORY
1516
| SanitizerSet::SAFESTACK

src/bootstrap/configure.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def v(*args):
4848
o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
4949
o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
5050
o("vendor", "build.vendor", "enable usage of vendored Rust crates")
51-
o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)")
51+
o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)")
5252
o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
5353
o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo")
5454
o("profiler", "build.profiler", "build the profiler runtime")

src/bootstrap/src/core/build_steps/llvm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ fn supported_sanitizers(
10911091
"x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]),
10921092
"x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]),
10931093
"x86_64-unknown-linux-gnu" => {
1094-
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "safestack", "tsan"])
1094+
common_libs("linux", "x86_64", &["asan", "dfsan", "lsan", "msan", "safestack", "tsan"])
10951095
}
10961096
"x86_64-unknown-linux-musl" => {
10971097
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])

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

+30-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ This feature allows for use of one of following sanitizers:
2929
* Those that apart from testing, may be used in production:
3030
* [ControlFlowIntegrity](#controlflowintegrity) LLVM Control Flow Integrity
3131
(CFI) provides forward-edge control flow protection.
32+
* [DataFlowSanitizer](#dataflowsanitizer) a generic dynamic data flow analysis
33+
framework.
3234
* [KernelControlFlowIntegrity](#kernelcontrolflowintegrity) LLVM Kernel
3335
Control Flow Integrity (KCFI) provides forward-edge control flow protection
3436
for operating systems kernels.
@@ -39,14 +41,21 @@ This feature allows for use of one of following sanitizers:
3941
* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow
4042
protection (aarch64 only).
4143

42-
To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
43-
`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`,
44-
`-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or `-Zsanitizer=thread`.
45-
You might also need the `--target` and `build-std` flags. Example:
44+
To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=cfi`,
45+
`-Zsanitizer=dataflow`,`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`,
46+
`-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or
47+
`-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags.
48+
49+
Example:
4650
```shell
4751
$ RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std --target x86_64-unknown-linux-gnu
4852
```
4953

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

5261
AddressSanitizer is a memory error detector. It can detect the following types
@@ -639,6 +648,21 @@ LLVM KCFI is supported on the following targets:
639648
See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more
640649
details.
641650
651+
# DataFlowSanitizer
652+
653+
DataFlowSanitizer is a generalised dynamic data flow analysis.
654+
655+
Unlike other Sanitizer tools, this tool is not designed to detect a specific
656+
class of bugs on its own. Instead, it provides a generic dynamic data flow
657+
analysis framework to be used by clients to help detect application-specific
658+
issues within their own code.
659+
660+
DataFlowSanitizer is supported on the following targets:
661+
662+
* `x86_64-unknown-linux-gnu`
663+
664+
See the [Clang DataFlowSanitizer documentation][clang-dataflow] for more details.
665+
642666
# KernelAddressSanitizer
643667
644668
KernelAddressSanitizer (KASAN) is a freestanding version of AddressSanitizer
@@ -849,6 +873,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
849873
* [Sanitizers project page](https://github.com./google/sanitizers/wiki/)
850874
* [AddressSanitizer in Clang][clang-asan]
851875
* [ControlFlowIntegrity in Clang][clang-cfi]
876+
* [DataFlowSanitizer in Clang][clang-dataflow]
852877
* [HWAddressSanitizer in Clang][clang-hwasan]
853878
* [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan]
854879
* [LeakSanitizer in Clang][clang-lsan]
@@ -858,6 +883,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
858883
859884
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
860885
[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html
886+
[clang-dataflow]: https://clang.llvm.org/docs/DataFlowSanitizer.html
861887
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
862888
[clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi
863889
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html

src/tools/compiletest/src/common.rs

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ impl PanicStrategy {
156156
pub enum Sanitizer {
157157
Address,
158158
Cfi,
159+
Dataflow,
159160
Kcfi,
160161
KernelAddress,
161162
Leak,

src/tools/compiletest/src/header/needs.rs

+7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ pub(super) fn handle_needs(
2929
condition: cache.sanitizer_cfi,
3030
ignore_reason: "ignored on targets without CFI sanitizer",
3131
},
32+
Need {
33+
name: "needs-sanitizer-dataflow",
34+
condition: cache.sanitizer_dataflow,
35+
ignore_reason: "ignored on targets without dataflow sanitizer",
36+
},
3237
Need {
3338
name: "needs-sanitizer-kcfi",
3439
condition: cache.sanitizer_kcfi,
@@ -190,6 +195,7 @@ pub(super) struct CachedNeedsConditions {
190195
sanitizer_support: bool,
191196
sanitizer_address: bool,
192197
sanitizer_cfi: bool,
198+
sanitizer_dataflow: bool,
193199
sanitizer_kcfi: bool,
194200
sanitizer_kasan: bool,
195201
sanitizer_leak: bool,
@@ -229,6 +235,7 @@ impl CachedNeedsConditions {
229235
sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(),
230236
sanitizer_address: sanitizers.contains(&Sanitizer::Address),
231237
sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi),
238+
sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow),
232239
sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi),
233240
sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress),
234241
sanitizer_leak: sanitizers.contains(&Sanitizer::Leak),

src/tools/rustfmt/config_proc_macro/src/attrs.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
6868
match &attr.meta {
6969
syn::Meta::NameValue(syn::MetaNameValue {
7070
path,
71-
value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }),
71+
value:
72+
syn::Expr::Lit(syn::ExprLit {
73+
lit: syn::Lit::Str(lit_str),
74+
..
75+
}),
7276
..
7377
}) if path.is_ident(name) => Some(lit_str.value()),
7478
_ => None,

src/tools/tidy/src/ui_tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
4242
"tests/ui/macros/not-utf8.bin", // testing including data with the include macros
4343
"tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment", // more include
4444
"tests/ui/proc-macro/auxiliary/included-file.txt", // more include
45+
"tests/ui/sanitize/sanitizer-dataflow-abilist.txt", // dataflow sanitizer ABI list file
4546
"tests/ui/invalid/foo.natvis.xml", // sample debugger visualizer
4647
"tests/ui/shell-argfiles/shell-argfiles.args", // passing args via a file
4748
"tests/ui/shell-argfiles/shell-argfiles-badquotes.args", // passing args via a file
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Verifies that functions are instrumented.
2+
//
3+
// needs-sanitizer-dataflow
4+
// compile-flags: -Copt-level=0 -Zsanitizer=dataflow
5+
6+
#![crate_type="lib"]
7+
8+
pub fn foo() {
9+
}
10+
// CHECK: define{{.*}}foo{{.*}}.dfsan

tests/ui/check-cfg/well-known-values.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
100100
LL | sanitize = "_UNEXPECTED_VALUE",
101101
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
102102
|
103-
= note: expected values for `sanitize` are: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`
103+
= note: expected values for `sanitize` are: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`
104104
= note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
105105

106106
warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`

0 commit comments

Comments
 (0)