diff --git a/driver/cl_options_sanitizers.cpp b/driver/cl_options_sanitizers.cpp index 2299cd48ca..92ffea5738 100644 --- a/driver/cl_options_sanitizers.cpp +++ b/driver/cl_options_sanitizers.cpp @@ -15,8 +15,10 @@ #include "driver/cl_options_sanitizers.h" #include "ddmd/errors.h" +#include "ddmd/dsymbol.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SpecialCaseList.h" namespace { @@ -28,6 +30,13 @@ cl::list fSanitize( "suspicious behavior."), cl::value_desc("checks")); +cl::list fSanitizeBlacklist( + "fsanitize-blacklist", cl::CommaSeparated, + cl::desc("Add to the blacklist files for the sanitizers."), + cl::value_desc("file")); + +std::unique_ptr sanitizerBlacklist; + #ifdef ENABLE_COVERAGE_SANITIZER cl::list fSanitizeCoverage( "fsanitize-coverage", cl::CommaSeparated, @@ -149,6 +158,14 @@ void initializeSanitizerOptionsFromCmdline() sancovOpts.CoverageType = llvm::SanitizerCoverageOptions::SCK_Edge; } #endif + + if (isAnySanitizerEnabled() && !fSanitizeBlacklist.empty()) { + std::string loadError; + sanitizerBlacklist = + llvm::SpecialCaseList::create(fSanitizeBlacklist, loadError); + if (!sanitizerBlacklist) + error(Loc(), "-fsanitize-blacklist error: %s", loadError.c_str()); + } } #ifdef ENABLE_COVERAGE_SANITIZER @@ -180,4 +197,9 @@ void outputSanitizerSettings(llvm::raw_ostream &hash_os) { #endif } +bool functionIsInSanitizerBlacklist(FuncDeclaration *funcDecl) { + return sanitizerBlacklist && + sanitizerBlacklist->inSection("fun", mangleExact(funcDecl)); +} + } // namespace opts diff --git a/driver/cl_options_sanitizers.h b/driver/cl_options_sanitizers.h index d99b8aac08..56d043c914 100644 --- a/driver/cl_options_sanitizers.h +++ b/driver/cl_options_sanitizers.h @@ -23,6 +23,7 @@ #define ENABLE_COVERAGE_SANITIZER #endif +class FuncDeclaration; namespace llvm { class raw_ostream; } @@ -54,6 +55,8 @@ llvm::SanitizerCoverageOptions getSanitizerCoverageOptions(); void outputSanitizerSettings(llvm::raw_ostream &hash_os); +bool functionIsInSanitizerBlacklist(FuncDeclaration *funcDecl); + } // namespace opts #endif // LDC_DRIVER_CL_OPTIONS_SANITIZERS_H diff --git a/gen/functions.cpp b/gen/functions.cpp index 9bc5e53623..be7abf2cca 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -954,7 +954,8 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { if (gABI->needsUnwindTables()) { func->addFnAttr(LLAttribute::UWTable); } - if (opts::isAnySanitizerEnabled()) { + if (opts::isAnySanitizerEnabled() && + !opts::functionIsInSanitizerBlacklist(fd)) { // Set the required sanitizer attribute. if (opts::isSanitizerEnabled(opts::AddressSanitizer)) { func->addFnAttr(LLAttribute::SanitizeAddress); diff --git a/tests/sanitizers/fsanitize_blacklist.d b/tests/sanitizers/fsanitize_blacklist.d new file mode 100644 index 0000000000..816f2bf8e5 --- /dev/null +++ b/tests/sanitizers/fsanitize_blacklist.d @@ -0,0 +1,63 @@ +// Test sanitizer blacklist functionality + +// RUN: %ldc -c -output-ll -fsanitize=address \ +// RUN: -fsanitize-blacklist=%S/inputs/fsanitize_blacklist.txt \ +// RUN: -fsanitize-blacklist=%S/inputs/fsanitize_blacklist2.txt \ +// RUN: -of=%t.ll %s && FileCheck %s < %t.ll + +// Don't attempt to load the blacklist when no sanitizer is active +// RUN: %ldc -o- -fsanitize-blacklist=%S/thisfilecertainlydoesnotexist %s + +// CHECK-LABEL: define {{.*}}9foofoofoo +// CHECK-SAME: #[[ATTR_WITHASAN:[0-9]+]] +void foofoofoo(int* i) +{ + // CHECK: call {{.*}}_asan + *i = 1; +} + +// CHECK-LABEL: define {{.*}}blacklisted +// CHECK-SAME: #[[ATTR_NOASAN:[0-9]+]] +extern (C) void blacklisted(int* i) +{ + // CHECK-NOT: call {{.*}}_asan + *i = 1; +} + +// Test blacklisted wildcard +// CHECK-LABEL: define {{.*}}10black_set1 +// CHECK-SAME: #[[ATTR_NOASAN:[0-9]+]] +void black_set1(int* i) +{ + // CHECK-NOT: call {{.*}}_asan + *i = 1; +} +// CHECK-LABEL: define {{.*}}10black_set2 +// CHECK-SAME: #[[ATTR_NOASAN:[0-9]+]] +void black_set2(int* i) +{ + // CHECK-NOT: call {{.*}}_asan + *i = 1; +} + +// Test blacklisting of template class methods +class ABCDEF(T) +{ + void method(int* i) + { + *i = 1; + } +} + +// CHECK-LABEL: define {{.*}}TiZ6ABCDEF6method +// CHECK-SAME: #[[ATTR_NOASAN:[0-9]+]] +ABCDEF!int ofBlacklistedType; + +// CHECK-LABEL: define {{.*}}TAyaZ6ABCDEF6method +// CHECK-SAME: #[[ATTR_WITHASAN:[0-9]+]] +ABCDEF!string ofInstrumentedType; + +//CHECK: attributes #[[ATTR_WITHASAN]] ={{.*}}sanitize_address +//CHECK: attributes #[[ATTR_NOASAN]] +//CHECK-NOT: sanitize_address +//CHECK-SAME: } diff --git a/tests/sanitizers/inputs/fsanitize_blacklist.txt b/tests/sanitizers/inputs/fsanitize_blacklist.txt new file mode 100644 index 0000000000..e4af0719bf --- /dev/null +++ b/tests/sanitizers/inputs/fsanitize_blacklist.txt @@ -0,0 +1,13 @@ +# Blacklist for the sanitizers. Turns off instrumentation of particular +# functions or sources. Use with care. You may set location of blacklist +# at compile-time using -fsanitize-blacklist= flag. + +# Example usage: +# fun:*bad_function_name* +# src:file_with_tricky_code.cc +# global:*global_with_bad_access_or_initialization* +# global:*global_with_initialization_issues*=init +# type:*Namespace::ClassName*=init + +fun:blacklisted +fun:*black_set* diff --git a/tests/sanitizers/inputs/fsanitize_blacklist2.txt b/tests/sanitizers/inputs/fsanitize_blacklist2.txt new file mode 100644 index 0000000000..3a0b098694 --- /dev/null +++ b/tests/sanitizers/inputs/fsanitize_blacklist2.txt @@ -0,0 +1 @@ +fun:_D*TiZ6ABCDEF6method*