Reimplement -fsanitize-recover family of flags.
Introduce the following -fsanitize-recover flags: - -fsanitize-recover=<list>: Enable recovery for selected checks or group of checks. It is forbidden to explicitly list unrecoverable sanitizers here (that is, "address", "unreachable", "return"). - -fno-sanitize-recover=<list>: Disable recovery for selected checks or group of checks. - -f(no-)?sanitize-recover is now a synonym for -f(no-)?sanitize-recover=undefined,integer and will soon be deprecated. These flags are parsed left to right, and mask of "recoverable" sanitizer is updated accordingly, much like what we do for -fsanitize= flags. -fsanitize= and -fsanitize-recover= flag families are independent. CodeGen change: If there is a single UBSan handler function, responsible for implementing multiple checks, which have different recoverable setting, then we emit two handler calls instead of one: the first one for the set of "unrecoverable" checks, another one - for set of "recoverable" checks. If all checks implemented by a handler have the same recoverability setting, then the generated code will be the same. llvm-svn: 225719
This commit is contained in:
parent
d88ab87064
commit
8845952b54
|
@ -1033,10 +1033,6 @@ are listed below.
|
||||||
|
|
||||||
Extra features of UndefinedBehaviorSanitizer:
|
Extra features of UndefinedBehaviorSanitizer:
|
||||||
|
|
||||||
- ``-fno-sanitize-recover``: By default, after a sanitizer diagnoses
|
|
||||||
an issue, it will attempt to continue executing the program if there
|
|
||||||
is a reasonable behavior it can give to the faulting operation. This
|
|
||||||
option causes the program to abort instead.
|
|
||||||
- ``-fsanitize-undefined-trap-on-error``: Causes traps to be emitted
|
- ``-fsanitize-undefined-trap-on-error``: Causes traps to be emitted
|
||||||
rather than calls to runtime libraries when a problem is detected.
|
rather than calls to runtime libraries when a problem is detected.
|
||||||
This option is intended for use in cases where the sanitizer runtime
|
This option is intended for use in cases where the sanitizer runtime
|
||||||
|
@ -1056,6 +1052,17 @@ are listed below.
|
||||||
program. The ``-fsanitize=undefined`` checks can be combined with other
|
program. The ``-fsanitize=undefined`` checks can be combined with other
|
||||||
sanitizers.
|
sanitizers.
|
||||||
|
|
||||||
|
**-f[no-]sanitize-recover=check1,check2,...**
|
||||||
|
|
||||||
|
Controls which checks enabled by ``-fsanitize=`` flag are non-fatal.
|
||||||
|
If the check is fatal, program will halt after the first error
|
||||||
|
of this kind is detected and error report is printed.
|
||||||
|
|
||||||
|
By default, non-fatal checks are those enabled by UndefinedBehaviorSanitizer,
|
||||||
|
except for ``-fsanitize=return`` and ``-fsanitize=unreachable``. Some
|
||||||
|
sanitizers (e.g. :doc:`AddressSanitizer`) may not support recovery,
|
||||||
|
and always crash the program after the issue is detected.
|
||||||
|
|
||||||
.. option:: -fno-assume-sane-operator-new
|
.. option:: -fno-assume-sane-operator-new
|
||||||
|
|
||||||
Don't assume that the C++'s new operator is sane.
|
Don't assume that the C++'s new operator is sane.
|
||||||
|
|
|
@ -534,11 +534,17 @@ def fno_sanitize_memory_track_origins : Flag<["-"], "fno-sanitize-memory-track-o
|
||||||
def fsanitize_address_field_padding : Joined<["-"], "fsanitize-address-field-padding=">,
|
def fsanitize_address_field_padding : Joined<["-"], "fsanitize-address-field-padding=">,
|
||||||
Group<f_clang_Group>, Flags<[CC1Option]>,
|
Group<f_clang_Group>, Flags<[CC1Option]>,
|
||||||
HelpText<"Level of field padding for AddressSanitizer">;
|
HelpText<"Level of field padding for AddressSanitizer">;
|
||||||
def fsanitize_recover : Flag<["-"], "fsanitize-recover">,
|
def fsanitize_recover : Flag<["-"], "fsanitize-recover">, Group<f_clang_Group>;
|
||||||
Group<f_clang_Group>;
|
|
||||||
def fno_sanitize_recover : Flag<["-"], "fno-sanitize-recover">,
|
def fno_sanitize_recover : Flag<["-"], "fno-sanitize-recover">,
|
||||||
Group<f_clang_Group>, Flags<[CC1Option]>,
|
Group<f_clang_Group>;
|
||||||
HelpText<"Disable sanitizer check recovery">;
|
def fsanitize_recover_EQ : CommaJoined<["-"], "fsanitize-recover=">,
|
||||||
|
Group<f_clang_Group>,
|
||||||
|
Flags<[CC1Option]>,
|
||||||
|
HelpText<"Enable recovery for specified sanitizers">;
|
||||||
|
def fno_sanitize_recover_EQ
|
||||||
|
: CommaJoined<["-"], "fno-sanitize-recover=">,
|
||||||
|
Group<f_clang_Group>,
|
||||||
|
HelpText<"Disable recovery for specified sanitizers">;
|
||||||
def fsanitize_undefined_trap_on_error : Flag<["-"], "fsanitize-undefined-trap-on-error">,
|
def fsanitize_undefined_trap_on_error : Flag<["-"], "fsanitize-undefined-trap-on-error">,
|
||||||
Group<f_clang_Group>, Flags<[CC1Option]>;
|
Group<f_clang_Group>, Flags<[CC1Option]>;
|
||||||
def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">,
|
def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">,
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ToolChain;
|
||||||
|
|
||||||
class SanitizerArgs {
|
class SanitizerArgs {
|
||||||
SanitizerSet Sanitizers;
|
SanitizerSet Sanitizers;
|
||||||
bool SanitizeRecover;
|
SanitizerSet RecoverableSanitizers;
|
||||||
|
|
||||||
std::string BlacklistFile;
|
std::string BlacklistFile;
|
||||||
int SanitizeCoverage;
|
int SanitizeCoverage;
|
||||||
|
|
|
@ -160,9 +160,6 @@ ENUM_CODEGENOPT(Inlining, InliningMethod, 2, NoInlining)
|
||||||
/// The default TLS model to use.
|
/// The default TLS model to use.
|
||||||
ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel)
|
ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel)
|
||||||
|
|
||||||
CODEGENOPT(SanitizeRecover, 1, 1) ///< Attempt to recover from sanitizer checks
|
|
||||||
///< by continuing execution when possible
|
|
||||||
|
|
||||||
#undef CODEGENOPT
|
#undef CODEGENOPT
|
||||||
#undef ENUM_CODEGENOPT
|
#undef ENUM_CODEGENOPT
|
||||||
#undef VALUE_CODEGENOPT
|
#undef VALUE_CODEGENOPT
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "clang/Basic/Sanitizers.h"
|
||||||
#include "llvm/Support/Regex.h"
|
#include "llvm/Support/Regex.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
@ -179,6 +180,10 @@ public:
|
||||||
/// Set of files definining the rules for the symbol rewriting.
|
/// Set of files definining the rules for the symbol rewriting.
|
||||||
std::vector<std::string> RewriteMapFiles;
|
std::vector<std::string> RewriteMapFiles;
|
||||||
|
|
||||||
|
/// Set of sanitizer checks that are non-fatal (i.e. execution should be
|
||||||
|
/// continued when possible).
|
||||||
|
SanitizerSet SanitizeRecover;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Define accessors/mutators for code generation options of enumeration type.
|
// Define accessors/mutators for code generation options of enumeration type.
|
||||||
#define CODEGENOPT(Name, Bits, Default)
|
#define CODEGENOPT(Name, Bits, Default)
|
||||||
|
|
|
@ -2211,9 +2211,10 @@ llvm::Constant *CodeGenFunction::EmitCheckSourceLocation(SourceLocation Loc) {
|
||||||
namespace {
|
namespace {
|
||||||
/// \brief Specify under what conditions this check can be recovered
|
/// \brief Specify under what conditions this check can be recovered
|
||||||
enum class CheckRecoverableKind {
|
enum class CheckRecoverableKind {
|
||||||
/// Always terminate program execution if this check fails
|
/// Always terminate program execution if this check fails.
|
||||||
Unrecoverable,
|
Unrecoverable,
|
||||||
/// Check supports recovering, allows user to specify which
|
/// Check supports recovering, runtime has both fatal (noreturn) and
|
||||||
|
/// non-fatal handlers for this check.
|
||||||
Recoverable,
|
Recoverable,
|
||||||
/// Runtime conditionally aborts, always need to support recovery.
|
/// Runtime conditionally aborts, always need to support recovery.
|
||||||
AlwaysRecoverable
|
AlwaysRecoverable
|
||||||
|
@ -2232,42 +2233,95 @@ static CheckRecoverableKind getRecoverableKind(SanitizerKind Kind) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void emitCheckHandlerCall(CodeGenFunction &CGF,
|
||||||
|
llvm::FunctionType *FnType,
|
||||||
|
ArrayRef<llvm::Value *> FnArgs,
|
||||||
|
StringRef CheckName,
|
||||||
|
CheckRecoverableKind RecoverKind, bool IsFatal,
|
||||||
|
llvm::BasicBlock *ContBB) {
|
||||||
|
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
|
||||||
|
bool NeedsAbortSuffix =
|
||||||
|
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
|
||||||
|
std::string FnName = ("__ubsan_handle_" + CheckName +
|
||||||
|
(NeedsAbortSuffix ? "_abort" : "")).str();
|
||||||
|
bool MayReturn =
|
||||||
|
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
|
||||||
|
|
||||||
|
llvm::AttrBuilder B;
|
||||||
|
if (!MayReturn) {
|
||||||
|
B.addAttribute(llvm::Attribute::NoReturn)
|
||||||
|
.addAttribute(llvm::Attribute::NoUnwind);
|
||||||
|
}
|
||||||
|
B.addAttribute(llvm::Attribute::UWTable);
|
||||||
|
|
||||||
|
llvm::Value *Fn = CGF.CGM.CreateRuntimeFunction(
|
||||||
|
FnType, FnName,
|
||||||
|
llvm::AttributeSet::get(CGF.getLLVMContext(),
|
||||||
|
llvm::AttributeSet::FunctionIndex, B));
|
||||||
|
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
|
||||||
|
if (!MayReturn) {
|
||||||
|
HandlerCall->setDoesNotReturn();
|
||||||
|
CGF.Builder.CreateUnreachable();
|
||||||
|
} else {
|
||||||
|
CGF.Builder.CreateBr(ContBB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CodeGenFunction::EmitCheck(
|
void CodeGenFunction::EmitCheck(
|
||||||
ArrayRef<std::pair<llvm::Value *, SanitizerKind>> Checked,
|
ArrayRef<std::pair<llvm::Value *, SanitizerKind>> Checked,
|
||||||
StringRef CheckName, ArrayRef<llvm::Constant *> StaticArgs,
|
StringRef CheckName, ArrayRef<llvm::Constant *> StaticArgs,
|
||||||
ArrayRef<llvm::Value *> DynamicArgs) {
|
ArrayRef<llvm::Value *> DynamicArgs) {
|
||||||
assert(IsSanitizerScope);
|
assert(IsSanitizerScope);
|
||||||
assert(Checked.size() > 0);
|
assert(Checked.size() > 0);
|
||||||
llvm::Value *Cond = Checked[0].first;
|
|
||||||
|
llvm::Value *FatalCond = nullptr;
|
||||||
|
llvm::Value *RecoverableCond = nullptr;
|
||||||
|
for (int i = 0, n = Checked.size(); i < n; ++i) {
|
||||||
|
llvm::Value *Check = Checked[i].first;
|
||||||
|
llvm::Value *&Cond =
|
||||||
|
CGM.getCodeGenOpts().SanitizeRecover.has(Checked[i].second)
|
||||||
|
? RecoverableCond
|
||||||
|
: FatalCond;
|
||||||
|
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *JointCond;
|
||||||
|
if (FatalCond && RecoverableCond)
|
||||||
|
JointCond = Builder.CreateAnd(FatalCond, RecoverableCond);
|
||||||
|
else
|
||||||
|
JointCond = FatalCond ? FatalCond : RecoverableCond;
|
||||||
|
assert(JointCond);
|
||||||
|
|
||||||
CheckRecoverableKind RecoverKind = getRecoverableKind(Checked[0].second);
|
CheckRecoverableKind RecoverKind = getRecoverableKind(Checked[0].second);
|
||||||
assert(SanOpts.has(Checked[0].second));
|
assert(SanOpts.has(Checked[0].second));
|
||||||
|
#ifndef NDEBUG
|
||||||
for (int i = 1, n = Checked.size(); i < n; ++i) {
|
for (int i = 1, n = Checked.size(); i < n; ++i) {
|
||||||
Cond = Builder.CreateAnd(Cond, Checked[i].first);
|
|
||||||
assert(RecoverKind == getRecoverableKind(Checked[i].second) &&
|
assert(RecoverKind == getRecoverableKind(Checked[i].second) &&
|
||||||
"All recoverable kinds in a single check must be same!");
|
"All recoverable kinds in a single check must be same!");
|
||||||
assert(SanOpts.has(Checked[i].second));
|
assert(SanOpts.has(Checked[i].second));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (CGM.getCodeGenOpts().SanitizeUndefinedTrapOnError) {
|
if (CGM.getCodeGenOpts().SanitizeUndefinedTrapOnError) {
|
||||||
assert (RecoverKind != CheckRecoverableKind::AlwaysRecoverable &&
|
assert(RecoverKind != CheckRecoverableKind::AlwaysRecoverable &&
|
||||||
"Runtime call required for AlwaysRecoverable kind!");
|
"Runtime call required for AlwaysRecoverable kind!");
|
||||||
return EmitTrapCheck(Cond);
|
// Assume that -fsanitize-undefined-trap-on-error overrides
|
||||||
|
// -fsanitize-recover= options, as we can only print meaningful error
|
||||||
|
// message and recover if we have a runtime support.
|
||||||
|
return EmitTrapCheck(JointCond);
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::BasicBlock *Cont = createBasicBlock("cont");
|
llvm::BasicBlock *Cont = createBasicBlock("cont");
|
||||||
|
llvm::BasicBlock *Handlers = createBasicBlock("handler." + CheckName);
|
||||||
llvm::BasicBlock *Handler = createBasicBlock("handler." + CheckName);
|
llvm::Instruction *Branch = Builder.CreateCondBr(JointCond, Cont, Handlers);
|
||||||
|
|
||||||
llvm::Instruction *Branch = Builder.CreateCondBr(Cond, Cont, Handler);
|
|
||||||
|
|
||||||
// Give hint that we very much don't expect to execute the handler
|
// Give hint that we very much don't expect to execute the handler
|
||||||
// Value chosen to match UR_NONTAKEN_WEIGHT, see BranchProbabilityInfo.cpp
|
// Value chosen to match UR_NONTAKEN_WEIGHT, see BranchProbabilityInfo.cpp
|
||||||
llvm::MDBuilder MDHelper(getLLVMContext());
|
llvm::MDBuilder MDHelper(getLLVMContext());
|
||||||
llvm::MDNode *Node = MDHelper.createBranchWeights((1U << 20) - 1, 1);
|
llvm::MDNode *Node = MDHelper.createBranchWeights((1U << 20) - 1, 1);
|
||||||
Branch->setMetadata(llvm::LLVMContext::MD_prof, Node);
|
Branch->setMetadata(llvm::LLVMContext::MD_prof, Node);
|
||||||
|
EmitBlock(Handlers);
|
||||||
|
|
||||||
EmitBlock(Handler);
|
// Emit handler arguments and create handler function type.
|
||||||
|
|
||||||
llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
|
llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
|
||||||
auto *InfoPtr =
|
auto *InfoPtr =
|
||||||
new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
|
new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
|
||||||
|
@ -2290,34 +2344,27 @@ void CodeGenFunction::EmitCheck(
|
||||||
ArgTypes.push_back(IntPtrTy);
|
ArgTypes.push_back(IntPtrTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Recover = RecoverKind == CheckRecoverableKind::AlwaysRecoverable ||
|
|
||||||
(RecoverKind == CheckRecoverableKind::Recoverable &&
|
|
||||||
CGM.getCodeGenOpts().SanitizeRecover);
|
|
||||||
|
|
||||||
llvm::FunctionType *FnType =
|
llvm::FunctionType *FnType =
|
||||||
llvm::FunctionType::get(CGM.VoidTy, ArgTypes, false);
|
llvm::FunctionType::get(CGM.VoidTy, ArgTypes, false);
|
||||||
llvm::AttrBuilder B;
|
|
||||||
if (!Recover) {
|
|
||||||
B.addAttribute(llvm::Attribute::NoReturn)
|
|
||||||
.addAttribute(llvm::Attribute::NoUnwind);
|
|
||||||
}
|
|
||||||
B.addAttribute(llvm::Attribute::UWTable);
|
|
||||||
|
|
||||||
// Checks that have two variants use a suffix to differentiate them
|
if (!FatalCond || !RecoverableCond) {
|
||||||
bool NeedsAbortSuffix = RecoverKind != CheckRecoverableKind::Unrecoverable &&
|
// Simple case: we need to generate a single handler call, either
|
||||||
!CGM.getCodeGenOpts().SanitizeRecover;
|
// fatal, or non-fatal.
|
||||||
std::string FunctionName = ("__ubsan_handle_" + CheckName +
|
emitCheckHandlerCall(*this, FnType, Args, CheckName, RecoverKind,
|
||||||
(NeedsAbortSuffix? "_abort" : "")).str();
|
(FatalCond != nullptr), Cont);
|
||||||
llvm::Value *Fn = CGM.CreateRuntimeFunction(
|
|
||||||
FnType, FunctionName,
|
|
||||||
llvm::AttributeSet::get(getLLVMContext(),
|
|
||||||
llvm::AttributeSet::FunctionIndex, B));
|
|
||||||
llvm::CallInst *HandlerCall = EmitNounwindRuntimeCall(Fn, Args);
|
|
||||||
if (Recover) {
|
|
||||||
Builder.CreateBr(Cont);
|
|
||||||
} else {
|
} else {
|
||||||
HandlerCall->setDoesNotReturn();
|
// Emit two handler calls: first one for set of unrecoverable checks,
|
||||||
Builder.CreateUnreachable();
|
// another one for recoverable.
|
||||||
|
llvm::BasicBlock *NonFatalHandlerBB =
|
||||||
|
createBasicBlock("non_fatal." + CheckName);
|
||||||
|
llvm::BasicBlock *FatalHandlerBB = createBasicBlock("fatal." + CheckName);
|
||||||
|
Builder.CreateCondBr(FatalCond, NonFatalHandlerBB, FatalHandlerBB);
|
||||||
|
EmitBlock(FatalHandlerBB);
|
||||||
|
emitCheckHandlerCall(*this, FnType, Args, CheckName, RecoverKind, true,
|
||||||
|
NonFatalHandlerBB);
|
||||||
|
EmitBlock(NonFatalHandlerBB);
|
||||||
|
emitCheckHandlerCall(*this, FnType, Args, CheckName, RecoverKind, false,
|
||||||
|
Cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitBlock(Cont);
|
EmitBlock(Cont);
|
||||||
|
|
|
@ -44,7 +44,10 @@ ID = ALIAS, ID##Group = 1 << SO_##ID##Group,
|
||||||
NotAllowedWithTrap = Vptr,
|
NotAllowedWithTrap = Vptr,
|
||||||
RequiresPIE = Memory | DataFlow,
|
RequiresPIE = Memory | DataFlow,
|
||||||
NeedsUnwindTables = Address | Thread | Memory | DataFlow,
|
NeedsUnwindTables = Address | Thread | Memory | DataFlow,
|
||||||
SupportsCoverage = Address | Memory | Leak | Undefined | Integer
|
SupportsCoverage = Address | Memory | Leak | Undefined | Integer,
|
||||||
|
RecoverableByDefault = Undefined | Integer,
|
||||||
|
Unrecoverable = Address | Unreachable | Return,
|
||||||
|
LegacyFsanitizeRecoverMask = Undefined | Integer
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +148,7 @@ bool SanitizerArgs::needsUnwindTables() const {
|
||||||
|
|
||||||
void SanitizerArgs::clear() {
|
void SanitizerArgs::clear() {
|
||||||
Sanitizers.clear();
|
Sanitizers.clear();
|
||||||
SanitizeRecover = false;
|
RecoverableSanitizers.clear();
|
||||||
BlacklistFile = "";
|
BlacklistFile = "";
|
||||||
SanitizeCoverage = 0;
|
SanitizeCoverage = 0;
|
||||||
MsanTrackOrigins = 0;
|
MsanTrackOrigins = 0;
|
||||||
|
@ -204,8 +207,40 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
|
||||||
}
|
}
|
||||||
addAllOf(Sanitizers, Kinds);
|
addAllOf(Sanitizers, Kinds);
|
||||||
|
|
||||||
SanitizeRecover = Args.hasFlag(options::OPT_fsanitize_recover,
|
// Parse -f(no-)?sanitize-recover flags.
|
||||||
options::OPT_fno_sanitize_recover, true);
|
unsigned RecoverableKinds = RecoverableByDefault;
|
||||||
|
unsigned DiagnosedUnrecoverableKinds = 0;
|
||||||
|
for (const auto *Arg : Args) {
|
||||||
|
if (Arg->getOption().matches(options::OPT_fsanitize_recover)) {
|
||||||
|
// FIXME: Add deprecation notice, and then remove this flag.
|
||||||
|
RecoverableKinds |= expandGroups(LegacyFsanitizeRecoverMask);
|
||||||
|
Arg->claim();
|
||||||
|
} else if (Arg->getOption().matches(options::OPT_fno_sanitize_recover)) {
|
||||||
|
// FIXME: Add deprecation notice, and then remove this flag.
|
||||||
|
RecoverableKinds &= ~expandGroups(LegacyFsanitizeRecoverMask);
|
||||||
|
Arg->claim();
|
||||||
|
} else if (Arg->getOption().matches(options::OPT_fsanitize_recover_EQ)) {
|
||||||
|
unsigned Add = parseArgValues(D, Arg, true);
|
||||||
|
// Report error if user explicitly tries to recover from unrecoverable
|
||||||
|
// sanitizer.
|
||||||
|
if (unsigned KindsToDiagnose =
|
||||||
|
Add & Unrecoverable & ~DiagnosedUnrecoverableKinds) {
|
||||||
|
SanitizerSet SetToDiagnose;
|
||||||
|
addAllOf(SetToDiagnose, KindsToDiagnose);
|
||||||
|
D.Diag(diag::err_drv_unsupported_option_argument)
|
||||||
|
<< Arg->getOption().getName() << toString(SetToDiagnose);
|
||||||
|
DiagnosedUnrecoverableKinds |= KindsToDiagnose;
|
||||||
|
}
|
||||||
|
RecoverableKinds |= expandGroups(Add);
|
||||||
|
Arg->claim();
|
||||||
|
} else if (Arg->getOption().matches(options::OPT_fno_sanitize_recover_EQ)) {
|
||||||
|
RecoverableKinds &= ~expandGroups(parseArgValues(D, Arg, true));
|
||||||
|
Arg->claim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RecoverableKinds &= Kinds;
|
||||||
|
RecoverableKinds &= ~Unrecoverable;
|
||||||
|
addAllOf(RecoverableSanitizers, RecoverableKinds);
|
||||||
|
|
||||||
UbsanTrapOnError =
|
UbsanTrapOnError =
|
||||||
Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error,
|
Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error,
|
||||||
|
@ -361,8 +396,9 @@ void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args,
|
||||||
return;
|
return;
|
||||||
CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers)));
|
CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers)));
|
||||||
|
|
||||||
if (!SanitizeRecover)
|
if (!RecoverableSanitizers.empty())
|
||||||
CmdArgs.push_back("-fno-sanitize-recover");
|
CmdArgs.push_back(Args.MakeArgString("-fsanitize-recover=" +
|
||||||
|
toString(RecoverableSanitizers)));
|
||||||
|
|
||||||
if (UbsanTrapOnError)
|
if (UbsanTrapOnError)
|
||||||
CmdArgs.push_back("-fsanitize-undefined-trap-on-error");
|
CmdArgs.push_back("-fsanitize-undefined-trap-on-error");
|
||||||
|
@ -426,7 +462,9 @@ unsigned expandGroups(unsigned Kinds) {
|
||||||
unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A,
|
unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A,
|
||||||
bool DiagnoseErrors) {
|
bool DiagnoseErrors) {
|
||||||
assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
|
assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
|
||||||
A->getOption().matches(options::OPT_fno_sanitize_EQ)) &&
|
A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
|
||||||
|
A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
|
||||||
|
A->getOption().matches(options::OPT_fno_sanitize_recover_EQ)) &&
|
||||||
"Invalid argument in parseArgValues!");
|
"Invalid argument in parseArgValues!");
|
||||||
unsigned Kinds = 0;
|
unsigned Kinds = 0;
|
||||||
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
|
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
|
||||||
|
|
|
@ -325,6 +325,21 @@ GenerateOptimizationRemarkRegex(DiagnosticsEngine &Diags, ArgList &Args,
|
||||||
return Pattern;
|
return Pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parseSanitizerKinds(StringRef FlagName,
|
||||||
|
const std::vector<std::string> &Sanitizers,
|
||||||
|
DiagnosticsEngine &Diags, SanitizerSet &S) {
|
||||||
|
for (const auto &Sanitizer : Sanitizers) {
|
||||||
|
SanitizerKind K = llvm::StringSwitch<SanitizerKind>(Sanitizer)
|
||||||
|
#define SANITIZER(NAME, ID) .Case(NAME, SanitizerKind::ID)
|
||||||
|
#include "clang/Basic/Sanitizers.def"
|
||||||
|
.Default(SanitizerKind::Unknown);
|
||||||
|
if (K == SanitizerKind::Unknown)
|
||||||
|
Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer;
|
||||||
|
else
|
||||||
|
S.set(K, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
||||||
DiagnosticsEngine &Diags,
|
DiagnosticsEngine &Diags,
|
||||||
const TargetOptions &TargetOpts) {
|
const TargetOptions &TargetOpts) {
|
||||||
|
@ -465,7 +480,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
||||||
|
|
||||||
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
|
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
|
||||||
Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier);
|
Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier);
|
||||||
Opts.SanitizeRecover = !Args.hasArg(OPT_fno_sanitize_recover);
|
|
||||||
|
|
||||||
Opts.DisableGCov = Args.hasArg(OPT_test_coverage);
|
Opts.DisableGCov = Args.hasArg(OPT_test_coverage);
|
||||||
Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data);
|
Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data);
|
||||||
|
@ -596,6 +610,12 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
||||||
|
|
||||||
Opts.RewriteMapFiles = Args.getAllArgValues(OPT_frewrite_map_file);
|
Opts.RewriteMapFiles = Args.getAllArgValues(OPT_frewrite_map_file);
|
||||||
|
|
||||||
|
// Parse -fsanitize-recover= arguments.
|
||||||
|
// FIXME: Report unrecoverable sanitizers incorrectly specified here.
|
||||||
|
parseSanitizerKinds("-fsanitize-recover=",
|
||||||
|
Args.getAllArgValues(OPT_fsanitize_recover_EQ), Diags,
|
||||||
|
Opts.SanitizeRecover);
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1635,18 +1655,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse -fsanitize= arguments.
|
// Parse -fsanitize= arguments.
|
||||||
std::vector<std::string> Sanitizers = Args.getAllArgValues(OPT_fsanitize_EQ);
|
parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
|
||||||
for (const auto &Sanitizer : Sanitizers) {
|
Diags, Opts.Sanitize);
|
||||||
SanitizerKind K = llvm::StringSwitch<SanitizerKind>(Sanitizer)
|
|
||||||
#define SANITIZER(NAME, ID) .Case(NAME, SanitizerKind::ID)
|
|
||||||
#include "clang/Basic/Sanitizers.def"
|
|
||||||
.Default(SanitizerKind::Unknown);
|
|
||||||
if (K == SanitizerKind::Unknown)
|
|
||||||
Diags.Report(diag::err_drv_invalid_value)
|
|
||||||
<< "-fsanitize=" << Sanitizer;
|
|
||||||
else
|
|
||||||
Opts.Sanitize.set(K, true);
|
|
||||||
}
|
|
||||||
// -fsanitize-address-field-padding=N has to be a LangOpt, parse it here.
|
// -fsanitize-address-field-padding=N has to be a LangOpt, parse it here.
|
||||||
Opts.SanitizeAddressFieldPadding =
|
Opts.SanitizeAddressFieldPadding =
|
||||||
getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags);
|
getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-UBSAN
|
// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -fsanitize-recover=alignment,null,object-size,shift,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-UBSAN
|
||||||
// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-TRAP
|
// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -fsanitize-recover=alignment,null,object-size,shift,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-TRAP
|
||||||
// RUN: %clang_cc1 -fsanitize=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL
|
// RUN: %clang_cc1 -fsanitize=null -fsanitize-recover=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL
|
||||||
// RUN: %clang_cc1 -fsanitize=signed-integer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-OVERFLOW
|
// RUN: %clang_cc1 -fsanitize=signed-integer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-OVERFLOW
|
||||||
|
|
||||||
// CHECK-UBSAN: @[[INT:.*]] = private unnamed_addr constant { i16, i16, [6 x i8] } { i16 0, i16 11, [6 x i8] c"'int'\00" }
|
// CHECK-UBSAN: @[[INT:.*]] = private unnamed_addr constant { i16, i16, [6 x i8] } { i16 0, i16 11, [6 x i8] c"'int'\00" }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Verify proper type emitted for compound assignments
|
// Verify proper type emitted for compound assignments
|
||||||
// RUN: %clang_cc1 -ffreestanding -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=signed-integer-overflow,unsigned-integer-overflow | FileCheck %s
|
// RUN: %clang_cc1 -ffreestanding -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-recover=signed-integer-overflow,unsigned-integer-overflow | FileCheck %s
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=unsigned-integer-overflow %s -emit-llvm -o - | FileCheck %s --check-prefix=RECOVER
|
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=unsigned-integer-overflow -fsanitize-recover=unsigned-integer-overflow %s -emit-llvm -o - | FileCheck %s --check-prefix=RECOVER
|
||||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -emit-llvm -o - | FileCheck %s --check-prefix=ABORT
|
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=unsigned-integer-overflow %s -emit-llvm -o - | FileCheck %s --check-prefix=ABORT
|
||||||
|
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=null,object-size,alignment -fsanitize-recover=object-size %s -emit-llvm -o - | FileCheck %s --check-prefix=PARTIAL
|
||||||
|
|
||||||
// RECOVER: @test
|
// RECOVER: @test
|
||||||
// ABORT: @test
|
// ABORT: @test
|
||||||
|
@ -15,3 +15,25 @@ void test() {
|
||||||
// ABORT: unreachable
|
// ABORT: unreachable
|
||||||
x = y + z;
|
x = y + z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void foo() {
|
||||||
|
union { int i; } u;
|
||||||
|
u.i=1;
|
||||||
|
// PARTIAL: %[[CHECK0:.*]] = icmp ne {{.*}}* %[[PTR:.*]], null
|
||||||
|
|
||||||
|
// PARTIAL: %[[SIZE:.*]] = call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false)
|
||||||
|
// PARTIAL-NEXT: %[[CHECK1:.*]] = icmp uge i64 %[[SIZE]], 4
|
||||||
|
|
||||||
|
// PARTIAL: %[[MISALIGN:.*]] = and i64 {{.*}}, 3
|
||||||
|
// PARTIAL-NEXT: %[[CHECK2:.*]] = icmp eq i64 %[[MISALIGN]], 0
|
||||||
|
|
||||||
|
// PARTIAL: %[[CHECK02:.*]] = and i1 %[[CHECK0]], %[[CHECK2]]
|
||||||
|
// PARTIAL-NEXT: %[[CHECK012:.*]] = and i1 %[[CHECK02]], %[[CHECK1]]
|
||||||
|
|
||||||
|
// PARTIAL: br i1 %[[CHECK012]], {{.*}} !prof ![[WEIGHT_MD:.*]], !nosanitize
|
||||||
|
|
||||||
|
// PARTIAL: br i1 %[[CHECK02]], {{.*}}
|
||||||
|
// PARTIAL: call void @__ubsan_handle_type_mismatch_abort(
|
||||||
|
// PARTIAL-NEXT: unreachable
|
||||||
|
// PARTIAL: call void @__ubsan_handle_type_mismatch(
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Verify ubsan vptr does not check down-casts on blacklisted types.
|
// Verify ubsan vptr does not check down-casts on blacklisted types.
|
||||||
// RUN: echo "type:_ZTI3Foo" > %t-type.blacklist
|
// RUN: echo "type:_ZTI3Foo" > %t-type.blacklist
|
||||||
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -emit-llvm %s -o - | FileCheck %s --check-prefix=DEFAULT
|
// 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-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=%t-type.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=TYPE
|
||||||
|
|
||||||
class Bar {
|
class Bar {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
// RUN: %clang_cc1 -std=c++11 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsanitize=vptr,address -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-ASAN
|
// RUN: %clang_cc1 -std=c++11 -fsanitize=vptr,address -fsanitize-recover=vptr,address -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-ASAN
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsanitize=vptr -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=DOWNCAST-NULL
|
// RUN: %clang_cc1 -std=c++11 -fsanitize=vptr -fsanitize-recover=vptr -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=DOWNCAST-NULL
|
||||||
|
|
||||||
struct S {
|
struct S {
|
||||||
double d;
|
double d;
|
||||||
|
|
|
@ -134,11 +134,20 @@
|
||||||
|
|
||||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
||||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
||||||
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover=all -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
||||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER
|
||||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover -fsanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=thread -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER
|
||||||
// RUZ: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover -fno-sanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover -fsanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER
|
||||||
// CHECK-RECOVER-NOT: sanitize-recover
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover -fno-sanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER
|
||||||
// CHECK-NO-RECOVER: "-fno-sanitize-recover"
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=object-size -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-RECOVER
|
||||||
|
|
||||||
|
// CHECK-RECOVER: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){16}"}}
|
||||||
|
// CHECK-NO-RECOVER-NOT: sanitize-recover
|
||||||
|
// CHECK-PARTIAL-RECOVER: "-fsanitize-recover=object-size"
|
||||||
|
|
||||||
|
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover=address,foobar,object-size,unreachable -### 2>&1 | FileCheck %s --check-prefix=CHECK-DIAG-RECOVER
|
||||||
|
// CHECK-DIAG-RECOVER: unsupported argument 'foobar' to option 'fsanitize-recover='
|
||||||
|
// CHECK-DIAG-RECOVER: unsupported argument 'address,unreachable' to option 'fsanitize-recover='
|
||||||
|
|
||||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANL
|
// RUN: %clang -target x86_64-linux-gnu -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANL
|
||||||
// CHECK-SANL: "-fsanitize=leak"
|
// CHECK-SANL: "-fsanitize=leak"
|
||||||
|
|
Loading…
Reference in New Issue