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`).
This commit is contained in:
Ramon de C Valle 2024-02-01 13:16:30 -08:00
parent eaee1e9453
commit dee4e02102
20 changed files with 678 additions and 10 deletions

View File

@ -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),

View File

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

View File

@ -1221,6 +1221,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");
}

View File

@ -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.
@ -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()

View File

@ -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));

View File

@ -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"
@ -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;
@ -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,

View File

@ -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;
@ -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`";
@ -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) => {
@ -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,
@ -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],

View File

@ -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",
@ -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,

View File

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

View File

@ -48,7 +48,7 @@ o("codegen-tests", "rust.codegen-tests", "run the tests/codegen tests")
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")

View File

@ -1,4 +1,4 @@
Change this file to make users of the `download-ci-llvm` configuration download
a new version of LLVM from CI, even if the LLVM submodule hasnt changed.
Last change is for: https://github.com/rust-lang/rust/pull/116881
Last change is for: https://github.com/rust-lang/rust/pull/120761

View File

@ -1088,7 +1088,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"])

View File

@ -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.
@ -40,13 +42,20 @@ This feature allows for use of one of following sanitizers:
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:
`-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
@ -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
@ -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]
@ -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

View File

@ -156,6 +156,7 @@ impl PanicStrategy {
pub enum Sanitizer {
Address,
Cfi,
Dataflow,
Kcfi,
KernelAddress,
Leak,

View File

@ -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),

View File

@ -43,6 +43,7 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
"tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment", // more include
"tests/ui/proc-macro/auxiliary/included-file.txt", // more include
"tests/ui/invalid/foo.natvis.xml", // sample debugger visualizer
"tests/ui/sanitizer/dataflow-abilist.txt", // dataflow sanitizer ABI list file
"tests/ui/shell-argfiles/shell-argfiles.args", // passing args via a file
"tests/ui/shell-argfiles/shell-argfiles-badquotes.args", // passing args via a file
"tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args", // passing args via a file

View File

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

View File

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

View File

@ -0,0 +1,505 @@
fun:main=uninstrumented
fun:main=discard
###############################################################################
# DFSan interface functions
###############################################################################
fun:dfsan_union=uninstrumented
fun:dfsan_union=discard
fun:dfsan_create_label=uninstrumented
fun:dfsan_create_label=discard
fun:dfsan_set_label=uninstrumented
fun:dfsan_set_label=discard
fun:dfsan_add_label=uninstrumented
fun:dfsan_add_label=discard
fun:dfsan_get_label=uninstrumented
fun:dfsan_get_label=custom
fun:dfsan_read_label=uninstrumented
fun:dfsan_read_label=discard
fun:dfsan_get_label_count=uninstrumented
fun:dfsan_get_label_count=discard
fun:dfsan_get_label_info=uninstrumented
fun:dfsan_get_label_info=discard
fun:dfsan_has_label=uninstrumented
fun:dfsan_has_label=discard
fun:dfsan_has_label_with_desc=uninstrumented
fun:dfsan_has_label_with_desc=discard
fun:dfsan_set_write_callback=uninstrumented
fun:dfsan_set_write_callback=custom
fun:dfsan_flush=uninstrumented
fun:dfsan_flush=discard
fun:dfsan_print_origin_trace=uninstrumented
fun:dfsan_print_origin_trace=discard
fun:dfsan_print_origin_id_trace=uninstrumented
fun:dfsan_print_origin_id_trace=discard
fun:dfsan_sprint_origin_trace=uninstrumented
fun:dfsan_sprint_origin_trace=discard
fun:dfsan_sprint_origin_id_trace=uninstrumented
fun:dfsan_sprint_origin_id_trace=discard
fun:dfsan_sprint_stack_trace=uninstrumented
fun:dfsan_sprint_stack_trace=discard
fun:dfsan_get_origin=uninstrumented
fun:dfsan_get_origin=custom
fun:dfsan_read_origin_of_first_taint=uninstrumented
fun:dfsan_read_origin_of_first_taint=discard
fun:dfsan_get_init_origin=uninstrumented
fun:dfsan_get_init_origin=discard
fun:dfsan_get_track_origins=uninstrumented
fun:dfsan_get_track_origins=discard
fun:dfsan_set_conditional_callback=uninstrumented
fun:dfsan_set_conditional_callback=discard
fun:dfsan_get_labels_in_signal_conditional=uninstrumented
fun:dfsan_get_labels_in_signal_conditional=discard
fun:dfsan_set_reaches_function_callback=uninstrumented
fun:dfsan_set_reaches_function_callback=discard
fun:dfsan_get_labels_in_signal_reaches_function=uninstrumented
fun:dfsan_get_labels_in_signal_reaches_function=discard
fun:dfsan_reaches_function_callback=uninstrumented
fun:dfsan_reaches_function_callback=discard
###############################################################################
# glibc
###############################################################################
# Functions of memory allocators
fun:__libc_memalign=discard
fun:aligned_alloc=discard
fun:calloc=discard
fun:cfree=discard
fun:mallinfo=discard
fun:malloc=discard
fun:free=discard
fun:malloc_stats=discard
fun:malloc_usable_size=discard
fun:mallopt=discard
fun:memalign=discard
fun:posix_memalign=discard
fun:pvalloc=discard
fun:realloc=discard
fun:reallocarray=discard
fun:valloc=discard
# Functions that return a value that depends on the input, but the output might
# not be necessarily data-dependent on the input.
fun:isalpha=functional
fun:isdigit=functional
fun:isprint=functional
fun:isxdigit=functional
fun:isalnum=functional
fun:ispunct=functional
fun:isspace=functional
fun:tolower=functional
fun:_tolower=functional
fun:toupper=functional
# Functions that return a value that is data-dependent on the input.
fun:__isinf=functional
fun:__isinff=functional
fun:__signbit=functional
fun:__signbitf=functional
fun:__signbitl=functional
fun:btowc=functional
fun:exp=functional
fun:exp2=functional
fun:expf=functional
fun:expl=functional
fun:fabs=functional
fun:finite=functional
fun:finitef=functional
fun:finitel=functional
fun:floor=functional
fun:fmod=functional
fun:isinf=functional
fun:isinff=functional
fun:isinfl=functional
fun:isnan=functional
fun:isnanf=functional
fun:isnanl=functional
fun:log=functional
fun:log1p=functional
fun:log1pf=functional
fun:log1pl=functional
fun:log2=functional
fun:log2f=functional
fun:log2l=functional
fun:modf=functional
fun:nextafter=functional
fun:nextafterf=functional
fun:nextafterl=functional
fun:nexttoward=functional
fun:nexttowardf=functional
fun:nexttowardl=functional
fun:pow=functional
fun:powf=functional
fun:powl=functional
fun:round=functional
fun:sqrt=functional
fun:sqrtf=functional
fun:sqrtl=functional
fun:wctob=functional
# Functions that produce an output that does not depend on the input (shadow is
# zeroed automatically).
fun:__assert_fail=discard
fun:__cmsg_nxthdr=discard
fun:__ctype_b_loc=discard
fun:__cxa_atexit=discard
fun:__errno_location=discard
fun:__newlocale=discard
fun:__sbrk=discard
fun:__sigsetjmp=discard
fun:__uselocale=discard
fun:__wctype_l=discard
fun:access=discard
fun:alarm=discard
fun:atexit=discard
fun:bind=discard
fun:chdir=discard
fun:close=discard
fun:closedir=discard
fun:connect=discard
fun:creat=discard
fun:dladdr=discard
fun:dlclose=discard
fun:epoll_create=discard
fun:epoll_create1=discard
fun:epoll_ctl=discard
fun:fclose=discard
fun:feof=discard
fun:ferror=discard
fun:fflush=discard
fun:fileno=discard
fun:fopen=discard
fun:fprintf=discard
fun:fputc=discard
fun:fputc=discard
fun:fputs=discard
fun:fputs=discard
fun:fseek=discard
fun:ftell=discard
fun:fwrite=discard
fun:getenv=discard
fun:getuid=discard
fun:geteuid=discard
fun:getpagesize=discard
fun:getpid=discard
fun:kill=discard
fun:listen=discard
fun:lseek=discard
fun:mkdir=discard
fun:mmap=discard
fun:munmap=discard
fun:open=discard
fun:openat=discard
fun:pipe=discard
fun:posix_fadvise=discard
fun:prctl=discard
fun:printf=discard
fun:pthread_sigmask=discard
fun:putc=discard
fun:putchar=discard
fun:puts=discard
fun:rand=discard
fun:random=discard
fun:remove=discard
fun:sched_getcpu=discard
fun:sched_get_priority_max=discard
fun:sched_setaffinity=discard
fun:sched_yield=discard
fun:sem_destroy=discard
fun:sem_init=discard
fun:sem_post=discard
fun:sem_wait=discard
fun:send=discard
fun:sendmsg=discard
fun:sendto=discard
fun:setsockopt=discard
fun:shutdown=discard
fun:sleep=discard
fun:socket=discard
fun:strerror=discard
fun:strspn=discard
fun:strcspn=discard
fun:symlink=discard
fun:syscall=discard
fun:unlink=discard
fun:uselocale=discard
fun:vfprintf=discard
# Functions that produce output does not depend on the input (need to zero the
# shadow manually).
fun:_dl_get_tls_static_info=custom
fun:clock_gettime=custom
fun:dlopen=custom
fun:epoll_wait=custom
fun:fgets=custom
fun:fstat=custom
fun:getcwd=custom
fun:get_current_dir_name=custom
fun:getentropy=custom
fun:gethostname=custom
fun:getpeername=custom
fun:getrlimit=custom
fun:getrusage=custom
fun:getsockname=custom
fun:getsockopt=custom
fun:nanosleep=custom
fun:pread=custom
fun:read=custom
fun:recvmmsg=custom
fun:recvmsg=custom
fun:sigaltstack=custom
fun:socketpair=custom
fun:stat=custom
fun:time=custom
# Functions that produce an output that depend on the input (propagate the
# shadow manually).
fun:ctime_r=custom
fun:inet_pton=custom
fun:localtime_r=custom
fun:memcpy=custom
fun:memmove=custom
fun:memset=custom
fun:strcpy=custom
fun:strdup=custom
fun:strncpy=custom
fun:strtod=custom
fun:strtol=custom
fun:strtoll=custom
fun:strtoul=custom
fun:strtoull=custom
fun:strcat=custom
fun:strncat=custom
fun:__isoc23_strtod=custom
fun:__isoc23_strtol=custom
fun:__isoc23_strtoll=custom
fun:__isoc23_strtoul=custom
fun:__isoc23_strtoull=custom
# Functions that produce an output that is computed from the input, but is not
# necessarily data dependent.
fun:bcmp=custom
fun:memchr=custom
fun:memcmp=custom
fun:strcasecmp=custom
fun:strchr=custom
fun:strcmp=custom
fun:strlen=custom
fun:strnlen=custom
fun:strncasecmp=custom
fun:strncmp=custom
fun:strpbrk=custom
fun:strrchr=custom
fun:strstr=custom
fun:strsep=custom
# Functions which take action based on global state, such as running a callback
# set by a separate function.
fun:write=custom
# Functions that take a callback (wrap the callback manually).
fun:dl_iterate_phdr=custom
fun:getpwuid_r=custom
fun:poll=custom
fun:sched_getaffinity=custom
fun:select=custom
fun:sigemptyset=custom
fun:sigaction=custom
fun:signal=custom
fun:gettimeofday=custom
# sprintf-like
fun:sprintf=custom
fun:snprintf=custom
# scanf-like
fun:sscanf=custom
fun:__isoc99_sscanf=custom
fun:__isoc23_sscanf=custom
# TODO: custom
fun:asprintf=discard
fun:qsort=discard
# fork
fun:fork=custom
###############################################################################
# pthread
###############################################################################
fun:__pthread_register_cancel=discard
fun:__pthread_unregister_cancel=discard
fun:pthread_attr_destroy=discard
fun:pthread_attr_getaffinity_np=discard
fun:pthread_attr_getdetachstate=discard
fun:pthread_attr_getguardsize=discard
fun:pthread_attr_getinheritsched=discard
fun:pthread_attr_getschedparam=discard
fun:pthread_attr_getschedpolicy=discard
fun:pthread_attr_getscope=discard
fun:pthread_attr_getstack=discard
fun:pthread_attr_getstackaddr=disacrd
fun:pthread_attr_getstacksize=discard
fun:pthread_attr_init=discard
fun:pthread_attr_setaffinity_np=discard
fun:pthread_attr_setdetachstate=discard
fun:pthread_attr_setguardsize=discard
fun:pthread_attr_setinheritsched=discard
fun:pthread_attr_setschedparam=discard
fun:pthread_attr_setschedpolicy=discard
fun:pthread_attr_setscope=discard
fun:pthread_attr_setstack=discard
fun:pthread_attr_setstackaddr=discard
fun:pthread_attr_setstacksize=discard
fun:pthread_equal=discard
fun:pthread_getschedparam=discard
fun:pthread_getspecific=discard
fun:pthread_key_create=discard
fun:pthread_key_delete=discard
fun:pthread_mutex_destroy=discard
fun:pthread_mutex_init=discard
fun:pthread_mutex_lock=discard
fun:pthread_mutex_trylock=discard
fun:pthread_mutex_unlock=discard
fun:pthread_mutexattr_destroy=discard
fun:pthread_mutexattr_init=discard
fun:pthread_mutexattr_settype=discard
fun:pthread_rwlock_destroy=discard
fun:pthread_rwlock_init=discard
fun:pthread_rwlock_rdlock=discard
fun:pthread_rwlock_timedrdlock=discard
fun:pthread_rwlock_timedwrlock=discard
fun:pthread_rwlock_tryrdlock=discard
fun:pthread_rwlock_trywrlock=discard
fun:pthread_rwlock_wrlock=discard
fun:pthread_rwlock_unlock=discard
fun:pthread_setschedparam=discard
fun:pthread_setname_np=discard
fun:pthread_once=discard
fun:pthread_self=discard
fun:pthread_setspecific=discard
# Functions that take a callback (wrap the callback manually).
fun:pthread_create=custom
# Functions that produce output does not depend on the input (need to zero the
# shadow manually).
fun:pthread_join=custom
###############################################################################
# libffi/libgo
###############################################################################
# Functions that are written in asm or are called from asm.
fun:ffi_call_unix64=uninstrumented
fun:ffi_call_unix64=discard
fun:ffi_closure_unix64_inner=uninstrumented
fun:ffi_closure_unix64_inner=discard
fun:ffi_closure_unix64=uninstrumented
fun:ffi_closure_unix64=discard
fun:__go_get_closure=uninstrumented
fun:__go_get_closure=discard
fun:__go_makefunc_can_recover=uninstrumented
fun:__go_makefunc_can_recover=discard
fun:__go_makefunc_returning=uninstrumented
fun:__go_makefunc_returning=discard
fun:reflect.MakeFuncStubGo=uninstrumented
fun:reflect.MakeFuncStubGo=discard
fun:reflect.makeFuncStub=uninstrumented
fun:reflect.makeFuncStub=discard
###############################################################################
# lib/Fuzzer
###############################################################################
# Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp
fun:__sanitizer_cov_trace_cmp1=custom
fun:__sanitizer_cov_trace_cmp1=uninstrumented
fun:__sanitizer_cov_trace_cmp2=custom
fun:__sanitizer_cov_trace_cmp2=uninstrumented
fun:__sanitizer_cov_trace_cmp4=custom
fun:__sanitizer_cov_trace_cmp4=uninstrumented
fun:__sanitizer_cov_trace_cmp8=custom
fun:__sanitizer_cov_trace_cmp8=uninstrumented
fun:__sanitizer_cov_trace_const_cmp1=custom
fun:__sanitizer_cov_trace_const_cmp1=uninstrumented
fun:__sanitizer_cov_trace_const_cmp2=custom
fun:__sanitizer_cov_trace_const_cmp2=uninstrumented
fun:__sanitizer_cov_trace_const_cmp4=custom
fun:__sanitizer_cov_trace_const_cmp4=uninstrumented
fun:__sanitizer_cov_trace_const_cmp8=custom
fun:__sanitizer_cov_trace_const_cmp8=uninstrumented
# Similar for __sanitizer_cov_trace_switch
fun:__sanitizer_cov_trace_switch=custom
fun:__sanitizer_cov_trace_switch=uninstrumented
# Ignores all other __sanitizer callbacks.
fun:__sanitizer_cov=uninstrumented
fun:__sanitizer_cov=discard
fun:__sanitizer_cov_module_init=uninstrumented
fun:__sanitizer_cov_module_init=discard
fun:__sanitizer_cov_with_check=uninstrumented
fun:__sanitizer_cov_with_check=discard
fun:__sanitizer_set_death_callback=uninstrumented
fun:__sanitizer_set_death_callback=discard
fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented
fun:__sanitizer_update_counter_bitset_and_clear_counters=discard
fun:__sanitizer_cov_trace_pc*=uninstrumented
fun:__sanitizer_cov_trace_pc*=discard
fun:__sanitizer_cov_pcs_init=uninstrumented
fun:__sanitizer_cov_pcs_init=discard
fun:__sanitizer_get_current_allocated_bytes=uninstrumented
fun:__sanitizer_get_current_allocated_bytes=discard
fun:__sanitizer_get_heap_size=uninstrumented
fun:__sanitizer_get_heap_size=discard
fun:__sanitizer_get_free_bytes=uninstrumented
fun:__sanitizer_get_free_bytes=discard
fun:__sanitizer_get_unmapped_bytes=uninstrumented
fun:__sanitizer_get_unmapped_bytes=discard
fun:__sanitizer_get_estimated_allocated_size=uninstrumented
fun:__sanitizer_get_estimated_allocated_size=discard
fun:__sanitizer_get_ownership=uninstrumented
fun:__sanitizer_get_ownership=discard
fun:__sanitizer_get_allocated_begin=uninstrumented
fun:__sanitizer_get_allocated_begin=discard
fun:__sanitizer_get_allocated_size=uninstrumented
fun:__sanitizer_get_allocated_size=discard
fun:__sanitizer_get_allocated_size_fast=uninstrumented
fun:__sanitizer_get_allocated_size_fast=discard
fun:__sanitizer_print_stack_trace=uninstrumented
fun:__sanitizer_print_stack_trace=discard
fun:TcmallocSlab_Internal_PushBatch_FixedShift=uninstrumented
fun:TcmallocSlab_Internal_PushBatch_FixedShift=discard
fun:TcmallocSlab_Internal_PushBatch_FixedShift_VCPU=uninstrumented
fun:TcmallocSlab_Internal_PushBatch_FixedShift_VCPU=discard
fun:TcmallocSlab_Internal_PerCpuCmpxchg64=uninstrumented
fun:TcmallocSlab_Internal_PerCpuCmpxchg64=discard
fun:TcmallocSlab_Internal_PerCpuCmpxchg64_VCPU=uninstrumented
fun:TcmallocSlab_Internal_PerCpuCmpxchg64_VCPU=discard
fun:TcmallocSlab_Internal_PopBatch_FixedShift=uninstrumented
fun:TcmallocSlab_Internal_PopBatch_FixedShift=discard
fun:TcmallocSlab_Internal_PopBatch_FixedShift_VCPU=uninstrumented
fun:TcmallocSlab_Internal_PopBatch_FixedShift_VCPU=discard
# Ignores the dfsan wrappers.
fun:__dfsw_*=uninstrumented
fun:__dfsw_*=discard
fun:__dfso_*=uninstrumented
fun:__dfso_*=discard
# Rust functions.
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

View File

@ -0,0 +1,60 @@
#![allow(non_camel_case_types)]
// Verifies that labels are propagated through loads and stores.
//
//@ needs-sanitizer-support
//@ needs-sanitizer-dataflow
//@ run-pass
//@ compile-flags: -Zsanitizer=dataflow -Zsanitizer-dataflow-abilist={{src-base}}/sanitizer/dataflow-abilist.txt
use std::mem::size_of;
use std::os::raw::{c_int, c_long, c_void};
type dfsan_label = u8;
extern "C" {
fn dfsan_add_label(label: dfsan_label, addr: *mut c_void, size: usize);
fn dfsan_get_label(data: c_long) -> dfsan_label;
fn dfsan_has_label(label: dfsan_label, elem: dfsan_label) -> c_int;
fn dfsan_read_label(addr: *const c_void, size: usize) -> dfsan_label;
fn dfsan_set_label(label: dfsan_label, addr: *mut c_void, size: usize);
}
fn propagate2(i: &i64) -> i64 {
i.clone()
}
fn propagate(i: i64) -> i64 {
let v = vec!(i, 1, 2, 3);
let j = v.iter().sum();
propagate2(&j)
}
pub fn main() {
let mut i = 1i64;
let i_ptr = &mut i as *mut i64;
let i_label: dfsan_label = 1;
unsafe {
dfsan_set_label(i_label, i_ptr as *mut c_void, size_of::<i64>());
}
let new_label = unsafe { dfsan_get_label(i) };
assert_eq!(i_label, new_label);
let read_label = unsafe { dfsan_read_label(i_ptr as *const c_void, size_of::<i64>()) };
assert_eq!(i_label, read_label);
let j_label: dfsan_label = 2;
unsafe {
dfsan_add_label(j_label, i_ptr as *mut c_void, size_of::<i64>());
}
let read_label = unsafe { dfsan_read_label(i_ptr as *const c_void, size_of::<i64>()) };
assert_eq!(unsafe { dfsan_has_label(read_label, i_label) }, 1);
assert_eq!(unsafe { dfsan_has_label(read_label, j_label) }, 1);
let mut new_i = propagate(i);
let new_i_ptr = &mut new_i as *mut i64;
let read_label = unsafe { dfsan_read_label(new_i_ptr as *const c_void, size_of::<i64>()) };
assert_eq!(unsafe { dfsan_has_label(read_label, i_label) }, 1);
assert_eq!(unsafe { dfsan_has_label(read_label, j_label) }, 1);
}