diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst index bde3c4a08e92137748a33b8e02f59dc47c9caff8..29c4d4ef90199a192dc1dde4812eff17d69eb718 100644 --- a/docs/UsersManual.rst +++ b/docs/UsersManual.rst @@ -1062,8 +1062,8 @@ are listed below. It is not possible to combine more than one of the ``-fsanitize=address``, ``-fsanitize=thread``, and ``-fsanitize=memory`` checkers in the same - program. The ``-fsanitize=undefined`` checks can be combined with other - sanitizers. + program. The ``-fsanitize=undefined`` checks can only be combined with + ``-fsanitize=address``. **-f[no-]sanitize-recover=check1,check2,...** diff --git a/include/clang/Driver/SanitizerArgs.h b/include/clang/Driver/SanitizerArgs.h index 5874c26b8cddfb86b31272b205d22e2263f7e012..04e28211956e4b0b2d30d1faeb9e88fc1be650a9 100644 --- a/include/clang/Driver/SanitizerArgs.h +++ b/include/clang/Driver/SanitizerArgs.h @@ -18,7 +18,6 @@ namespace clang { namespace driver { -class Driver; class ToolChain; class SanitizerArgs { @@ -58,7 +57,6 @@ class SanitizerArgs { private: void clear(); - bool getDefaultBlacklist(const Driver &D, std::string &BLPath); }; } // namespace driver diff --git a/lib/Driver/SanitizerArgs.cpp b/lib/Driver/SanitizerArgs.cpp index 610d2ae12ab3047c2b6a5f70e978f479fed2eaa4..fb907a7eea3da50d0402e4f69535917a27ea0630 100644 --- a/lib/Driver/SanitizerArgs.cpp +++ b/lib/Driver/SanitizerArgs.cpp @@ -137,6 +137,27 @@ static uint64_t getToolchainUnsupportedKinds(const ToolChain &TC) { return Unsupported; } +static bool getDefaultBlacklist(const Driver &D, uint64_t Kinds, + std::string &BLPath) { + const char *BlacklistFile = nullptr; + if (Kinds & SanitizeKind::Address) + BlacklistFile = "asan_blacklist.txt"; + else if (Kinds & SanitizeKind::Memory) + BlacklistFile = "msan_blacklist.txt"; + else if (Kinds & SanitizeKind::Thread) + BlacklistFile = "tsan_blacklist.txt"; + else if (Kinds & SanitizeKind::DataFlow) + BlacklistFile = "dfsan_abilist.txt"; + + if (BlacklistFile) { + clang::SmallString<64> Path(D.ResourceDir); + llvm::sys::path::append(Path, BlacklistFile); + BLPath = Path.str(); + return true; + } + return false; +} + bool SanitizerArgs::needsUbsanRt() const { return !UbsanTrapOnError && hasOneOf(Sanitizers, NeedsUbsanRt); } @@ -236,17 +257,51 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, AllRemove |= expandGroups(Remove); } } - addAllOf(Sanitizers, Kinds); // We disable the vptr sanitizer if it was enabled by group expansion but RTTI // is disabled. - if (Sanitizers.has(SanitizerKind::Vptr) && + if ((Kinds & SanitizeKind::Vptr) && (RTTIMode == ToolChain::RM_DisabledImplicitly || RTTIMode == ToolChain::RM_DisabledExplicitly)) { Kinds &= ~SanitizeKind::Vptr; - Sanitizers.set(SanitizerKind::Vptr, 0); } + // Warn about undefined sanitizer options that require runtime support. + UbsanTrapOnError = + Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, + options::OPT_fno_sanitize_undefined_trap_on_error, false); + if (UbsanTrapOnError && (Kinds & SanitizeKind::NotAllowedWithTrap)) { + D.Diag(clang::diag::err_drv_argument_not_allowed_with) + << lastArgumentForMask(D, Args, NotAllowedWithTrap) + << "-fsanitize-undefined-trap-on-error"; + Kinds &= ~SanitizeKind::NotAllowedWithTrap; + } + + // Warn about incompatible groups of sanitizers. + std::pair<uint64_t, uint64_t> IncompatibleGroups[] = { + std::make_pair(SanitizeKind::Address, SanitizeKind::Thread), + std::make_pair(SanitizeKind::Address, SanitizeKind::Memory), + std::make_pair(SanitizeKind::Thread, SanitizeKind::Memory), + std::make_pair(SanitizeKind::Leak, SanitizeKind::Thread), + std::make_pair(SanitizeKind::Leak, SanitizeKind::Memory), + std::make_pair(SanitizeKind::NeedsUbsanRt, SanitizeKind::Thread), + std::make_pair(SanitizeKind::NeedsUbsanRt, SanitizeKind::Memory)}; + for (auto G : IncompatibleGroups) { + uint64_t Group = G.first; + if (Kinds & Group) { + if (uint64_t Incompatible = Kinds & G.second) { + D.Diag(clang::diag::err_drv_argument_not_allowed_with) + << lastArgumentForMask(D, Args, Group) + << lastArgumentForMask(D, Args, Incompatible); + Kinds &= ~Incompatible; + } + } + } + // FIXME: Currently -fsanitize=leak is silently ignored in the presence of + // -fsanitize=address. Perhaps it should print an error, or perhaps + // -f(-no)sanitize=leak should change whether leak detection is enabled by + // default in ASan? + // Parse -f(no-)?sanitize-recover flags. uint64_t RecoverableKinds = RecoverableByDefault; uint64_t DiagnosedUnrecoverableKinds = 0; @@ -285,54 +340,12 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, } RecoverableKinds &= Kinds; RecoverableKinds &= ~Unrecoverable; - addAllOf(RecoverableSanitizers, RecoverableKinds); - - UbsanTrapOnError = - Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, - options::OPT_fno_sanitize_undefined_trap_on_error, false); - - // Warn about undefined sanitizer options that require runtime support. - if (UbsanTrapOnError && hasOneOf(Sanitizers, NotAllowedWithTrap)) { - D.Diag(clang::diag::err_drv_argument_not_allowed_with) - << lastArgumentForMask(D, Args, NotAllowedWithTrap) - << "-fsanitize-undefined-trap-on-error"; - } - - // Check for incompatible sanitizers. - bool NeedsAsan = Sanitizers.has(SanitizerKind::Address); - bool NeedsTsan = Sanitizers.has(SanitizerKind::Thread); - bool NeedsMsan = Sanitizers.has(SanitizerKind::Memory); - bool NeedsLsan = Sanitizers.has(SanitizerKind::Leak); - if (NeedsAsan && NeedsTsan) - D.Diag(clang::diag::err_drv_argument_not_allowed_with) - << lastArgumentForKind(D, Args, SanitizerKind::Address) - << lastArgumentForKind(D, Args, SanitizerKind::Thread); - if (NeedsAsan && NeedsMsan) - D.Diag(clang::diag::err_drv_argument_not_allowed_with) - << lastArgumentForKind(D, Args, SanitizerKind::Address) - << lastArgumentForKind(D, Args, SanitizerKind::Memory); - if (NeedsTsan && NeedsMsan) - D.Diag(clang::diag::err_drv_argument_not_allowed_with) - << lastArgumentForKind(D, Args, SanitizerKind::Thread) - << lastArgumentForKind(D, Args, SanitizerKind::Memory); - if (NeedsLsan && NeedsTsan) - D.Diag(clang::diag::err_drv_argument_not_allowed_with) - << lastArgumentForKind(D, Args, SanitizerKind::Leak) - << lastArgumentForKind(D, Args, SanitizerKind::Thread); - if (NeedsLsan && NeedsMsan) - D.Diag(clang::diag::err_drv_argument_not_allowed_with) - << lastArgumentForKind(D, Args, SanitizerKind::Leak) - << lastArgumentForKind(D, Args, SanitizerKind::Memory); - // FIXME: Currently -fsanitize=leak is silently ignored in the presence of - // -fsanitize=address. Perhaps it should print an error, or perhaps - // -f(-no)sanitize=leak should change whether leak detection is enabled by - // default in ASan? // Setup blacklist files. // Add default blacklist from resource directory. { std::string BLPath; - if (getDefaultBlacklist(D, BLPath) && llvm::sys::fs::exists(BLPath)) + if (getDefaultBlacklist(D, Kinds, BLPath) && llvm::sys::fs::exists(BLPath)) BlacklistFiles.push_back(BLPath); } // Parse -f(no-)sanitize-blacklist options. @@ -359,7 +372,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, } // Parse -f[no-]sanitize-memory-track-origins[=level] options. - if (NeedsMsan) { + if (Kinds & SanitizeKind::Memory) { if (Arg *A = Args.getLastArg(options::OPT_fsanitize_memory_track_origins_EQ, options::OPT_fsanitize_memory_track_origins, @@ -380,7 +393,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, } // Parse -fsanitize-coverage=N. Currently one of asan/msan/lsan is required. - if (hasOneOf(Sanitizers, SupportsCoverage)) { + if (Kinds & SanitizeKind::SupportsCoverage) { if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage)) { StringRef S = A->getValue(); // Legal values are 0..4. @@ -390,7 +403,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, } } - if (NeedsAsan) { + if (Kinds & SanitizeKind::Address) { AsanSharedRuntime = Args.hasArg(options::OPT_shared_libasan) || (TC.getTriple().getEnvironment() == llvm::Triple::Android); @@ -425,6 +438,10 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, // Parse -link-cxx-sanitizer flag. LinkCXXRuntimes = Args.hasArg(options::OPT_fsanitize_link_cxx_runtime) || D.CCCIsCXX(); + + // Finally, initialize the set of available and recoverable sanitizers. + addAllOf(Sanitizers, Kinds); + addAllOf(RecoverableSanitizers, RecoverableKinds); } static std::string toString(const clang::SanitizerSet &Sanitizers) { @@ -477,26 +494,6 @@ void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new")); } -bool SanitizerArgs::getDefaultBlacklist(const Driver &D, std::string &BLPath) { - const char *BlacklistFile = nullptr; - if (Sanitizers.has(SanitizerKind::Address)) - BlacklistFile = "asan_blacklist.txt"; - else if (Sanitizers.has(SanitizerKind::Memory)) - BlacklistFile = "msan_blacklist.txt"; - else if (Sanitizers.has(SanitizerKind::Thread)) - BlacklistFile = "tsan_blacklist.txt"; - else if (Sanitizers.has(SanitizerKind::DataFlow)) - BlacklistFile = "dfsan_abilist.txt"; - - if (BlacklistFile) { - SmallString<64> Path(D.ResourceDir); - llvm::sys::path::append(Path, BlacklistFile); - BLPath = Path.str(); - return true; - } - return false; -} - uint64_t parseValue(const char *Value) { uint64_t ParsedKind = llvm::StringSwitch<SanitizeKind>(Value) #define SANITIZER(NAME, ID) .Case(NAME, ID) diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index 343249d226b01b4f13a03edaf14f8c7b55b10929..da8ace3055dcb965150d450681934b973fa2537f 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -57,6 +57,9 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=leak,memory -pie -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANL-SANM // CHECK-SANL-SANM: '-fsanitize=leak' not allowed with '-fsanitize=memory' +// RUN: %clang -target x86_64-linux-gnu -fsanitize=signed-integer-overflow,memory -pie -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MSAN-UBSAN +// CHECK-MSAN-UBSAN: '-fsanitize=signed-integer-overflow' not allowed with '-fsanitize=memory' + // RUN: %clang -target x86_64-linux-gnu -fsanitize-memory-track-origins -pie %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ONLY-TRACK-ORIGINS // CHECK-ONLY-TRACK-ORIGINS: warning: argument unused during compilation: '-fsanitize-memory-track-origins' diff --git a/test/Driver/sanitizer-ld.c b/test/Driver/sanitizer-ld.c index dc8e93e50f26117fa389eb053281dc3a31de974b..501a01c4d81d355c3e7def9b0ede7079501ef1ae 100644 --- a/test/Driver/sanitizer-ld.c +++ b/test/Driver/sanitizer-ld.c @@ -280,19 +280,6 @@ // CHECK-LSAN-LINUX: "-lpthread" // CHECK-LSAN-LINUX: "-ldl" -// RUN: %clang -fsanitize=leak,undefined %s -### -o %t.o 2>&1 \ -// RUN: -target x86_64-unknown-linux \ -// RUN: --sysroot=%S/Inputs/basic_linux_tree \ -// RUN: | FileCheck --check-prefix=CHECK-LSAN-UBSAN-LINUX %s -// CHECK-LSAN-UBSAN-LINUX: "{{.*}}ld{{(.exe)?}}" -// CHECK-LSAN-UBSAN-LINUX-NOT: libclang_rt.san -// CHECK-LSAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.lsan-x86_64.a" "-no-whole-archive" -// CHECK-LSAN-UBSAN-LINUX-NOT: libclang_rt.san -// CHECK-LSAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan-x86_64.a" "-no-whole-archive" -// CHECK-LSAN-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx -// CHECK-LSAN-UBSAN-LINUX-NOT: "-lstdc++" -// CHECK-LSAN-UBSAN-LINUX: "-lpthread" - // RUN: %clang -fsanitize=leak,address %s -### -o %t.o 2>&1 \ // RUN: -target x86_64-unknown-linux \ // RUN: --sysroot=%S/Inputs/basic_linux_tree \