diff --git a/clang/docs/SanitizerCoverage.rst b/clang/docs/SanitizerCoverage.rst --- a/clang/docs/SanitizerCoverage.rst +++ b/clang/docs/SanitizerCoverage.rst @@ -289,6 +289,58 @@ // for every non-constant array index. void __sanitizer_cov_trace_gep(uintptr_t Idx); +Partially disabling instrumentation +=================================== + +It is sometimes useful to tell SanitizerCoverage to instrument only a subset of the +functions in your target. +With ``-fsanitize-coverage-whitelist=whitelist.txt`` +and ``-fsanitize-coverage-blacklist=blacklist.txt``, +you can specify such a subset through the combination of a whitelist and a blacklist. + +SanitizerCoverage will only instrument functions that satisfy two conditions. +First, the function should belong to a source file with a path that is both whitelisted +and not blacklisted. +Second, the function should have a mangled name that is both whitelisted and not blacklisted. + +The whitelist and blacklist format is similar to that of the sanitizer blacklist format. +The default whitelist will match every source file and every function. +The default blacklist will match no source file and no function. + +In most cases, the whitelist will list the folders or source files for which you want +instrumentation and allow all function names, while the blacklist will opt out some specific +files or functions that the whitelist loosely allowed. + +Here is an example whitelist: + +.. code-block:: none + + # Enable instrumentation for a whole folder + src:bar/* + # Enable instrumentation for a specific source file + src:foo/a.cpp + # Enable instrumentation for all functions in those files + fun:* + +And an example blacklist: + +.. code-block:: none + + # Disable instrumentation for a specific source file that the whitelist allowed + src:bar/b.cpp + # Disable instrumentation for a specific function that the whitelist allowed + fun:*myFunc* + +The use of ``*`` wildcards above is required because function names are matched after mangling. +Without the wildcards, one would have to write the whole mangled name. + +Be careful that the paths of source files are matched exactly as they are provided on the clang +command line. +For example, the whitelist above would include file ``bar/b.cpp`` if the path was provided +exactly like this, but would it would fail to include it with other ways to refer to the same +file such as ``./bar/b.cpp``, or ``bar\b.cpp`` on Windows. +So, please make sure to always double check that your lists are correctly applied. + Default implementation ====================== diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -306,6 +306,16 @@ /// List of dynamic shared object files to be loaded as pass plugins. std::vector PassPlugins; + /// Path to whitelist file specifying which objects + /// (files, functions) should exclusively be instrumented + /// by sanitizer coverage pass. + std::vector SanitizeCoverageWhitelistFiles; + + /// Path to blacklist file specifying which objects + /// (files, functions) listed for instrumentation by sanitizer + /// coverage pass should actually not be instrumented. + std::vector SanitizeCoverageBlacklistFiles; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -147,6 +147,10 @@ "invalid argument '%0' to -fdebug-prefix-map">; def err_drv_malformed_sanitizer_blacklist : Error< "malformed sanitizer blacklist: '%0'">; +def err_drv_malformed_sanitizer_coverage_whitelist : Error< + "malformed sanitizer coverage whitelist: '%0'">; +def err_drv_malformed_sanitizer_coverage_blacklist : Error< + "malformed sanitizer coverage blacklist: '%0'">; def err_drv_duplicate_config : Error< "no more than one option '--config' is allowed">; def err_drv_config_file_not_exist : Error< diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -966,6 +966,16 @@ Group, Flags<[CoreOption, DriverOption]>, HelpText<"Disable specified features of coverage instrumentation for " "Sanitizers">, Values<"func,bb,edge,indirect-calls,trace-bb,trace-cmp,trace-div,trace-gep,8bit-counters,trace-pc,trace-pc-guard,no-prune,inline-8bit-counters">; +def fsanitize_coverage_whitelist_EQ : Joined<["-"], "fsanitize-coverage-whitelist=">, + Group, Flags<[CoreOption, DriverOption]>, + HelpText<"Restrict sanitizer coverage instrumentation exclusively to modules and functions that match the provided special case list, except the blacklisted ones">; +def fsanitize_coverage_whitelist : Separate<["-"], "fsanitize-coverage-whitelist">, + Group, Flags<[CoreOption, DriverOption]>, Alias; +def fsanitize_coverage_blacklist_EQ : Joined<["-"], "fsanitize-coverage-blacklist=">, + Group, Flags<[CoreOption, DriverOption]>, + HelpText<"Disable sanitizer coverage instrumentation for modules and functions that match the provided special case list, even the whitelisted ones">; +def fsanitize_coverage_blacklist : Separate<["-"], "fsanitize-coverage-blacklist">, + Group, Flags<[CoreOption, DriverOption]>, Alias; def fsanitize_memory_track_origins_EQ : Joined<["-"], "fsanitize-memory-track-origins=">, Group, HelpText<"Enable origins tracking in MemorySanitizer">; diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -28,6 +28,8 @@ std::vector BlacklistFiles; std::vector ExtraDeps; int CoverageFeatures = 0; + std::vector CoverageWhitelistFiles; + std::vector CoverageBlacklistFiles; int MsanTrackOrigins = 0; bool MsanUseAfterDtor = true; bool CfiCrossDso = false; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -215,7 +215,7 @@ Opts.Inline8bitCounters = CGOpts.SanitizeCoverageInline8bitCounters; Opts.PCTable = CGOpts.SanitizeCoveragePCTable; Opts.StackDepth = CGOpts.SanitizeCoverageStackDepth; - PM.add(createSanitizerCoverageModulePass(Opts)); + PM.add(createSanitizerCoverageModulePass(Opts, CGOpts.SanitizeCoverageWhitelistFiles, CGOpts.SanitizeCoverageBlacklistFiles)); } // Check if ASan should use GC-friendly instrumentation for globals. diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -720,6 +720,44 @@ CoverageFeatures |= CoverageFunc; } + // Parse -fsanitize-coverage-{white,black}list options. + for (const auto *Arg : Args) { + if (Arg->getOption().matches(options::OPT_fsanitize_coverage_whitelist_EQ)) { + Arg->claim(); + std::string CWLPath = Arg->getValue(); + if (llvm::sys::fs::exists(CWLPath)) { + CoverageWhitelistFiles.push_back(CWLPath); + ExtraDeps.push_back(CWLPath); + } else { + D.Diag(clang::diag::err_drv_no_such_file) << CWLPath; + } + } else if (Arg->getOption().matches(options::OPT_fsanitize_coverage_blacklist_EQ)) { + Arg->claim(); + std::string CBLPath = Arg->getValue(); + if (llvm::sys::fs::exists(CBLPath)) { + CoverageBlacklistFiles.push_back(CBLPath); + ExtraDeps.push_back(CBLPath); + } else { + D.Diag(clang::diag::err_drv_no_such_file) << CBLPath; + } + } + } + // Validate whitelist and blacklist format. + if (CoverageWhitelistFiles.size() > 0) { + std::string WLError; + std::unique_ptr SCL( + llvm::SpecialCaseList::create(CoverageWhitelistFiles, WLError)); + if (!SCL.get()) + D.Diag(clang::diag::err_drv_malformed_sanitizer_coverage_whitelist) << WLError; + } + if (CoverageBlacklistFiles.size() > 0) { + std::string BLError; + std::unique_ptr SCL( + llvm::SpecialCaseList::create(CoverageBlacklistFiles, BLError)); + if (!SCL.get()) + D.Diag(clang::diag::err_drv_malformed_sanitizer_coverage_blacklist) << BLError; + } + SharedRuntime = Args.hasFlag(options::OPT_shared_libsan, options::OPT_static_libsan, TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() || @@ -886,6 +924,18 @@ CmdArgs.push_back(F.second); } + // Forward coverage whitelist and blacklist + for (const auto &CWLPath : CoverageWhitelistFiles) { + SmallString<64> CoverageWhitelistOpt("-fsanitize-coverage-whitelist="); + CoverageWhitelistOpt += CWLPath; + CmdArgs.push_back(Args.MakeArgString(CoverageWhitelistOpt)); + } + for (const auto &CBLPath : CoverageBlacklistFiles) { + SmallString<64> CoverageBlacklistOpt("-fsanitize-coverage-blacklist="); + CoverageBlacklistOpt += CBLPath; + CmdArgs.push_back(Args.MakeArgString(CoverageBlacklistOpt)); + } + if (TC.getTriple().isOSWindows() && needsUbsanRt()) { // Instruct the code generator to embed linker directives in the object file // that cause the required runtime libraries to be linked. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1111,6 +1111,8 @@ Opts.SanitizeCoveragePCTable = Args.hasArg(OPT_fsanitize_coverage_pc_table); Opts.SanitizeCoverageStackDepth = Args.hasArg(OPT_fsanitize_coverage_stack_depth); + Opts.SanitizeCoverageWhitelistFiles = Args.getAllArgValues(OPT_fsanitize_coverage_whitelist_EQ); + Opts.SanitizeCoverageBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_coverage_blacklist_EQ); Opts.SanitizeMemoryTrackOrigins = getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags); Opts.SanitizeMemoryUseAfterDtor = diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -183,7 +183,9 @@ // Insert SanitizerCoverage instrumentation. ModulePass *createSanitizerCoverageModulePass( - const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()); + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector& WhitelistFiles = std::vector(), + const std::vector& BlacklistFiles = std::vector()); /// Calculate what to divide by to scale counts. /// diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp --- a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SpecialCaseList.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" @@ -179,8 +180,16 @@ class SanitizerCoverageModule : public ModulePass { public: SanitizerCoverageModule( - const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector& WhitelistFiles = std::vector(), + const std::vector& BlacklistFiles = std::vector()) : ModulePass(ID), Options(OverrideFromCL(Options)) { + if (WhitelistFiles.size() > 0) { + Whitelist = SpecialCaseList::createOrDie(WhitelistFiles); + } + if (BlacklistFiles.size() > 0) { + Blacklist = SpecialCaseList::createOrDie(BlacklistFiles); + } initializeSanitizerCoverageModulePass(*PassRegistry::getPassRegistry()); } bool runOnModule(Module &M) override; @@ -250,6 +259,9 @@ SmallVector GlobalsToAppendToCompilerUsed; SanitizerCoverageOptions Options; + + std::unique_ptr Whitelist; + std::unique_ptr Blacklist; }; } // namespace @@ -313,6 +325,12 @@ bool SanitizerCoverageModule::runOnModule(Module &M) { if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; + if (Whitelist && + !Whitelist->inSection("coverage", "src", M.getSourceFileName())) + return false; + if (Blacklist && + Blacklist->inSection("coverage", "src", M.getSourceFileName())) + return false; C = &(M.getContext()); DL = &M.getDataLayout(); CurModule = &M; @@ -541,6 +559,10 @@ if (F.hasPersonalityFn() && isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) return false; + if (Whitelist && !Whitelist->inSection("coverage", "fun", F.getName())) + return false; + if (Blacklist && Blacklist->inSection("coverage", "fun", F.getName())) + return false; if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) SplitAllCriticalEdges(F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); SmallVector IndirCalls; @@ -898,6 +920,8 @@ "ModulePass", false, false) ModulePass *llvm::createSanitizerCoverageModulePass( - const SanitizerCoverageOptions &Options) { - return new SanitizerCoverageModule(Options); + const SanitizerCoverageOptions &Options, + const std::vector& WhitelistFiles, + const std::vector& BlacklistFiles) { + return new SanitizerCoverageModule(Options, WhitelistFiles, BlacklistFiles); }