From ad95481b2769ad61f23afc5e2c31f11c66090657 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Sun, 2 Dec 2012 19:50:33 +0000 Subject: [PATCH] [ubsan] Add flag to enable recovery from checks when possible. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@169114 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/CC1Options.td | 2 ++ include/clang/Frontend/CodeGenOptions.def | 3 ++ lib/CodeGen/CGBuiltin.cpp | 2 +- lib/CodeGen/CGExpr.cpp | 24 +++++++++++----- lib/CodeGen/CGExprScalar.cpp | 6 ++-- lib/CodeGen/CodeGenFunction.cpp | 5 ++-- lib/CodeGen/CodeGenFunction.h | 12 +++++++- lib/Frontend/CompilerInvocation.cpp | 1 + test/CodeGen/catch-undef-behavior.c | 34 +++++++++++------------ test/CodeGen/sanitize-recover.c | 17 ++++++++++++ test/CodeGenCXX/catch-undef-behavior.cpp | 8 ++++-- 11 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 test/CodeGen/sanitize-recover.c diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index af06f2ee91..b50e64f141 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -201,6 +201,8 @@ def mconstructor_aliases : Flag<["-"], "mconstructor-aliases">, HelpText<"Emit complete constructors and destructors as aliases when possible">; def mlink_bitcode_file : Separate<["-"], "mlink-bitcode-file">, HelpText<"Link the given bitcode file before performing optimizations.">; +def fsanitize_recover : Flag<["-"], "fsanitize-recover">, + HelpText<"Attempt to recover from failed sanitizer checks when possible">; //===----------------------------------------------------------------------===// // Dependency Output Options diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index d3f29f1b58..f20485a6e9 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -125,6 +125,9 @@ ENUM_CODEGENOPT(Inlining, InliningMethod, 2, NoInlining) /// The default TLS model to use. ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel) +CODEGENOPT(SanitizeRecover, 1, 0) ///< Attempt to recover from sanitizer checks + ///< by continuing execution when possible + #undef CODEGENOPT #undef ENUM_CODEGENOPT #undef VALUE_CODEGENOPT diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index e8c05d3a46..6067408403 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -409,7 +409,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, if (getLangOpts().SanitizeUnreachable) EmitCheck(Builder.getFalse(), "builtin_unreachable", EmitCheckSourceLocation(E->getExprLoc()), - llvm::ArrayRef()); + llvm::ArrayRef(), CRK_Unrecoverable); else Builder.CreateUnreachable(); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index ad6e3beea3..ef17bdc1de 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -533,7 +533,7 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, llvm::ConstantInt::get(SizeTy, AlignVal), llvm::ConstantInt::get(Int8Ty, TCK) }; - EmitCheck(Cond, "type_mismatch", StaticData, Address); + EmitCheck(Cond, "type_mismatch", StaticData, Address, CRK_Recoverable); } // If possible, check that the vptr indicates that there is a subobject of @@ -586,7 +586,8 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, }; llvm::Value *DynamicData[] = { Address, Hash }; EmitCheck(Builder.CreateICmpEQ(CacheVal, Hash), - "dynamic_type_cache_miss", StaticData, DynamicData, true); + "dynamic_type_cache_miss", StaticData, DynamicData, + CRK_AlwaysRecoverable); } } @@ -2023,7 +2024,7 @@ llvm::Constant *CodeGenFunction::EmitCheckSourceLocation(SourceLocation Loc) { void CodeGenFunction::EmitCheck(llvm::Value *Checked, StringRef CheckName, llvm::ArrayRef StaticArgs, llvm::ArrayRef DynamicArgs, - bool Recoverable) { + CheckRecoverableKind RecoverKind) { llvm::BasicBlock *Cont = createBasicBlock("cont"); llvm::BasicBlock *Handler = createBasicBlock("handler." + CheckName); @@ -2051,20 +2052,29 @@ void CodeGenFunction::EmitCheck(llvm::Value *Checked, StringRef CheckName, ArgTypes.push_back(IntPtrTy); } + bool Recover = (RecoverKind == CRK_AlwaysRecoverable) || + ((RecoverKind == CRK_Recoverable) && + CGM.getCodeGenOpts().SanitizeRecover); + llvm::FunctionType *FnType = llvm::FunctionType::get(CGM.VoidTy, ArgTypes, false); llvm::AttrBuilder B; - if (!Recoverable) { + if (!Recover) { B.addAttribute(llvm::Attributes::NoReturn) .addAttribute(llvm::Attributes::NoUnwind); } B.addAttribute(llvm::Attributes::UWTable); - llvm::Value *Fn = CGM.CreateRuntimeFunction(FnType, - ("__ubsan_handle_" + CheckName).str(), + + // Checks that have two variants use a suffix to differentiate them + bool NeedsAbortSuffix = (RecoverKind != CRK_Unrecoverable) && + !CGM.getCodeGenOpts().SanitizeRecover; + Twine FunctionName = "__ubsan_handle_" + CheckName + + Twine(NeedsAbortSuffix? "_abort" : ""); + llvm::Value *Fn = CGM.CreateRuntimeFunction(FnType, FunctionName.str(), llvm::Attributes::get(getLLVMContext(), B)); llvm::CallInst *HandlerCall = Builder.CreateCall(Fn, Args); - if (Recoverable) { + if (Recover) { Builder.CreateBr(Cont); } else { HandlerCall->setDoesNotReturn(); diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 298be923cb..993e2a1dc7 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -645,7 +645,8 @@ void ScalarExprEmitter::EmitFloatConversionCheck(Value *OrigSrc, CGF.EmitCheckTypeDescriptor(OrigSrcType), CGF.EmitCheckTypeDescriptor(DstType) }; - CGF.EmitCheck(Check, "float_cast_overflow", StaticArgs, OrigSrc); + CGF.EmitCheck(Check, "float_cast_overflow", StaticArgs, OrigSrc, + CodeGenFunction::CRK_Recoverable); } /// EmitScalarConversion - Emit a conversion from the specified type to the @@ -850,7 +851,8 @@ void ScalarExprEmitter::EmitBinOpCheck(Value *Check, const BinOpInfo &Info) { DynamicData.push_back(Info.RHS); } - CGF.EmitCheck(Check, CheckName, StaticData, DynamicData); + CGF.EmitCheck(Check, CheckName, StaticData, DynamicData, + CodeGenFunction::CRK_Recoverable); } //===----------------------------------------------------------------------===// diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index f425e1d8ac..8f2e891fed 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -551,7 +551,7 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, if (getLangOpts().SanitizeReturn) EmitCheck(Builder.getFalse(), "missing_return", EmitCheckSourceLocation(FD->getLocation()), - llvm::ArrayRef()); + llvm::ArrayRef(), CRK_Unrecoverable); else if (CGM.getCodeGenOpts().OptimizationLevel == 0) Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap)); Builder.CreateUnreachable(); @@ -1141,7 +1141,8 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { EmitCheckTypeDescriptor(size->getType()) }; EmitCheck(Builder.CreateICmpSGT(Size, Zero), - "vla_bound_not_positive", StaticArgs, Size); + "vla_bound_not_positive", StaticArgs, Size, + CRK_Recoverable); } // Always zexting here would be wrong if it weren't diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index f2ab226ab5..ff3c737421 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2575,13 +2575,23 @@ public: /// passing to a runtime sanitizer handler. llvm::Constant *EmitCheckSourceLocation(SourceLocation Loc); + /// \brief Specify under what conditions this check can be recovered + enum CheckRecoverableKind { + /// Always terminate program execution if this check fails + CRK_Unrecoverable, + /// Check supports recovering, allows user to specify which + CRK_Recoverable, + /// Runtime conditionally aborts, always need to support recovery. + CRK_AlwaysRecoverable + }; + /// \brief Create a basic block that will call a handler function in a /// sanitizer runtime with the provided arguments, and create a conditional /// branch to it. void EmitCheck(llvm::Value *Checked, StringRef CheckName, llvm::ArrayRef StaticArgs, llvm::ArrayRef DynamicArgs, - bool Recoverable = false); + CheckRecoverableKind Recoverable); /// \brief Create a basic block that will call the trap intrinsic, and emit a /// conditional branch to it, for the -ftrapv checks. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9a80b1f3b3..9b10f85028 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -393,6 +393,7 @@ 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_fsanitize_recover); Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); Opts.InstrumentForProfiling = Args.hasArg(OPT_pg); diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c index 7d50cea39a..5af957c989 100644 --- a/test/CodeGen/catch-undef-behavior.c +++ b/test/CodeGen/catch-undef-behavior.c @@ -41,12 +41,12 @@ void foo() { // CHECK-NEXT: br i1 %[[OK]] // CHECK: %[[ARG:.*]] = ptrtoint {{.*}} %[[PTR]] to i64 - // CHECK-NEXT: call void @__ubsan_handle_type_mismatch(i8* bitcast ({{.*}} @[[LINE_100]] to i8*), i64 %[[ARG]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_type_mismatch_abort(i8* bitcast ({{.*}} @[[LINE_100]] to i8*), i64 %[[ARG]]) noreturn nounwind // With -fsanitize=null, only perform the null check. // CHECK-NULL: %[[NULL:.*]] = icmp ne {{.*}}, null // CHECK-NULL: br i1 %[[NULL]] - // CHECK-NULL: call void @__ubsan_handle_type_mismatch(i8* bitcast ({{.*}} @[[LINE_100]] to i8*), i64 %{{.*}}) noreturn nounwind + // CHECK-NULL: call void @__ubsan_handle_type_mismatch_abort(i8* bitcast ({{.*}} @[[LINE_100]] to i8*), i64 %{{.*}}) noreturn nounwind #line 100 u.i=1; } @@ -61,7 +61,7 @@ int bar(int *a) { // CHECK-NEXT: icmp eq i64 %[[MISALIGN]], 0 // CHECK: %[[ARG:.*]] = ptrtoint - // CHECK-NEXT: call void @__ubsan_handle_type_mismatch(i8* bitcast ({{.*}} @[[LINE_200]] to i8*), i64 %[[ARG]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_type_mismatch_abort(i8* bitcast ({{.*}} @[[LINE_200]] to i8*), i64 %[[ARG]]) noreturn nounwind #line 200 return *a; } @@ -80,7 +80,7 @@ int lsh_overflow(int a, int b) { // FIXME: Only emit one trap block here. // CHECK: %[[ARG1:.*]] = zext // CHECK-NEXT: %[[ARG2:.*]] = zext - // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds(i8* bitcast ({{.*}} @[[LINE_300_A]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds_abort(i8* bitcast ({{.*}} @[[LINE_300_A]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind // CHECK: %[[SHIFTED_OUT_WIDTH:.*]] = sub nuw nsw i32 31, %[[RHS]] // CHECK-NEXT: %[[SHIFTED_OUT:.*]] = lshr i32 %[[LHS:.*]], %[[SHIFTED_OUT_WIDTH]] @@ -89,7 +89,7 @@ int lsh_overflow(int a, int b) { // CHECK: %[[ARG1:.*]] = zext // CHECK-NEXT: %[[ARG2:.*]] = zext - // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds(i8* bitcast ({{.*}} @[[LINE_300_B]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds_abort(i8* bitcast ({{.*}} @[[LINE_300_B]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind // CHECK: %[[RET:.*]] = shl i32 %[[LHS]], %[[RHS]] // CHECK-NEXT: ret i32 %[[RET]] @@ -104,7 +104,7 @@ int rsh_inbounds(int a, int b) { // CHECK: %[[ARG1:.*]] = zext // CHECK-NEXT: %[[ARG2:.*]] = zext - // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds(i8* bitcast ({{.*}} @[[LINE_400]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds_abort(i8* bitcast ({{.*}} @[[LINE_400]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind // CHECK: %[[RET:.*]] = ashr i32 %[[LHS]], %[[RHS]] // CHECK-NEXT: ret i32 %[[RET]] @@ -114,14 +114,14 @@ int rsh_inbounds(int a, int b) { // CHECK: @load int load(int *p) { - // CHECK: call void @__ubsan_handle_type_mismatch(i8* bitcast ({{.*}} @[[LINE_500]] to i8*), i64 %{{.*}}) noreturn nounwind + // CHECK: call void @__ubsan_handle_type_mismatch_abort(i8* bitcast ({{.*}} @[[LINE_500]] to i8*), i64 %{{.*}}) noreturn nounwind #line 500 return *p; } // CHECK: @store void store(int *p, int q) { - // CHECK: call void @__ubsan_handle_type_mismatch(i8* bitcast ({{.*}} @[[LINE_600]] to i8*), i64 %{{.*}}) noreturn nounwind + // CHECK: call void @__ubsan_handle_type_mismatch_abort(i8* bitcast ({{.*}} @[[LINE_600]] to i8*), i64 %{{.*}}) noreturn nounwind #line 600 *p = q; } @@ -130,7 +130,7 @@ struct S { int k; }; // CHECK: @member_access int *member_access(struct S *p) { - // CHECK: call void @__ubsan_handle_type_mismatch(i8* bitcast ({{.*}} @[[LINE_700]] to i8*), i64 %{{.*}}) noreturn nounwind + // CHECK: call void @__ubsan_handle_type_mismatch_abort(i8* bitcast ({{.*}} @[[LINE_700]] to i8*), i64 %{{.*}}) noreturn nounwind #line 700 return &p->k; } @@ -139,7 +139,7 @@ int *member_access(struct S *p) { int signed_overflow(int a, int b) { // CHECK: %[[ARG1:.*]] = zext // CHECK-NEXT: %[[ARG2:.*]] = zext - // CHECK-NEXT: call void @__ubsan_handle_add_overflow(i8* bitcast ({{.*}} @[[LINE_800]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_add_overflow_abort(i8* bitcast ({{.*}} @[[LINE_800]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]]) noreturn nounwind #line 800 return a + b; } @@ -159,7 +159,7 @@ void vla_bound(int n) { // CHECK: icmp sgt i32 %[[PARAM:.*]], 0 // // CHECK: %[[ARG:.*]] = zext i32 %[[PARAM]] to i64 - // CHECK-NEXT: call void @__ubsan_handle_vla_bound_not_positive(i8* bitcast ({{.*}} @[[LINE_900]] to i8*), i64 %[[ARG]]) noreturn nounwind + // CHECK-NEXT: call void @__ubsan_handle_vla_bound_not_positive_abort(i8* bitcast ({{.*}} @[[LINE_900]] to i8*), i64 %[[ARG]]) noreturn nounwind #line 900 int arr[n * 3]; } @@ -174,7 +174,7 @@ float int_float_no_overflow(__int128 n) { float int_float_overflow(unsigned __int128 n) { // This is 2**104. FLT_MAX is 2**128 - 2**104. // CHECK: icmp ule i128 %{{.*}}, -20282409603651670423947251286016 - // CHECK: call void @__ubsan_handle_float_cast_overflow( + // CHECK: call void @__ubsan_handle_float_cast_overflow_abort( return n; } @@ -183,7 +183,7 @@ void int_fp16_overflow(int n, __fp16 *p) { // CHECK: %[[GE:.*]] = icmp sge i32 %{{.*}}, -65504 // CHECK: %[[LE:.*]] = icmp sle i32 %{{.*}}, 65504 // CHECK: and i1 %[[GE]], %[[LE]] - // CHECK: call void @__ubsan_handle_float_cast_overflow( + // CHECK: call void @__ubsan_handle_float_cast_overflow_abort( *p = n; } @@ -192,7 +192,7 @@ int float_int_overflow(float f) { // CHECK: %[[GE:.*]] = fcmp oge float %[[F:.*]], 0xC1E0000000000000 // CHECK: %[[LE:.*]] = fcmp ole float %[[F]], 0x41DFFFFFE0000000 // CHECK: and i1 %[[GE]], %[[LE]] - // CHECK: call void @__ubsan_handle_float_cast_overflow( + // CHECK: call void @__ubsan_handle_float_cast_overflow_abort( return f; } @@ -201,7 +201,7 @@ unsigned float_uint_overflow(float f) { // CHECK: %[[GE:.*]] = fcmp oge float %[[F:.*]], 0.{{0*}}e+00 // CHECK: %[[LE:.*]] = fcmp ole float %[[F]], 0x41EFFFFFE0000000 // CHECK: and i1 %[[GE]], %[[LE]] - // CHECK: call void @__ubsan_handle_float_cast_overflow( + // CHECK: call void @__ubsan_handle_float_cast_overflow_abort( return f; } @@ -210,7 +210,7 @@ signed char fp16_char_overflow(__fp16 *p) { // CHECK: %[[GE:.*]] = fcmp oge float %[[F:.*]], -1.28{{0*}}e+02 // CHECK: %[[LE:.*]] = fcmp ole float %[[F]], 1.27{{0*}}e+02 // CHECK: and i1 %[[GE]], %[[LE]] - // CHECK: call void @__ubsan_handle_float_cast_overflow( + // CHECK: call void @__ubsan_handle_float_cast_overflow_abort( return *p; } @@ -219,7 +219,7 @@ float float_float_overflow(double f) { // CHECK: %[[GE:.*]] = fcmp oge double %[[F:.*]], 0xC7EFFFFFE0000000 // CHECK: %[[LE:.*]] = fcmp ole double %[[F]], 0x47EFFFFFE0000000 // CHECK: and i1 %[[GE]], %[[LE]] - // CHECK: call void @__ubsan_handle_float_cast_overflow( + // CHECK: call void @__ubsan_handle_float_cast_overflow_abort( return f; } diff --git a/test/CodeGen/sanitize-recover.c b/test/CodeGen/sanitize-recover.c new file mode 100644 index 0000000000..49809286e2 --- /dev/null +++ b/test/CodeGen/sanitize-recover.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=unsigned-integer-overflow -fsanitize-recover %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 + + +// RECOVER: @test +// ABORT: @test +void test() { + extern volatile unsigned x, y, z; + + // RECOVER: uadd.with.overflow.i32 + // RECOVER: ubsan_handle_add_overflow( + // RECOVER-NOT: unreachable + // ABORT: uadd.with.overflow.i32 + // ABORT: ubsan_handle_add_overflow_abort( + // ABORT: unreachable + x = y + z; +} diff --git a/test/CodeGenCXX/catch-undef-behavior.cpp b/test/CodeGenCXX/catch-undef-behavior.cpp index 9c2985c198..86a306daf5 100644 --- a/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/test/CodeGenCXX/catch-undef-behavior.cpp @@ -69,7 +69,9 @@ void member_access(S *p) { // CHECK-NEXT: icmp eq i64 %[[CACHEVAL]], %[[HASH]] // CHECK-NEXT: br i1 - // CHECK: call void @__ubsan_handle_dynamic_type_cache_miss({{.*}}, i64 %{{.*}}, i64 %[[HASH]]) + // CHECK: call void @__ubsan_handle_dynamic_type_cache_miss_abort({{.*}}, i64 %{{.*}}, i64 %[[HASH]]) + // CHECK-NOT: unreachable + // CHECK: {{.*}}: // (2) Check 'p->b' is appropriately sized and aligned for a load. @@ -102,7 +104,9 @@ void member_access(S *p) { // [...] // CHECK: getelementptr inbounds [128 x i64]* @__ubsan_vptr_type_cache, i32 0, i64 % // CHECK: br i1 - // CHECK: call void @__ubsan_handle_dynamic_type_cache_miss({{.*}}, i64 %{{.*}}, i64 %{{.*}}) + // CHECK: call void @__ubsan_handle_dynamic_type_cache_miss_abort({{.*}}, i64 %{{.*}}, i64 %{{.*}}) + // CHECK-NOT: unreachable + // CHECK: {{.*}}: k = p->f(); } -- 2.40.0