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 SanitizerBL; + std::unique_ptr 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; } 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 #include @@ -99,8 +100,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 SanitizerBlacklists; + + /// Paths to sanitizer blacklist files. std::vector SanitizerBlacklistFiles; /// \brief Paths to the XRay "always instrument" files specifying which 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 namespace clang { -class SanitizerBlacklist { - std::unique_ptr 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 SCL; + + Blacklist(SanitizerMask Sanitizers, + std::unique_ptr SCL) + : Sanitizers(Sanitizers), SCL(std::move(SCL)) {} + }; + + SmallVector BLs; SourceManager &SM; + bool isBlacklisted(StringRef SectionName, StringRef Ident, SanitizerMask Kind, + StringRef Category) const; + public: - SanitizerBlacklist(const std::vector &BlacklistPaths, - SourceManager &SM); - bool isBlacklistedGlobal(StringRef GlobalName, + SanitizerBlacklistInfo(ArrayRef 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/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -10,7 +10,9 @@ #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 @@ -19,6 +21,7 @@ namespace clang { namespace driver { +class Driver; class ToolChain; class SanitizerArgs { @@ -26,8 +29,6 @@ SanitizerSet RecoverableSanitizers; SanitizerSet TrapSanitizers; - std::vector BlacklistFiles; - std::vector ExtraDeps; int CoverageFeatures = 0; int MsanTrackOrigins = 0; bool MsanUseAfterDtor = false; @@ -45,6 +46,18 @@ bool TsanAtomics = true; bool MinimalRuntime = false; + /// Blacklists which each apply to specific sanitizers. + std::vector SanitizerBlacklists; + + /// Paths to all sanitizer blacklist files. + std::vector SanitizerBlacklistFiles; + + /// Paths to sanitizer blacklist files which need depfile entries. + std::vector 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); @@ -75,6 +88,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: 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: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3929,8 +3929,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(); @@ -3949,9 +3950,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/Basic/LangOptions.cpp =================================================================== --- lib/Basic/LangOptions.cpp +++ lib/Basic/LangOptions.cpp @@ -30,6 +30,7 @@ #include "clang/Basic/LangOptions.def" // These options do not affect AST generation. + SanitizerBlacklists.clear(); SanitizerBlacklistFiles.clear(); XRayAlwaysInstrumentFiles.clear(); XRayNeverInstrumentFiles.clear(); 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 &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 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/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2604,7 +2604,8 @@ return; std::string TypeName = RD->getQualifiedNameAsString(); - if (getContext().getSanitizerBlacklist().isBlacklistedType(TypeName)) + if (getContext().getSanitizerBlacklist().isBlacklistedType( + TypeName, SanitizerKind::CFI)) return; SanitizerScope SanScope(this); @@ -2687,7 +2688,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/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -316,17 +316,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/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -725,7 +725,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/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -767,8 +767,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/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -1106,7 +1106,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 @@ -1538,20 +1538,21 @@ 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; } @@ -1560,13 +1561,14 @@ 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()) { @@ -1578,7 +1580,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/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -89,31 +89,34 @@ /// 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"; - else if (Kinds & (Undefined | Integer | Nullability)) - BlacklistFile = "ubsan_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 Blacklists[] = { + {Address, "asan_blacklist.txt"}, + {Memory, "msan_blacklist.txt"}, + {Thread, "tsan_blacklist.txt"}, + {DataFlow, "dfsan_abilist.txt"}, + {CFI, "cfi_blacklist.txt"}, + {Undefined | Nullability | Integer, "ubsan_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 @@ -437,26 +440,24 @@ 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(); } } @@ -464,7 +465,7 @@ { std::string BLError; std::unique_ptr SCL( - llvm::SpecialCaseList::create(BlacklistFiles, BLError)); + llvm::SpecialCaseList::create(SanitizerBlacklistFiles, BLError)); if (!SCL.get()) D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError; } @@ -665,18 +666,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, @@ -691,6 +708,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 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 { @@ -758,9 +803,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/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 &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) { @@ -2453,7 +2471,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: 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 -disable-O0-optnone -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 -disable-O0-optnone -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 -disable-O0-optnone -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 -disable-O0-optnone -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 -disable-O0-optnone -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: 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/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/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-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/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/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/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/Driver/fsanitize-blacklist.c =================================================================== --- test/Driver/fsanitize-blacklist.c +++ test/Driver/fsanitize-blacklist.c @@ -20,7 +20,8 @@ // 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 // RUN: %clang -target x86_64-linux-gnu -fsanitize=integer -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-UBSAN-BLACKLIST --implicit-check-not=fdepfile-entry @@ -29,6 +30,16 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=alignment -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-UBSAN-BLACKLIST --implicit-check-not=fdepfile-entry // CHECK-DEFAULT-UBSAN-BLACKLIST: -fsanitize-blacklist={{.*}}ubsan_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/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.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