aboutsummaryrefslogtreecommitdiff
path: root/lib/Driver/SanitizerArgs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Driver/SanitizerArgs.cpp')
-rw-r--r--lib/Driver/SanitizerArgs.cpp192
1 files changed, 126 insertions, 66 deletions
diff --git a/lib/Driver/SanitizerArgs.cpp b/lib/Driver/SanitizerArgs.cpp
index 3c985a1f71d7..bdc17d11c92b 100644
--- a/lib/Driver/SanitizerArgs.cpp
+++ b/lib/Driver/SanitizerArgs.cpp
@@ -18,6 +18,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/TargetParser.h"
#include <memory>
using namespace clang;
@@ -30,20 +31,22 @@ enum : SanitizerMask {
NeedsUbsanCxxRt = Vptr | CFI,
NotAllowedWithTrap = Vptr,
NotAllowedWithMinimalRuntime = Vptr,
- RequiresPIE = DataFlow | Scudo,
+ RequiresPIE = DataFlow | HWAddress | Scudo,
NeedsUnwindTables = Address | HWAddress | Thread | Memory | DataFlow,
- SupportsCoverage = Address | HWAddress | KernelAddress | Memory | Leak |
- Undefined | Integer | Nullability | DataFlow | Fuzzer |
- FuzzerNoLink,
+ SupportsCoverage = Address | HWAddress | KernelAddress | KernelHWAddress |
+ Memory | Leak | Undefined | Integer | Nullability |
+ DataFlow | Fuzzer | FuzzerNoLink,
RecoverableByDefault = Undefined | Integer | Nullability,
Unrecoverable = Unreachable | Return,
+ AlwaysRecoverable = KernelAddress | KernelHWAddress,
LegacyFsanitizeRecoverMask = Undefined | Integer,
NeedsLTO = CFI,
TrappingSupported = (Undefined & ~Vptr) | UnsignedIntegerOverflow |
Nullability | LocalBounds | CFI,
TrappingDefault = CFI,
- CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast,
- CompatibleWithMinimalRuntime = TrappingSupported,
+ CFIClasses =
+ CFIVCall | CFINVCall | CFIMFCall | CFIDerivedCast | CFIUnrelatedCast,
+ CompatibleWithMinimalRuntime = TrappingSupported | Scudo,
};
enum CoverageFeature {
@@ -92,31 +95,32 @@ static std::string describeSanitizeArg(const llvm::opt::Arg *A,
/// 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 & HWAddress)
- BlacklistFile = "hwasan_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 void addDefaultBlacklists(const Driver &D, SanitizerMask Kinds,
+ std::vector<std::string> &BlacklistFiles) {
+ struct Blacklist {
+ const char *File;
+ SanitizerMask Mask;
+ } Blacklists[] = {{"asan_blacklist.txt", Address},
+ {"hwasan_blacklist.txt", HWAddress},
+ {"msan_blacklist.txt", Memory},
+ {"tsan_blacklist.txt", Thread},
+ {"dfsan_abilist.txt", DataFlow},
+ {"cfi_blacklist.txt", CFI},
+ {"ubsan_blacklist.txt", Undefined | Integer | Nullability}};
+
+ for (auto BL : Blacklists) {
+ if (!(Kinds & BL.Mask))
+ continue;
+
clang::SmallString<64> Path(D.ResourceDir);
- llvm::sys::path::append(Path, BlacklistFile);
- BLPath = Path.str();
- return true;
+ llvm::sys::path::append(Path, "share", BL.File);
+ if (llvm::sys::fs::exists(Path))
+ BlacklistFiles.push_back(Path.str());
+ else if (BL.Mask == CFI)
+ // If cfi_blacklist.txt cannot be found in the resource dir, driver
+ // should fail.
+ D.Diag(clang::diag::err_drv_no_such_file) << Path;
}
- return false;
}
/// Sets group bits for every group that has at least one representative already
@@ -176,7 +180,8 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
bool SanitizerArgs::needsUbsanRt() const {
// All of these include ubsan.
if (needsAsanRt() || needsMsanRt() || needsHwasanRt() || needsTsanRt() ||
- needsDfsanRt() || needsLsanRt() || needsCfiDiagRt() || needsScudoRt())
+ needsDfsanRt() || needsLsanRt() || needsCfiDiagRt() ||
+ (needsScudoRt() && !requiresMinimalRuntime()))
return false;
return (Sanitizers.Mask & NeedsUbsanRt & ~TrapSanitizers.Mask) ||
@@ -215,6 +220,10 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// Used to deduplicate diagnostics.
SanitizerMask Kinds = 0;
const SanitizerMask Supported = setGroupBits(TC.getSupportedSanitizers());
+
+ CfiCrossDso = Args.hasFlag(options::OPT_fsanitize_cfi_cross_dso,
+ options::OPT_fno_sanitize_cfi_cross_dso, false);
+
ToolChain::RTTIMode RTTIMode = TC.getRTTIMode();
const Driver &D = TC.getDriver();
@@ -274,6 +283,24 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
Add &= ~NotAllowedWithMinimalRuntime;
}
+ // FIXME: Make CFI on member function calls compatible with cross-DSO CFI.
+ // There are currently two problems:
+ // - Virtual function call checks need to pass a pointer to the function
+ // address to llvm.type.test and a pointer to the address point to the
+ // diagnostic function. Currently we pass the same pointer to both
+ // places.
+ // - Non-virtual function call checks may need to check multiple type
+ // identifiers.
+ // Fixing both of those may require changes to the cross-DSO CFI
+ // interface.
+ if (CfiCrossDso && (Add & CFIMFCall & ~DiagnosedKinds)) {
+ D.Diag(diag::err_drv_argument_not_allowed_with)
+ << "-fsanitize=cfi-mfcall"
+ << "-fsanitize-cfi-cross-dso";
+ Add &= ~CFIMFCall;
+ DiagnosedKinds |= CFIMFCall;
+ }
+
if (SanitizerMask KindsToDiagnose = Add & ~Supported & ~DiagnosedKinds) {
std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
D.Diag(diag::err_drv_unsupported_opt_for_target)
@@ -285,19 +312,18 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// Test for -fno-rtti + explicit -fsanitizer=vptr before expanding groups
// so we don't error out if -fno-rtti and -fsanitize=undefined were
// passed.
- if (Add & Vptr &&
- (RTTIMode == ToolChain::RM_DisabledImplicitly ||
- RTTIMode == ToolChain::RM_DisabledExplicitly)) {
- if (RTTIMode == ToolChain::RM_DisabledImplicitly)
- // Warn about not having rtti enabled if the vptr sanitizer is
- // explicitly enabled
- D.Diag(diag::warn_drv_disabling_vptr_no_rtti_default);
- else {
- const llvm::opt::Arg *NoRTTIArg = TC.getRTTIArg();
- assert(NoRTTIArg &&
- "RTTI disabled explicitly but we have no argument!");
+ if ((Add & Vptr) && (RTTIMode == ToolChain::RM_Disabled)) {
+ if (const llvm::opt::Arg *NoRTTIArg = TC.getRTTIArg()) {
+ assert(NoRTTIArg->getOption().matches(options::OPT_fno_rtti) &&
+ "RTTI disabled without -fno-rtti option?");
+ // The user explicitly passed -fno-rtti with -fsanitize=vptr, but
+ // the vptr sanitizer requires RTTI, so this is a user error.
D.Diag(diag::err_drv_argument_not_allowed_with)
<< "-fsanitize=vptr" << NoRTTIArg->getAsString(Args);
+ } else {
+ // The vptr sanitizer requires RTTI, but RTTI is disabled (by
+ // default). Warn that the vptr sanitizer is being disabled.
+ D.Diag(diag::warn_drv_disabling_vptr_no_rtti_default);
}
// Take out the Vptr sanitizer from the enabled sanitizers
@@ -313,6 +339,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
if (MinimalRuntime) {
Add &= ~NotAllowedWithMinimalRuntime;
}
+ if (CfiCrossDso)
+ Add &= ~CFIMFCall;
Add &= Supported;
if (Add & Fuzzer)
@@ -335,14 +363,41 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
}
}
+ std::pair<SanitizerMask, SanitizerMask> IncompatibleGroups[] = {
+ std::make_pair(Address, Thread | Memory),
+ std::make_pair(Thread, Memory),
+ std::make_pair(Leak, Thread | Memory),
+ std::make_pair(KernelAddress, Address | Leak | Thread | Memory),
+ std::make_pair(HWAddress, Address | Thread | Memory | KernelAddress),
+ std::make_pair(Efficiency, Address | HWAddress | Leak | Thread | Memory |
+ KernelAddress),
+ std::make_pair(Scudo, Address | HWAddress | Leak | Thread | Memory |
+ KernelAddress | Efficiency),
+ std::make_pair(SafeStack, Address | HWAddress | Leak | Thread | Memory |
+ KernelAddress | Efficiency),
+ std::make_pair(ShadowCallStack, Address | HWAddress | Leak | Thread |
+ Memory | KernelAddress | Efficiency |
+ SafeStack),
+ std::make_pair(KernelHWAddress, Address | HWAddress | Leak | Thread |
+ Memory | KernelAddress | Efficiency |
+ SafeStack | ShadowCallStack)};
+
// Enable toolchain specific default sanitizers if not explicitly disabled.
- Kinds |= TC.getDefaultSanitizers() & ~AllRemove;
+ SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove;
+
+ // Disable default sanitizers that are incompatible with explicitly requested
+ // ones.
+ for (auto G : IncompatibleGroups) {
+ SanitizerMask Group = G.first;
+ if ((Default & Group) && (Kinds & G.second))
+ Default &= ~Group;
+ }
+
+ Kinds |= Default;
// We disable the vptr sanitizer if it was enabled by group expansion but RTTI
// is disabled.
- if ((Kinds & Vptr) &&
- (RTTIMode == ToolChain::RM_DisabledImplicitly ||
- RTTIMode == ToolChain::RM_DisabledExplicitly)) {
+ if ((Kinds & Vptr) && (RTTIMode == ToolChain::RM_Disabled)) {
Kinds &= ~Vptr;
}
@@ -352,6 +407,15 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
<< lastArgumentForMask(D, Args, Kinds & NeedsLTO) << "-flto";
}
+ if ((Kinds & ShadowCallStack) &&
+ TC.getTriple().getArch() == llvm::Triple::aarch64 &&
+ !llvm::AArch64::isX18ReservedByDefault(TC.getTriple()) &&
+ !Args.hasArg(options::OPT_ffixed_x18)) {
+ D.Diag(diag::err_drv_argument_only_allowed_with)
+ << lastArgumentForMask(D, Args, Kinds & ShadowCallStack)
+ << "-ffixed-x18";
+ }
+
// Report error if there are non-trapping sanitizers that require
// c++abi-specific parts of UBSan runtime, and they are not provided by the
// toolchain. We don't have a good way to check the latter, so we just
@@ -372,16 +436,6 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
}
// Warn about incompatible groups of sanitizers.
- std::pair<SanitizerMask, SanitizerMask> IncompatibleGroups[] = {
- std::make_pair(Address, Thread | Memory),
- std::make_pair(Thread, Memory),
- std::make_pair(Leak, Thread | Memory),
- std::make_pair(KernelAddress, Address | Leak | Thread | Memory),
- std::make_pair(HWAddress, Address | Thread | Memory | KernelAddress),
- std::make_pair(Efficiency, Address | HWAddress | Leak | Thread | Memory |
- KernelAddress),
- std::make_pair(Scudo, Address | HWAddress | Leak | Thread | Memory |
- KernelAddress | Efficiency)};
for (auto G : IncompatibleGroups) {
SanitizerMask Group = G.first;
if (Kinds & Group) {
@@ -399,8 +453,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// default in ASan?
// Parse -f(no-)?sanitize-recover flags.
- SanitizerMask RecoverableKinds = RecoverableByDefault;
+ SanitizerMask RecoverableKinds = RecoverableByDefault | AlwaysRecoverable;
SanitizerMask DiagnosedUnrecoverableKinds = 0;
+ SanitizerMask DiagnosedAlwaysRecoverableKinds = 0;
for (const auto *Arg : Args) {
const char *DeprecatedReplacement = nullptr;
if (Arg->getOption().matches(options::OPT_fsanitize_recover)) {
@@ -428,7 +483,18 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
RecoverableKinds |= expandSanitizerGroups(Add);
Arg->claim();
} else if (Arg->getOption().matches(options::OPT_fno_sanitize_recover_EQ)) {
- RecoverableKinds &= ~expandSanitizerGroups(parseArgValues(D, Arg, true));
+ SanitizerMask Remove = parseArgValues(D, Arg, true);
+ // Report error if user explicitly tries to disable recovery from
+ // always recoverable sanitizer.
+ if (SanitizerMask KindsToDiagnose =
+ Remove & AlwaysRecoverable & ~DiagnosedAlwaysRecoverableKinds) {
+ SanitizerSet SetToDiagnose;
+ SetToDiagnose.Mask |= KindsToDiagnose;
+ D.Diag(diag::err_drv_unsupported_option_argument)
+ << Arg->getOption().getName() << toString(SetToDiagnose);
+ DiagnosedAlwaysRecoverableKinds |= KindsToDiagnose;
+ }
+ RecoverableKinds &= ~expandSanitizerGroups(Remove);
Arg->claim();
}
if (DeprecatedReplacement) {
@@ -444,11 +510,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// 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);
- }
+ addDefaultBlacklists(D, Kinds, BlacklistFiles);
// Parse -f(no-)sanitize-blacklist options.
for (const auto *Arg : Args) {
if (Arg->getOption().matches(options::OPT_fsanitize_blacklist)) {
@@ -457,9 +519,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
if (llvm::sys::fs::exists(BLPath)) {
BlacklistFiles.push_back(BLPath);
ExtraDeps.push_back(BLPath);
- } else
+ } 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();
@@ -517,8 +579,6 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
}
if (AllAddedKinds & CFI) {
- CfiCrossDso = Args.hasFlag(options::OPT_fsanitize_cfi_cross_dso,
- options::OPT_fno_sanitize_cfi_cross_dso, false);
// Without PIE, external function address may resolve to a PLT record, which
// can not be verified by the target module.
NeedPIE |= CfiCrossDso;