vsk created this revision. Sanitizer blacklists currently apply to all enabled sanitizers. E.g if multiple sanitizers are enabled, it isn't possible to specify a blacklist for one sanitizer and not another.
This makes it impossible to load default blacklists for more than one sanitizer, because doing so would result in false negatives (i.e, some code may not be instrumented by the right sanitizer, because the final sanitizer blacklist is the union of all available blacklists). This patch fixes the situation by changing the internal representation of blacklists. It becomes mandatory to specify which sanitizers are covered by each blacklist, and to specify the sanitization kind up-front when querying the blacklist. This resolves the "multiple default sanitizer blacklists" situation. Instead of arbitrarily picking a default blacklist and ignoring the rest, we would correctly load all of them s.t entries in one blacklist which apply to one sanitizer could not mistakenly be applied to another sanitizer. For now, the user-facing -fsanitize-blacklist driver option is left unchanged. Specifying a blacklist in this way creates a blacklist which covers all enabled sanitizers. The internal -fsanitize-blacklist frontend option is modified so that the names of the covered sanitizers are passed along with the path to the blacklist file. E.g this: clang -cc1 -fsanitize=address -fsanitize-blacklist=BL.txt Becomes this: clang -cc1 -fsanitize=address -fsanitize-blacklist=address:BL.txt This patch obsoletes https://reviews.llvm.org/D32047. https://reviews.llvm.org/D32842 Files: include/clang/AST/ASTContext.h include/clang/Basic/LangOptions.h include/clang/Basic/SanitizerBlacklist.h include/clang/Driver/SanitizerArgs.h lib/AST/ASTContext.cpp lib/AST/Decl.cpp lib/Basic/LangOptions.cpp lib/Basic/SanitizerBlacklist.cpp lib/CodeGen/CGClass.cpp lib/CodeGen/CGDeclCXX.cpp lib/CodeGen/CGExpr.cpp lib/CodeGen/CodeGenFunction.cpp lib/CodeGen/CodeGenModule.cpp lib/CodeGen/CodeGenModule.h lib/Driver/SanitizerArgs.cpp lib/Frontend/CompilerInvocation.cpp test/CodeGen/address-safety-attr.cpp test/CodeGen/asan-globals.cpp test/CodeGen/sanitize-address-field-padding.cpp test/CodeGen/sanitize-init-order.cpp test/CodeGen/sanitize-thread-attr.cpp test/CodeGen/ubsan-blacklist.c test/CodeGen/ubsan-type-blacklist.cpp test/CodeGenCXX/cfi-blacklist.cpp test/Driver/Inputs/resource_dir/cfi_blacklist.txt test/Driver/fsanitize-blacklist.c test/Frontend/sanitizer-blacklists.c
Index: test/Frontend/sanitizer-blacklists.c =================================================================== --- /dev/null +++ test/Frontend/sanitizer-blacklists.c @@ -0,0 +1,32 @@ +// Blacklisting a function for ASan shouldn't affect TSan instrumentation. +// +// RUN: echo "fun:foo" > %t.blacklist +// +// RUN: %clang_cc1 -fsanitize=address,thread -triple x86_64-apple-darwin10 \ +// RUN: -fsanitize-blacklist=address:%t.blacklist \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefixes=COMMON,CHECK1 + +// Check that we can blacklist a function for multiple sanitizers using +// separate blacklists. +// +// RUN: %clang_cc1 -fsanitize=address,thread -triple x86_64-apple-darwin10 \ +// RUN: -fsanitize-blacklist=address:%t.blacklist \ +// RUN: -fsanitize-blacklist=thread:%t.tsan_blacklist \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefixes=COMMON,CHECK2 + +// Check that we can blacklist a function for multiple sanitizers using the +// same blacklist. +// +// RUN: %clang_cc1 -fsanitize=address,thread -triple x86_64-apple-darwin10 \ +// RUN: -fsanitize-blacklist=address,thread:%t.blacklist \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefixes=COMMON,CHECK2 + +// COMMON: define void @foo() [[FOO_ATTR:#[0-9]+]] +void foo() {} + +// CHECK1: attributes [[FOO_ATTR]] = { {{[^}]*}} sanitize_thread +// CHECK1-NOT: sanitize_address + +// CHECK2: attributes [[FOO_ATTR]] +// CHECK2-NOT: sanitize_address +// CHECK2-NOT: sanitize_thread Index: test/Driver/fsanitize-blacklist.c =================================================================== --- test/Driver/fsanitize-blacklist.c +++ test/Driver/fsanitize-blacklist.c @@ -20,9 +20,20 @@ // CHECK-BLACKLIST2: -fdepfile-entry={{.*}}.good" "-fdepfile-entry={{.*}}.second // Check that the default blacklist is not added as an extra dependency. -// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-BLACKLIST --implicit-check-not=fdepfile-entry +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -resource-dir=%S/Inputs/resource_dir %s -### &> %t.cc1_asan +// RUN: FileCheck %s --check-prefix=CHECK-DEFAULT-BLACKLIST --implicit-check-not=fdepfile-entry -input-file %t.cc1_asan // CHECK-DEFAULT-BLACKLIST: -fsanitize-blacklist={{.*}}asan_blacklist.txt +// Check that default blacklists are not added unless the matching sanitizer is +// enabled, even if the blacklist exists. +// RUN: FileCheck %s --implicit-check-not=cfi_blacklist.txt -input-file %t.cc1_asan + +// Check that we can add multiple default blacklists if the matching sanitizers +// are enabled. +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address,cfi -flto -fvisibility=hidden -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=MULTIPLE-DEFAULT-BLACKLISTS +// MULTIPLE-DEFAULT-BLACKLISTS-DAG: -fsanitize-blacklist={{.*}}asan_blacklist.txt +// MULTIPLE-DEFAULT-BLACKLISTS-DAG: -fsanitize-blacklist={{.*}}cfi_blacklist.txt + // Ignore -fsanitize-blacklist flag if there is no -fsanitize flag. // RUN: %clang -target x86_64-linux-gnu -fsanitize-blacklist=%t.good %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SANITIZE --check-prefix=DELIMITERS // CHECK-NO-SANITIZE-NOT: -fsanitize-blacklist Index: test/CodeGenCXX/cfi-blacklist.cpp =================================================================== --- test/CodeGenCXX/cfi-blacklist.cpp +++ test/CodeGenCXX/cfi-blacklist.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOBL %s // RUN: echo "type:std::*" > %t.txt -// RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=cfi-vcall:%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s struct S1 { virtual void f(); Index: test/CodeGen/ubsan-type-blacklist.cpp =================================================================== --- test/CodeGen/ubsan-type-blacklist.cpp +++ test/CodeGen/ubsan-type-blacklist.cpp @@ -1,7 +1,7 @@ // Verify ubsan vptr does not check down-casts on blacklisted types. // RUN: echo "type:_ZTI3Foo" > %t-type.blacklist // RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -emit-llvm %s -o - | FileCheck %s --check-prefix=DEFAULT -// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -fsanitize-blacklist=%t-type.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=TYPE +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-recover=vptr -fsanitize-blacklist=vptr:%t-type.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=TYPE class Bar { public: Index: test/CodeGen/ubsan-blacklist.c =================================================================== --- test/CodeGen/ubsan-blacklist.c +++ test/CodeGen/ubsan-blacklist.c @@ -2,8 +2,8 @@ // RUN: echo "fun:hash" > %t-func.blacklist // RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t-file.blacklist // RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -emit-llvm %s -o - | FileCheck %s --check-prefix=DEFAULT -// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -fsanitize-blacklist=%t-func.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=FUNC -// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -fsanitize-blacklist=%t-file.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=FILE +// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -fsanitize-blacklist=unsigned-integer-overflow:%t-func.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=FUNC +// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -fsanitize-blacklist=unsigned-integer-overflow:%t-file.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=FILE unsigned i; Index: test/CodeGen/sanitize-thread-attr.cpp =================================================================== --- test/CodeGen/sanitize-thread-attr.cpp +++ test/CodeGen/sanitize-thread-attr.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck -check-prefix=WITHOUT %s // RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -fsanitize=thread | FileCheck -check-prefix=TSAN %s // RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t -// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -fsanitize=thread -fsanitize-blacklist=%t | FileCheck -check-prefix=BL %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -fsanitize=thread -fsanitize-blacklist=thread:%t | FileCheck -check-prefix=BL %s // The sanitize_thread attribute should be attached to functions // when ThreadSanitizer is enabled, unless no_sanitize_thread attribute Index: test/CodeGen/sanitize-init-order.cpp =================================================================== --- test/CodeGen/sanitize-init-order.cpp +++ test/CodeGen/sanitize-init-order.cpp @@ -4,8 +4,8 @@ // RUN: echo "src:%s=init" | sed -e 's/\\/\\\\/g' > %t-file.blacklist // RUN: echo "type:PODWithCtorAndDtor=init" > %t-type.blacklist // RUN: echo "type:NS::PODWithCtor=init" >> %t-type.blacklist -// RUN: %clang_cc1 -fsanitize=address -fsanitize-blacklist=%t-file.blacklist -emit-llvm -o - %s | FileCheck %s --check-prefix=BLACKLIST -// RUN: %clang_cc1 -fsanitize=address -fsanitize-blacklist=%t-type.blacklist -emit-llvm -o - %s | FileCheck %s --check-prefix=BLACKLIST +// RUN: %clang_cc1 -fsanitize=address -fsanitize-blacklist=address:%t-file.blacklist -emit-llvm -o - %s | FileCheck %s --check-prefix=BLACKLIST +// RUN: %clang_cc1 -fsanitize=address -fsanitize-blacklist=address:%t-type.blacklist -emit-llvm -o - %s | FileCheck %s --check-prefix=BLACKLIST struct PODStruct { int x; Index: test/CodeGen/sanitize-address-field-padding.cpp =================================================================== --- test/CodeGen/sanitize-address-field-padding.cpp +++ test/CodeGen/sanitize-address-field-padding.cpp @@ -1,9 +1,9 @@ // Test -fsanitize-address-field-padding // RUN: echo 'type:SomeNamespace::BlacklistedByName=field-padding' > %t.type.blacklist // RUN: echo 'src:*sanitize-address-field-padding.cpp=field-padding' > %t.file.blacklist -// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsanitize=address -fsanitize-address-field-padding=1 -fsanitize-blacklist=%t.type.blacklist -Rsanitize-address -emit-llvm -o - %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsanitize=address -fsanitize-address-field-padding=1 -fsanitize-blacklist=%t.type.blacklist -Rsanitize-address -emit-llvm -o - %s -O1 -mconstructor-aliases 2>&1 | FileCheck %s --check-prefix=WITH_CTOR_ALIASES -// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsanitize=address -fsanitize-address-field-padding=1 -fsanitize-blacklist=%t.file.blacklist -Rsanitize-address -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefix=FILE_BLACKLIST +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsanitize=address -fsanitize-address-field-padding=1 -fsanitize-blacklist=address:%t.type.blacklist -Rsanitize-address -emit-llvm -o - %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsanitize=address -fsanitize-address-field-padding=1 -fsanitize-blacklist=address:%t.type.blacklist -Rsanitize-address -emit-llvm -o - %s -O1 -mconstructor-aliases 2>&1 | FileCheck %s --check-prefix=WITH_CTOR_ALIASES +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsanitize=address -fsanitize-address-field-padding=1 -fsanitize-blacklist=address:%t.file.blacklist -Rsanitize-address -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefix=FILE_BLACKLIST // RUN: %clang_cc1 -fsanitize=address -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefix=NO_PADDING // Try to emulate -save-temps option and make sure -disable-llvm-passes will not run sanitize instrumentation. // RUN: %clang_cc1 -fsanitize=address -emit-llvm -disable-llvm-passes -o - %s | %clang_cc1 -fsanitize=address -emit-llvm -o - -x ir | FileCheck %s --check-prefix=NO_PADDING Index: test/CodeGen/asan-globals.cpp =================================================================== --- test/CodeGen/asan-globals.cpp +++ test/CodeGen/asan-globals.cpp @@ -1,9 +1,9 @@ // RUN: echo "int extra_global;" > %t.extra-source.cpp // RUN: echo "global:*blacklisted_global*" > %t.blacklist -// RUN: %clang_cc1 -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.blacklist -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=address:%t.blacklist -emit-llvm -o - %s | FileCheck %s // The blacklist file uses regexps, so Windows path backslashes. // RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t.blacklist-src -// RUN: %clang_cc1 -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.blacklist-src -emit-llvm -o - %s | FileCheck %s --check-prefix=BLACKLIST-SRC +// RUN: %clang_cc1 -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=address:%t.blacklist-src -emit-llvm -o - %s | FileCheck %s --check-prefix=BLACKLIST-SRC int global; int dyn_init_global = global; Index: test/CodeGen/address-safety-attr.cpp =================================================================== --- test/CodeGen/address-safety-attr.cpp +++ test/CodeGen/address-safety-attr.cpp @@ -7,12 +7,12 @@ // RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address | FileCheck -check-prefix=ASAN %s // RUN: echo "fun:*BlacklistedFunction*" > %t.func.blacklist -// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.func.blacklist | FileCheck -check-prefix=BLFUNC %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=address:%t.func.blacklist | FileCheck -check-prefix=BLFUNC %s // The blacklist file uses regexps, so escape backslashes, which are common in // Windows paths. // RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t.file.blacklist -// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.file.blacklist | FileCheck -check-prefix=BLFILE %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=address:%t.file.blacklist | FileCheck -check-prefix=BLFILE %s // The sanitize_address attribute should be attached to functions // when AddressSanitizer is enabled, unless no_sanitize_address attribute Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -15,6 +15,7 @@ #include "clang/Config/config.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" +#include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/Util.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/LangStandard.h" @@ -392,6 +393,23 @@ } } +static void +parseSanitizerBlacklists(StringRef FlagName, LangOptions &Opts, + const std::vector<std::string> &BlacklistArgs, + DiagnosticsEngine &Diags) { + for (const std::string &BA : BlacklistArgs) { + SanitizerMask Kind; + std::string Path; + if (!SanitizerArgs::decodeBlacklistArg(BA, Kind, Path)) { + Diags.Report(diag::err_drv_invalid_value) << FlagName << BA; + continue; + } + Opts.SanitizerBlacklistFiles.emplace_back(std::move(Path)); + Opts.SanitizerBlacklists.emplace_back(Kind, + Opts.SanitizerBlacklistFiles.back()); + } +} + // Set the profile kind for fprofile-instrument. static void setPGOInstrumentor(CodeGenOptions &Opts, ArgList &Args, DiagnosticsEngine &Diags) { @@ -2346,7 +2364,9 @@ // -fsanitize-address-field-padding=N has to be a LangOpt, parse it here. Opts.SanitizeAddressFieldPadding = getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags); - Opts.SanitizerBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_blacklist); + parseSanitizerBlacklists("-fsanitize-blacklist", Opts, + Args.getAllArgValues(OPT_fsanitize_blacklist), + Diags); // -fxray-instrument Opts.XRayInstrument = Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -83,29 +83,33 @@ /// Produce a string containing comma-separated names of sanitizers in \p /// Sanitizers set. -static std::string toString(const clang::SanitizerSet &Sanitizers); - -static bool getDefaultBlacklist(const Driver &D, SanitizerMask Kinds, - std::string &BLPath) { - const char *BlacklistFile = nullptr; - if (Kinds & Address) - BlacklistFile = "asan_blacklist.txt"; - else if (Kinds & Memory) - BlacklistFile = "msan_blacklist.txt"; - else if (Kinds & Thread) - BlacklistFile = "tsan_blacklist.txt"; - else if (Kinds & DataFlow) - BlacklistFile = "dfsan_abilist.txt"; - else if (Kinds & CFI) - BlacklistFile = "cfi_blacklist.txt"; - - if (BlacklistFile) { +static std::string toString(SanitizerMask Kinds); +static std::string toString(SanitizerSet Sanitizers); + +/// Given the name of a sanitizer, produce the corresponding ordinal value. +static SanitizerMask toMask(StringRef SanitizerFlag); + +void SanitizerArgs::collectDefaultBlacklists(const Driver &D, + SanitizerMask Kinds) { + const std::pair<SanitizerMask, const char *> Blacklists[] = { + {Address, "asan_blacklist.txt"}, + {Memory, "msan_blacklist.txt"}, + {Thread, "tsan_blacklist.txt"}, + {DataFlow, "dfsan_abilist.txt"}, + {CFI, "cfi_blacklist.txt"}}; + + for (const auto &BL : Blacklists) { + if (!(BL.first & Kinds)) + continue; + clang::SmallString<64> Path(D.ResourceDir); - llvm::sys::path::append(Path, BlacklistFile); - BLPath = Path.str(); - return true; + llvm::sys::path::append(Path, BL.second); + if (llvm::sys::fs::exists(Path)) { + SanitizerBlacklistFiles.push_back(Path.str()); + SanitizerBlacklists.emplace_back(BL.first, + SanitizerBlacklistFiles.back()); + } } - return false; } /// Sets group bits for every group that has at least one representative already @@ -386,34 +390,32 @@ TrappingKinds &= Kinds; // Setup blacklist files. - // Add default blacklist from resource directory. - { - std::string BLPath; - if (getDefaultBlacklist(D, Kinds, BLPath) && llvm::sys::fs::exists(BLPath)) - BlacklistFiles.push_back(BLPath); - } + collectDefaultBlacklists(D, Kinds); + // Parse -f(no-)sanitize-blacklist options. for (const auto *Arg : Args) { if (Arg->getOption().matches(options::OPT_fsanitize_blacklist)) { Arg->claim(); std::string BLPath = Arg->getValue(); if (llvm::sys::fs::exists(BLPath)) { - BlacklistFiles.push_back(BLPath); - ExtraDeps.push_back(BLPath); + SanitizerBlacklistFiles.push_back(BLPath); + SanitizerBlacklists.emplace_back(All, SanitizerBlacklistFiles.back()); + ExtraDeps.emplace_back(SanitizerBlacklistFiles.back()); } else D.Diag(clang::diag::err_drv_no_such_file) << BLPath; } else if (Arg->getOption().matches(options::OPT_fno_sanitize_blacklist)) { Arg->claim(); - BlacklistFiles.clear(); + SanitizerBlacklistFiles.clear(); + SanitizerBlacklists.clear(); ExtraDeps.clear(); } } // Validate blacklists format. { std::string BLError; std::unique_ptr<llvm::SpecialCaseList> SCL( - llvm::SpecialCaseList::create(BlacklistFiles, BLError)); + llvm::SpecialCaseList::create(SanitizerBlacklistFiles, BLError)); if (!SCL.get()) D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError; } @@ -572,18 +574,34 @@ TrapSanitizers.Mask |= TrappingKinds; } -static std::string toString(const clang::SanitizerSet &Sanitizers) { +static std::string toString(SanitizerMask Kinds) { std::string Res; #define SANITIZER(NAME, ID) \ - if (Sanitizers.has(ID)) { \ + if (Kinds & ID) { \ if (!Res.empty()) \ Res += ","; \ Res += NAME; \ } + #include "clang/Basic/Sanitizers.def" +#undef SANITIZER return Res; } +static std::string toString(SanitizerSet Sanitizers) { + return toString(Sanitizers.Mask); +} + +static SanitizerMask toMask(StringRef SanitizerFlag) { +#define SANITIZER(NAME, ID) \ + if (SanitizerFlag == NAME) \ + return ID; + +#include "clang/Basic/Sanitizers.def" +#undef SANITIZER + return SanitizerMask(); +} + static void addIncludeLinkerOption(const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, @@ -598,6 +616,34 @@ CmdArgs.push_back(Args.MakeArgString(LinkerOptionFlag)); } +std::string SanitizerArgs::encodeBlacklistArg(const SanitizerBlacklist &SB) { + return toString(SB.Sanitizers) + ":" + SB.Path.str(); +} + +bool SanitizerArgs::decodeBlacklistArg(const std::string &Arg, + SanitizerMask &Kinds, + std::string &Path) { + StringRef LHS, RHS; + std::tie(LHS, RHS) = StringRef(Arg).split(':'); + if (LHS.empty() || RHS.empty()) + return false; + + SmallVector<StringRef, 4> Sanitizers; + LHS.split(Sanitizers, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + if (Sanitizers.empty()) + return false; + + for (StringRef Sanitizer : Sanitizers) { + if (SanitizerMask Kind = toMask(Sanitizer)) + Kinds |= Kind; + else + return false; + } + + Path = RHS.str(); + return true; +} + void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, types::ID InputType) const { @@ -661,9 +707,9 @@ CmdArgs.push_back( Args.MakeArgString("-fsanitize-trap=" + toString(TrapSanitizers))); - for (const auto &BLPath : BlacklistFiles) { + for (const auto &SB : SanitizerBlacklists) { SmallString<64> BlacklistOpt("-fsanitize-blacklist="); - BlacklistOpt += BLPath; + BlacklistOpt += encodeBlacklistArg(SB); CmdArgs.push_back(Args.MakeArgString(BlacklistOpt)); } for (const auto &Dep : ExtraDeps) { Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -1116,7 +1116,8 @@ /// annotations are emitted during finalization of the LLVM code. void AddGlobalAnnotations(const ValueDecl *D, llvm::GlobalValue *GV); - bool isInSanitizerBlacklist(llvm::Function *Fn, SourceLocation Loc) const; + bool isInSanitizerBlacklist(SanitizerMask Kind, llvm::Function *Fn, + SourceLocation Loc) const; bool isInSanitizerBlacklist(llvm::GlobalVariable *GV, SourceLocation Loc, QualType Ty, Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1441,35 +1441,37 @@ Annotations.push_back(EmitAnnotateAttr(GV, I, D->getLocation())); } -bool CodeGenModule::isInSanitizerBlacklist(llvm::Function *Fn, +bool CodeGenModule::isInSanitizerBlacklist(SanitizerMask Kind, + llvm::Function *Fn, SourceLocation Loc) const { const auto &SanitizerBL = getContext().getSanitizerBlacklist(); // Blacklist by function name. - if (SanitizerBL.isBlacklistedFunction(Fn->getName())) + if (SanitizerBL.isBlacklistedFunction(Fn->getName(), Kind)) return true; // Blacklist by location. if (Loc.isValid()) - return SanitizerBL.isBlacklistedLocation(Loc); + return SanitizerBL.isBlacklistedLocation(Loc, Kind); // If location is unknown, this may be a compiler-generated function. Assume // it's located in the main file. auto &SM = Context.getSourceManager(); if (const auto *MainFile = SM.getFileEntryForID(SM.getMainFileID())) { - return SanitizerBL.isBlacklistedFile(MainFile->getName()); + return SanitizerBL.isBlacklistedFile(MainFile->getName(), Kind); } return false; } bool CodeGenModule::isInSanitizerBlacklist(llvm::GlobalVariable *GV, SourceLocation Loc, QualType Ty, StringRef Category) const { // For now globals can be blacklisted only in ASan and KASan. - if (!LangOpts.Sanitize.hasOneOf( - SanitizerKind::Address | SanitizerKind::KernelAddress)) + SanitizerMask ASanMask = + SanitizerKind::Address | SanitizerKind::KernelAddress; + if (!LangOpts.Sanitize.hasOneOf(ASanMask)) return false; const auto &SanitizerBL = getContext().getSanitizerBlacklist(); - if (SanitizerBL.isBlacklistedGlobal(GV->getName(), Category)) + if (SanitizerBL.isBlacklistedGlobal(GV->getName(), ASanMask, Category)) return true; - if (SanitizerBL.isBlacklistedLocation(Loc, Category)) + if (SanitizerBL.isBlacklistedLocation(Loc, ASanMask, Category)) return true; // Check global type. if (!Ty.isNull()) { @@ -1481,7 +1483,7 @@ // We allow to blacklist only record types (classes, structs etc.) if (Ty->isRecordType()) { std::string TypeStr = Ty.getAsString(getContext().getPrintingPolicy()); - if (SanitizerBL.isBlacklistedType(TypeStr, Category)) + if (SanitizerBL.isBlacklistedType(TypeStr, ASanMask, Category)) return true; } } Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -733,8 +733,19 @@ CurFnInfo = &FnInfo; assert(CurFn->isDeclaration() && "Function already has body?"); - if (CGM.isInSanitizerBlacklist(Fn, Loc)) - SanOpts.clear(); + // If this function has been blacklisted for any of the enabled sanitizers, + // disable the sanitizer for the function. + do { +#define SANITIZER(NAME, ID) \ + if (SanOpts.empty()) \ + break; \ + if (SanOpts.has(SanitizerKind::ID)) \ + if (CGM.isInSanitizerBlacklist(SanitizerKind::ID, Fn, Loc)) \ + SanOpts.set(SanitizerKind::ID, false); + +#include "clang/Basic/Sanitizers.def" +#undef SANITIZER + } while (0); if (D) { // Apply the no_sanitize* attributes to SanOpts. Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -662,7 +662,7 @@ // Blacklist based on the mangled type. if (!CGM.getContext().getSanitizerBlacklist().isBlacklistedType( - Out.str())) { + Out.str(), SanitizerKind::Vptr)) { llvm::hash_code TypeHash = hash_value(Out.str()); // Load the vptr, and compute hash_16_bytes(TypeHash, vptr). Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -278,17 +278,23 @@ if (!getLangOpts().Exceptions) Fn->setDoesNotThrow(); - if (!isInSanitizerBlacklist(Fn, Loc)) { - if (getLangOpts().Sanitize.hasOneOf(SanitizerKind::Address | - SanitizerKind::KernelAddress)) + SanitizerMask ASanMask = + SanitizerKind::Address | SanitizerKind::KernelAddress; + if (getLangOpts().Sanitize.hasOneOf(ASanMask)) + if (!isInSanitizerBlacklist(ASanMask, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeAddress); - if (getLangOpts().Sanitize.has(SanitizerKind::Thread)) + + if (getLangOpts().Sanitize.has(SanitizerKind::Thread)) + if (!isInSanitizerBlacklist(SanitizerKind::Thread, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeThread); - if (getLangOpts().Sanitize.has(SanitizerKind::Memory)) + + if (getLangOpts().Sanitize.has(SanitizerKind::Memory)) + if (!isInSanitizerBlacklist(SanitizerKind::Memory, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeMemory); - if (getLangOpts().Sanitize.has(SanitizerKind::SafeStack)) + + if (getLangOpts().Sanitize.has(SanitizerKind::SafeStack)) + if (!isInSanitizerBlacklist(SanitizerKind::SafeStack, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SafeStack); - } return Fn; } Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2605,7 +2605,8 @@ return; std::string TypeName = RD->getQualifiedNameAsString(); - if (getContext().getSanitizerBlacklist().isBlacklistedType(TypeName)) + if (getContext().getSanitizerBlacklist().isBlacklistedType( + TypeName, SanitizerKind::CFI)) return; SanitizerScope SanScope(this); @@ -2688,7 +2689,8 @@ return false; std::string TypeName = RD->getQualifiedNameAsString(); - return !getContext().getSanitizerBlacklist().isBlacklistedType(TypeName); + return !getContext().getSanitizerBlacklist().isBlacklistedType( + TypeName, SanitizerKind::CFI); } llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad( Index: lib/Basic/SanitizerBlacklist.cpp =================================================================== --- lib/Basic/SanitizerBlacklist.cpp +++ lib/Basic/SanitizerBlacklist.cpp @@ -15,32 +15,50 @@ using namespace clang; -SanitizerBlacklist::SanitizerBlacklist( - const std::vector<std::string> &BlacklistPaths, SourceManager &SM) - : SCL(llvm::SpecialCaseList::createOrDie(BlacklistPaths)), SM(SM) {} - -bool SanitizerBlacklist::isBlacklistedGlobal(StringRef GlobalName, - StringRef Category) const { - return SCL->inSection("global", GlobalName, Category); +SanitizerBlacklistInfo::SanitizerBlacklistInfo( + ArrayRef<SanitizerBlacklist> Blacklists, SourceManager &SM) + : SM(SM) { + for (const auto &SB : Blacklists) + BLs.emplace_back(SB.Sanitizers, + llvm::SpecialCaseList::createOrDie({SB.Path})); } -bool SanitizerBlacklist::isBlacklistedType(StringRef MangledTypeName, +bool SanitizerBlacklistInfo::isBlacklisted(StringRef SectionName, + StringRef Ident, SanitizerMask Kind, StringRef Category) const { - return SCL->inSection("type", MangledTypeName, Category); + for (const auto &BL : BLs) + if ((BL.Sanitizers & Kind) && + BL.SCL->inSection(SectionName, Ident, Category)) + return true; + return false; } -bool SanitizerBlacklist::isBlacklistedFunction(StringRef FunctionName) const { - return SCL->inSection("fun", FunctionName); +bool SanitizerBlacklistInfo::isBlacklistedGlobal(StringRef GlobalName, + SanitizerMask Kind, + StringRef Category) const { + return isBlacklisted("global", GlobalName, Kind, Category); } -bool SanitizerBlacklist::isBlacklistedFile(StringRef FileName, - StringRef Category) const { - return SCL->inSection("src", FileName, Category); +bool SanitizerBlacklistInfo::isBlacklistedType(StringRef MangledTypeName, + SanitizerMask Kind, + StringRef Category) const { + return isBlacklisted("type", MangledTypeName, Kind, Category); +} + +bool SanitizerBlacklistInfo::isBlacklistedFunction(StringRef FunctionName, + SanitizerMask Kind) const { + return isBlacklisted("fun", FunctionName, Kind, StringRef()); } -bool SanitizerBlacklist::isBlacklistedLocation(SourceLocation Loc, +bool SanitizerBlacklistInfo::isBlacklistedFile(StringRef FileName, + SanitizerMask Kind, StringRef Category) const { - return Loc.isValid() && - isBlacklistedFile(SM.getFilename(SM.getFileLoc(Loc)), Category); + return isBlacklisted("src", FileName, Kind, Category); } +bool SanitizerBlacklistInfo::isBlacklistedLocation(SourceLocation Loc, + SanitizerMask Kind, + StringRef Category) const { + return Loc.isValid() && + isBlacklistedFile(SM.getFilename(SM.getFileLoc(Loc)), Kind, Category); +} Index: lib/Basic/LangOptions.cpp =================================================================== --- lib/Basic/LangOptions.cpp +++ lib/Basic/LangOptions.cpp @@ -32,6 +32,7 @@ // FIXME: This should not be reset; modules can be different with different // sanitizer options (this affects __has_feature(address_sanitizer) etc). Sanitize.clear(); + SanitizerBlacklists.clear(); SanitizerBlacklistFiles.clear(); XRayAlwaysInstrumentFiles.clear(); XRayNeverInstrumentFiles.clear(); Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3907,8 +3907,9 @@ bool RecordDecl::mayInsertExtraPadding(bool EmitRemark) const { ASTContext &Context = getASTContext(); - if (!Context.getLangOpts().Sanitize.hasOneOf( - SanitizerKind::Address | SanitizerKind::KernelAddress) || + SanitizerMask ASanMask = + SanitizerKind::Address | SanitizerKind::KernelAddress; + if (!Context.getLangOpts().Sanitize.hasOneOf(ASanMask) || !Context.getLangOpts().SanitizeAddressFieldPadding) return false; const auto &Blacklist = Context.getSanitizerBlacklist(); @@ -3927,9 +3928,10 @@ ReasonToReject = 4; // has trivial destructor. else if (CXXRD->isStandardLayout()) ReasonToReject = 5; // is standard layout. - else if (Blacklist.isBlacklistedLocation(getLocation(), "field-padding")) + else if (Blacklist.isBlacklistedLocation(getLocation(), ASanMask, + "field-padding")) ReasonToReject = 6; // is in a blacklisted file. - else if (Blacklist.isBlacklistedType(getQualifiedNameAsString(), + else if (Blacklist.isBlacklistedType(getQualifiedNameAsString(), ASanMask, "field-padding")) ReasonToReject = 7; // is blacklisted. Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -748,7 +748,7 @@ cudaConfigureCallDecl(nullptr), FirstLocalImport(), LastLocalImport(), ExternCContext(nullptr), MakeIntegerSeqDecl(nullptr), TypePackElementDecl(nullptr), SourceMgr(SM), LangOpts(LOpts), - SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), + SanitizerBL(new SanitizerBlacklistInfo(LangOpts.SanitizerBlacklists, SM)), XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles, LangOpts.XRayNeverInstrumentFiles, SM)), AddrSpaceMap(nullptr), Target(nullptr), AuxTarget(nullptr), Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -10,24 +10,25 @@ #define LLVM_CLANG_DRIVER_SANITIZERARGS_H #include "clang/Basic/Sanitizers.h" +#include "clang/Basic/SanitizerBlacklist.h" #include "clang/Driver/Types.h" +#include "llvm/ADT/Optional.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include <string> #include <vector> namespace clang { namespace driver { +class Driver; class ToolChain; class SanitizerArgs { SanitizerSet Sanitizers; SanitizerSet RecoverableSanitizers; SanitizerSet TrapSanitizers; - std::vector<std::string> BlacklistFiles; - std::vector<std::string> ExtraDeps; int CoverageFeatures = 0; int MsanTrackOrigins = 0; bool MsanUseAfterDtor = false; @@ -42,6 +43,18 @@ bool TsanFuncEntryExit = true; bool TsanAtomics = true; + /// Blacklists which each apply to specific sanitizers. + std::vector<SanitizerBlacklist> SanitizerBlacklists; + + /// Paths to all sanitizer blacklist files. + std::vector<std::string> SanitizerBlacklistFiles; + + /// Paths to sanitizer blacklist files which need depfile entries. + std::vector<StringRef> ExtraDeps; + + /// Collect all default blacklists for the sanitizers enabled in \p Kinds. + void collectDefaultBlacklists(const Driver &D, SanitizerMask Kinds); + public: /// Parses the sanitizer arguments from an argument list. SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args); @@ -73,6 +86,13 @@ bool hasCrossDsoCfi() const { return CfiCrossDso; } void addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, types::ID InputType) const; + + /// Encode a sanitizer blacklist argument as a string. + static std::string encodeBlacklistArg(const SanitizerBlacklist &SB); + + /// Decode a sanitizer blacklist argument. Returns true on success. + static bool decodeBlacklistArg(const std::string &Arg, SanitizerMask &Kinds, + std::string &Path); }; } // namespace driver Index: include/clang/Basic/SanitizerBlacklist.h =================================================================== --- include/clang/Basic/SanitizerBlacklist.h +++ include/clang/Basic/SanitizerBlacklist.h @@ -15,29 +15,54 @@ #define LLVM_CLANG_BASIC_SANITIZERBLACKLIST_H #include "clang/Basic/LLVM.h" +#include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/SpecialCaseList.h" #include <memory> namespace clang { -class SanitizerBlacklist { - std::unique_ptr<llvm::SpecialCaseList> SCL; +struct SanitizerBlacklist { + /// The set of sanitizers this blacklist applies to. + SanitizerMask Sanitizers; + + /// The path to the blacklist file. + StringRef Path; + + SanitizerBlacklist(SanitizerMask SM, StringRef P) : Sanitizers(SM), Path(P) {} +}; + +class SanitizerBlacklistInfo { + struct Blacklist { + SanitizerMask Sanitizers; + std::unique_ptr<llvm::SpecialCaseList> SCL; + + Blacklist(SanitizerMask Sanitizers, + std::unique_ptr<llvm::SpecialCaseList> SCL) + : Sanitizers(Sanitizers), SCL(std::move(SCL)) {} + }; + + SmallVector<Blacklist, 2> BLs; SourceManager &SM; + bool isBlacklisted(StringRef SectionName, StringRef Ident, SanitizerMask Kind, + StringRef Category) const; + public: - SanitizerBlacklist(const std::vector<std::string> &BlacklistPaths, - SourceManager &SM); - bool isBlacklistedGlobal(StringRef GlobalName, + SanitizerBlacklistInfo(ArrayRef<SanitizerBlacklist> Blacklists, + SourceManager &SM); + bool isBlacklistedGlobal(StringRef GlobalName, SanitizerMask Kind, StringRef Category = StringRef()) const; - bool isBlacklistedType(StringRef MangledTypeName, + bool isBlacklistedType(StringRef MangledTypeName, SanitizerMask Kind, StringRef Category = StringRef()) const; - bool isBlacklistedFunction(StringRef FunctionName) const; - bool isBlacklistedFile(StringRef FileName, + bool isBlacklistedFunction(StringRef FunctionName, SanitizerMask Kind) const; + bool isBlacklistedFile(StringRef FileName, SanitizerMask Kind, StringRef Category = StringRef()) const; - bool isBlacklistedLocation(SourceLocation Loc, + bool isBlacklistedLocation(SourceLocation Loc, SanitizerMask Kind, StringRef Category = StringRef()) const; }; Index: include/clang/Basic/LangOptions.h =================================================================== --- include/clang/Basic/LangOptions.h +++ include/clang/Basic/LangOptions.h @@ -19,6 +19,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Sanitizers.h" +#include "clang/Basic/SanitizerBlacklist.h" #include "clang/Basic/Visibility.h" #include <string> #include <vector> @@ -98,8 +99,11 @@ /// \brief Set of enabled sanitizers. SanitizerSet Sanitize; - /// \brief Paths to blacklist files specifying which objects + /// \brief Per-sanitizer blacklist files specifying which objects /// (files, functions, variables) should not be instrumented. + std::vector<SanitizerBlacklist> SanitizerBlacklists; + + /// Paths to sanitizer blacklist files. std::vector<std::string> SanitizerBlacklistFiles; /// \brief Paths to the XRay "always instrument" files specifying which Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -474,7 +474,7 @@ /// \brief Blacklist object that is used by sanitizers to decide which /// entities should not be instrumented. - std::unique_ptr<SanitizerBlacklist> SanitizerBL; + std::unique_ptr<SanitizerBlacklistInfo> SanitizerBL; /// \brief Function filtering mechanism to determine whether a given function /// should be imbued with the XRay "always" or "never" attributes. @@ -658,7 +658,7 @@ const LangOptions& getLangOpts() const { return LangOpts; } - const SanitizerBlacklist &getSanitizerBlacklist() const { + const SanitizerBlacklistInfo &getSanitizerBlacklist() const { return *SanitizerBL; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits