diff options
author | Alexey Samsonov <vonosmas@gmail.com> | 2015-01-12 22:39:12 +0000 |
---|---|---|
committer | Alexey Samsonov <vonosmas@gmail.com> | 2015-01-12 22:39:12 +0000 |
commit | 8845952b54a7574cedbbd0661e889cd4e2d5947b (patch) | |
tree | cac9ea08214180cb195bc54409842254053fa5f4 /clang/lib/CodeGen/CGExpr.cpp | |
parent | d88ab87064d9b366393d58066f3bee8111f6cec4 (diff) | |
download | bcm5719-llvm-8845952b54a7574cedbbd0661e889cd4e2d5947b.tar.gz bcm5719-llvm-8845952b54a7574cedbbd0661e889cd4e2d5947b.zip |
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
Diffstat (limited to 'clang/lib/CodeGen/CGExpr.cpp')
-rw-r--r-- | clang/lib/CodeGen/CGExpr.cpp | 123 |
1 files changed, 85 insertions, 38 deletions
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index f8136ba0620..ebcbcddce17 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/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); |