diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst index 9d8e978ed3695ce866208bbd69136bf98cf0d22c..fc5af4ecd5b1a9074dbdb37bcd17fe1717a9e553 100644 --- a/docs/UsersManual.rst +++ b/docs/UsersManual.rst @@ -1033,10 +1033,6 @@ are listed below. 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 rather than calls to runtime libraries when a problem is detected. 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 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 Don't assume that the C++'s new operator is sane. diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 48e29839afe7d343d55fd4a4e6f026bf7477c68d..30733f1e9144a64401effbd2f349f13d185a842b 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -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=">, Group<f_clang_Group>, Flags<[CC1Option]>, HelpText<"Level of field padding for AddressSanitizer">; -def fsanitize_recover : Flag<["-"], "fsanitize-recover">, - Group<f_clang_Group>; +def fsanitize_recover : Flag<["-"], "fsanitize-recover">, Group<f_clang_Group>; def fno_sanitize_recover : Flag<["-"], "fno-sanitize-recover">, - Group<f_clang_Group>, Flags<[CC1Option]>, - HelpText<"Disable sanitizer check recovery">; + Group<f_clang_Group>; +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">, Group<f_clang_Group>, Flags<[CC1Option]>; def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">, diff --git a/include/clang/Driver/SanitizerArgs.h b/include/clang/Driver/SanitizerArgs.h index 0b165b45d197ff9f9602f4c4a367bd868e5350c9..3524da0e7ffea3dc45ad32c2db4a6dcec5688e33 100644 --- a/include/clang/Driver/SanitizerArgs.h +++ b/include/clang/Driver/SanitizerArgs.h @@ -22,7 +22,7 @@ class ToolChain; class SanitizerArgs { SanitizerSet Sanitizers; - bool SanitizeRecover; + SanitizerSet RecoverableSanitizers; std::string BlacklistFile; int SanitizeCoverage; diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index 15b4a5a7937c46b8ea2208be0dac5f8697c1ffe2..b44672d0f5f0a2a2dab3cabfcbe7c760a1c012d3 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -160,9 +160,6 @@ ENUM_CODEGENOPT(Inlining, InliningMethod, 2, NoInlining) /// The default TLS model to use. ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel) -CODEGENOPT(SanitizeRecover, 1, 1) ///< Attempt to recover from sanitizer checks - ///< by continuing execution when possible - #undef CODEGENOPT #undef ENUM_CODEGENOPT #undef VALUE_CODEGENOPT diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index 19cc6c11c5a0a21d4e3acb240678e56b808c093b..849b606377e1b44dfceeb237dbc6c85591902f96 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -17,6 +17,7 @@ #include <memory> #include <string> #include <vector> +#include "clang/Basic/Sanitizers.h" #include "llvm/Support/Regex.h" namespace clang { @@ -179,6 +180,10 @@ public: /// Set of files definining the rules for the symbol rewriting. std::vector<std::string> RewriteMapFiles; + /// Set of sanitizer checks that are non-fatal (i.e. execution should be + /// continued when possible). + SanitizerSet SanitizeRecover; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index f8136ba0620eb930a70ca05790de338e09ecbf74..ebcbcddce173705d6b6f69f03f2d57b1f26165f3 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -2211,9 +2211,10 @@ llvm::Constant *CodeGenFunction::EmitCheckSourceLocation(SourceLocation Loc) { namespace { /// \brief Specify under what conditions this check can be recovered enum class CheckRecoverableKind { - /// Always terminate program execution if this check fails + /// Always terminate program execution if this check fails. 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, /// Runtime conditionally aborts, always need to support recovery. 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( ArrayRef<std::pair<llvm::Value *, SanitizerKind>> Checked, StringRef CheckName, ArrayRef<llvm::Constant *> StaticArgs, ArrayRef<llvm::Value *> DynamicArgs) { assert(IsSanitizerScope); 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); assert(SanOpts.has(Checked[0].second)); +#ifndef NDEBUG for (int i = 1, n = Checked.size(); i < n; ++i) { - Cond = Builder.CreateAnd(Cond, Checked[i].first); assert(RecoverKind == getRecoverableKind(Checked[i].second) && "All recoverable kinds in a single check must be same!"); assert(SanOpts.has(Checked[i].second)); } +#endif if (CGM.getCodeGenOpts().SanitizeUndefinedTrapOnError) { - assert (RecoverKind != CheckRecoverableKind::AlwaysRecoverable && - "Runtime call required for AlwaysRecoverable kind!"); - return EmitTrapCheck(Cond); + assert(RecoverKind != CheckRecoverableKind::AlwaysRecoverable && + "Runtime call required for AlwaysRecoverable kind!"); + // 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 *Handler = createBasicBlock("handler." + CheckName); - - llvm::Instruction *Branch = Builder.CreateCondBr(Cond, Cont, Handler); - + llvm::BasicBlock *Handlers = createBasicBlock("handler." + CheckName); + llvm::Instruction *Branch = Builder.CreateCondBr(JointCond, Cont, Handlers); // Give hint that we very much don't expect to execute the handler // Value chosen to match UR_NONTAKEN_WEIGHT, see BranchProbabilityInfo.cpp llvm::MDBuilder MDHelper(getLLVMContext()); llvm::MDNode *Node = MDHelper.createBranchWeights((1U << 20) - 1, 1); 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); auto *InfoPtr = new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false, @@ -2290,34 +2344,27 @@ void CodeGenFunction::EmitCheck( ArgTypes.push_back(IntPtrTy); } - bool Recover = RecoverKind == CheckRecoverableKind::AlwaysRecoverable || - (RecoverKind == CheckRecoverableKind::Recoverable && - CGM.getCodeGenOpts().SanitizeRecover); - llvm::FunctionType *FnType = 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 - bool NeedsAbortSuffix = RecoverKind != CheckRecoverableKind::Unrecoverable && - !CGM.getCodeGenOpts().SanitizeRecover; - std::string FunctionName = ("__ubsan_handle_" + CheckName + - (NeedsAbortSuffix? "_abort" : "")).str(); - 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); + if (!FatalCond || !RecoverableCond) { + // Simple case: we need to generate a single handler call, either + // fatal, or non-fatal. + emitCheckHandlerCall(*this, FnType, Args, CheckName, RecoverKind, + (FatalCond != nullptr), Cont); } else { - HandlerCall->setDoesNotReturn(); - Builder.CreateUnreachable(); + // Emit two handler calls: first one for set of unrecoverable checks, + // 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); diff --git a/lib/Driver/SanitizerArgs.cpp b/lib/Driver/SanitizerArgs.cpp index 09c2cb05f4f29b9bd83cc36bda8f529cc635e759..bd7bc218e3bf1182de0049cdc47dd2067ff4743a 100644 --- a/lib/Driver/SanitizerArgs.cpp +++ b/lib/Driver/SanitizerArgs.cpp @@ -44,7 +44,10 @@ ID = ALIAS, ID##Group = 1 << SO_##ID##Group, NotAllowedWithTrap = Vptr, RequiresPIE = 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() { Sanitizers.clear(); - SanitizeRecover = false; + RecoverableSanitizers.clear(); BlacklistFile = ""; SanitizeCoverage = 0; MsanTrackOrigins = 0; @@ -204,8 +207,40 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, } addAllOf(Sanitizers, Kinds); - SanitizeRecover = Args.hasFlag(options::OPT_fsanitize_recover, - options::OPT_fno_sanitize_recover, true); + // Parse -f(no-)?sanitize-recover flags. + 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 = Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, @@ -361,8 +396,9 @@ void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, return; CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers))); - if (!SanitizeRecover) - CmdArgs.push_back("-fno-sanitize-recover"); + if (!RecoverableSanitizers.empty()) + CmdArgs.push_back(Args.MakeArgString("-fsanitize-recover=" + + toString(RecoverableSanitizers))); if (UbsanTrapOnError) 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, bool DiagnoseErrors) { 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!"); unsigned Kinds = 0; for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d630b5bf5c4a81dd6dbb5a23c19e5ec2422e3619..0f6f01d0a0821a4102875752ed0708f2389d6ccb 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -325,6 +325,21 @@ GenerateOptimizationRemarkRegex(DiagnosticsEngine &Diags, ArgList &Args, 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, DiagnosticsEngine &Diags, 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.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); - Opts.SanitizeRecover = !Args.hasArg(OPT_fno_sanitize_recover); Opts.DisableGCov = Args.hasArg(OPT_test_coverage); 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); + // 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; } @@ -1635,18 +1655,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, } // Parse -fsanitize= arguments. - std::vector<std::string> Sanitizers = Args.getAllArgValues(OPT_fsanitize_EQ); - 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) - << "-fsanitize=" << Sanitizer; - else - Opts.Sanitize.set(K, true); - } + parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), + Diags, Opts.Sanitize); // -fsanitize-address-field-padding=N has to be a LangOpt, parse it here. Opts.SanitizeAddressFieldPadding = getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags); diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c index 385ec1c85208cef4598e3e9b221f475d7abdcdff..c41b37c39bc8edd0b68fc10119ec596df9c94859 100644 --- a/test/CodeGen/catch-undef-behavior.c +++ b/test/CodeGen/catch-undef-behavior.c @@ -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-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=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL +// 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 -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 -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 // CHECK-UBSAN: @[[INT:.*]] = private unnamed_addr constant { i16, i16, [6 x i8] } { i16 0, i16 11, [6 x i8] c"'int'\00" } diff --git a/test/CodeGen/compound-assign-overflow.c b/test/CodeGen/compound-assign-overflow.c index 15334294d47e7942361532e3bf9b34b5618c936b..f126bb05d53c1e5f76239d033d4c842b7d8aa580 100644 --- a/test/CodeGen/compound-assign-overflow.c +++ b/test/CodeGen/compound-assign-overflow.c @@ -1,5 +1,5 @@ // 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> diff --git a/test/CodeGen/sanitize-recover.c b/test/CodeGen/sanitize-recover.c index 3c9c89553813d0a52abefe461b81f44d86160b71..b263f51631813957e9f49e3f48565f874a85ab18 100644 --- a/test/CodeGen/sanitize-recover.c +++ b/test/CodeGen/sanitize-recover.c @@ -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 -fno-sanitize-recover %s -emit-llvm -o - | FileCheck %s --check-prefix=ABORT - +// 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 %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 // ABORT: @test @@ -15,3 +15,25 @@ void test() { // ABORT: unreachable 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( +} diff --git a/test/CodeGen/ubsan-type-blacklist.cpp b/test/CodeGen/ubsan-type-blacklist.cpp index e2bbb6a803a6a73d56a35374b57a06ae48e55fdb..b3137e7806c76bcc4163a290480bd2a7328693e8 100644 --- a/test/CodeGen/ubsan-type-blacklist.cpp +++ b/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 -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 -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 class Bar { public: diff --git a/test/CodeGenCXX/catch-undef-behavior.cpp b/test/CodeGenCXX/catch-undef-behavior.cpp index ca174a2c635af7854549f7f66092dc11e23917c0..4120d0fead4da5befe30fe83504841de2aaf2f29 100644 --- a/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/test/CodeGenCXX/catch-undef-behavior.cpp @@ -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=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=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 -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 -fsanitize-recover=vptr -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=DOWNCAST-NULL struct S { double d; diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index 7ec8229bfd9d8b0f1f81b1bdde0c612f690db865..1988503f677cef012ae534e27418faa17a00e77d 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -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 -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 -fsanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-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 -// CHECK-RECOVER-NOT: sanitize-recover -// CHECK-NO-RECOVER: "-fno-sanitize-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 +// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover -fsanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-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 +// 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 // CHECK-SANL: "-fsanitize=leak"