Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -4401,8 +4401,25 @@ if (UnusedReturnSizePtr) PopCleanupBlock(); + // Unpoison the stack before noreturn calls (e.g., throw, _exit, etc). + // See https://github.com/google/sanitizers/issues/37 + if (SanOpts.has(SanitizerKind::Address)) { + SanitizerScope SanScope(this); + Builder.SetInsertPoint(CS.getInstruction()); + auto *FnName = "__asan_handle_no_return"; + auto *FnType = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); + auto FnAttrs = llvm::AttributeList::get( + getLLVMContext(), llvm::AttributeList::FunctionIndex, + {llvm::Attribute::NoUnwind}); + auto *Fn = CGM.CreateRuntimeFunction(FnType, FnName, FnAttrs); + EmitNounwindRuntimeCall(Fn); + Builder.SetInsertPoint(CS->getParent()); + } + // Strip away the noreturn attribute to better diagnose unreachable UB. if (SanOpts.has(SanitizerKind::Unreachable)) { + // Also remove from function since CS.hasFnAttr(..) also checks attributes + // of the called function. if (auto *F = CS.getCalledFunction()) F->removeFnAttr(llvm::Attribute::NoReturn); CS.removeAttribute(llvm::AttributeList::FunctionIndex, Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -4085,8 +4085,8 @@ /// passing to a runtime sanitizer handler. llvm::Constant *EmitCheckSourceLocation(SourceLocation Loc); - /// Create a basic block that will call a handler function in a - /// sanitizer runtime with the provided arguments, and create a conditional + /// Create a basic block that will either trap or call a handler function in + /// the UBSan runtime with the provided arguments, and create a conditional /// branch to it. void EmitCheck(ArrayRef> Checked, SanitizerHandler Check, ArrayRef StaticArgs, Index: clang/test/CodeGen/asan-noreturn.c =================================================================== --- /dev/null +++ clang/test/CodeGen/asan-noreturn.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsanitize=address -triple x86_64-linux -emit-llvm -o - %s | FileCheck %s + +void my_longjmp(void) __attribute__((noreturn)); + +// CHECK-LABEL: define void @calls_noreturn() +void calls_noreturn() { + my_longjmp(); + // CHECK: call void @__asan_handle_no_return(){{.*}} !nosanitize + // CHECK-NEXT: call void @my_longjmp() [[CALL_SITE_ATTR:#[0-9]+]] + // CHECK-NEXT: unreachable +} + +// CHECK: declare void @my_longjmp() [[FN_ATTR:#[0-9]+]] +// CHECK: declare void @__asan_handle_no_return() [[HANDLER_FN_ATTR:#[0-9]+]] + +// CHECK-LABEL: attributes +// CHECK: [[FN_ATTR]] = { {{.*noreturn.*}} } +// CHECK: [[HANDLER_FN_ATTR]] = { nounwind } +// CHECK: [[CALL_SITE_ATTR]] = { {{.*noreturn.*}} } Index: clang/test/CodeGenCXX/ubsan-unreachable.cpp =================================================================== --- clang/test/CodeGenCXX/ubsan-unreachable.cpp +++ clang/test/CodeGenCXX/ubsan-unreachable.cpp @@ -1,39 +1,37 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=unreachable | FileCheck %s -extern void __attribute__((noreturn)) abort(); +void abort() __attribute__((noreturn)); -// CHECK-LABEL: define void @_Z14calls_noreturnv +// CHECK-LABEL: define void @_Z14calls_noreturnv() void calls_noreturn() { + // Check absence ([^#]*) of call site attributes (including noreturn) + // CHECK: call void @_Z5abortv(){{[^#]*}} abort(); - // Check that there are no attributes on the call site. - // CHECK-NOT: call void @_Z5abortv{{.*}}# - // CHECK: __ubsan_handle_builtin_unreachable // CHECK: unreachable } struct A { - // CHECK: declare void @_Z5abortv{{.*}} [[ABORT_ATTR:#[0-9]+]] + // CHECK: declare void @_Z5abortv() [[EXTERN_FN_ATTR:#[0-9]+]] // CHECK-LABEL: define linkonce_odr void @_ZN1A5call1Ev void call1() { - // CHECK-NOT: call void @_ZN1A16does_not_return2Ev{{.*}}# + // CHECK: call void @_ZN1A16does_not_return2Ev({{.*}}){{[^#]*}} does_not_return2(); // CHECK: __ubsan_handle_builtin_unreachable // CHECK: unreachable } - // Test static members. - static void __attribute__((noreturn)) does_not_return1() { - // CHECK-NOT: call void @_Z5abortv{{.*}}# + // Test static members. Checks are below after `struct A` scope ends. + static void does_not_return1() __attribute__((noreturn)) { abort(); } // CHECK-LABEL: define linkonce_odr void @_ZN1A5call2Ev void call2() { - // CHECK-NOT: call void @_ZN1A16does_not_return1Ev{{.*}}# + // CHECK: call void @_ZN1A16does_not_return1Ev(){{[^#]*}} does_not_return1(); // CHECK: __ubsan_handle_builtin_unreachable @@ -41,23 +39,23 @@ } // Test calls through pointers to non-static member functions. - typedef void __attribute__((noreturn)) (A::*MemFn)(); + typedef void (A::*MemFn)() __attribute__((noreturn)); // CHECK-LABEL: define linkonce_odr void @_ZN1A5call3Ev void call3() { MemFn MF = &A::does_not_return2; + // CHECK: call void %{{[0-9]+\(.*}}){{[^#]*}} (this->*MF)(); - // CHECK-NOT: call void %{{.*}}# // CHECK: __ubsan_handle_builtin_unreachable // CHECK: unreachable } // Test regular members. // CHECK-LABEL: define linkonce_odr void @_ZN1A16does_not_return2Ev({{.*}}) - // CHECK-SAME: [[DOES_NOT_RETURN_ATTR:#[0-9]+]] - void __attribute__((noreturn)) does_not_return2() { - // CHECK-NOT: call void @_Z5abortv(){{.*}}# + // CHECK-SAME: [[USER_FN_ATTR:#[0-9]+]] + void does_not_return2() __attribute__((noreturn)) { + // CHECK: call void @_Z5abortv(){{[^#]*}} abort(); // CHECK: call void @__ubsan_handle_builtin_unreachable @@ -68,7 +66,9 @@ } }; -// CHECK: define linkonce_odr void @_ZN1A16does_not_return1Ev() [[DOES_NOT_RETURN_ATTR]] +// CHECK-LABEL: define linkonce_odr void @_ZN1A16does_not_return1Ev() +// CHECK-SAME: [[USER_FN_ATTR]] +// CHECK: call void @_Z5abortv(){{[^#]*}} void force_irgen() { A a; @@ -77,5 +77,7 @@ a.call3(); } -// CHECK-NOT: [[ABORT_ATTR]] = {{[^}]+}}noreturn -// CHECK-NOT: [[DOES_NOT_RETURN_ATTR]] = {{[^}]+}}noreturn +// 'noreturn' should be removed from functions and call sites +// CHECK-LABEL: attributes +// CHECK: [[USER_FN_ATTR]] = { {{.*[^noreturn].*}} } +// CHECK: [[EXTERN_FN_ATTR]] = { {{.*[^noreturn].*}} } Index: compiler-rt/test/ubsan/TestCases/Misc/unreachable_asan-compatibility.c =================================================================== --- /dev/null +++ compiler-rt/test/ubsan/TestCases/Misc/unreachable_asan-compatibility.c @@ -0,0 +1,18 @@ +// Ensure compatiblity of UBSan unreachable with ASan in the presence of +// noreturn functions +// RUN: %clang -O2 -fPIC -fsanitize=address,unreachable %s -emit-llvm -S -o - | FileCheck %s + +// REQUIRES: ubsan-asan +// Expects test to produce executable that can be pushed to device. +// UNSUPPORTED: android + +void bar(void) __attribute__((noreturn)); + +void foo() { + bar(); +} +// CHECK-LABEL: define void @foo() +// CHECK: call void @__asan_handle_no_return +// CHECK-NEXT: call void @bar +// CHECK-NEXT: call void @__ubsan_handle_builtin_unreachable +// CHECK-NEXT: unreachable Index: llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -149,7 +149,6 @@ "__asan_version_mismatch_check_v"; static const char *const kAsanPtrCmp = "__sanitizer_ptr_cmp"; static const char *const kAsanPtrSub = "__sanitizer_ptr_sub"; -static const char *const kAsanHandleNoReturnName = "__asan_handle_no_return"; static const int kMaxAsanStackMallocSizeClass = 10; static const char *const kAsanStackMallocNameTemplate = "__asan_stack_malloc_"; static const char *const kAsanStackFreeNameTemplate = "__asan_stack_free_"; @@ -715,7 +714,6 @@ Type *IntptrTy; ShadowMapping Mapping; DominatorTree *DT; - Function *AsanHandleNoReturnFunc; Function *AsanPtrCmpFunction, *AsanPtrSubFunction; Constant *AsanShadowGlobal; @@ -2383,9 +2381,6 @@ MemIntrinCallbackPrefix + "memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy)); - AsanHandleNoReturnFunc = checkSanitizerInterfaceFunction( - M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy())); - AsanPtrCmpFunction = checkSanitizerInterfaceFunction(M.getOrInsertFunction( kAsanPtrCmp, IRB.getVoidTy(), IntptrTy, IntptrTy)); AsanPtrSubFunction = checkSanitizerInterfaceFunction(M.getOrInsertFunction( @@ -2524,7 +2519,6 @@ // are calls between uses). SmallPtrSet TempsToInstrument; SmallVector ToInstrument; - SmallVector NoReturnCalls; SmallVector AllBlocks; SmallVector PointerComparisonsOrSubtracts; int NumAllocas = 0; @@ -2568,7 +2562,6 @@ if (CS) { // A call inside BB. TempsToInstrument.clear(); - if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction()); } if (CallInst *CI = dyn_cast(&Inst)) maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); @@ -2605,19 +2598,12 @@ FunctionStackPoisoner FSP(F, *this); bool ChangedStack = FSP.runOnFunction(); - // We must unpoison the stack before every NoReturn call (throw, _exit, etc). - // See e.g. https://github.com/google/sanitizers/issues/37 - for (auto CI : NoReturnCalls) { - IRBuilder<> IRB(CI); - IRB.CreateCall(AsanHandleNoReturnFunc, {}); - } - for (auto Inst : PointerComparisonsOrSubtracts) { instrumentPointerComparisonOrSubtraction(Inst); NumInstrumented++; } - if (NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty()) + if (NumInstrumented > 0 || ChangedStack) FunctionModified = true; LLVM_DEBUG(dbgs() << "ASAN done instrumenting: " << FunctionModified << " " Index: llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll =================================================================== --- llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll +++ /dev/null @@ -1,49 +0,0 @@ -; RUN: opt < %s -asan -asan-module -S | FileCheck %s -; AddressSanitizer must insert __asan_handle_no_return -; before every noreturn call or invoke. - -target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" -target triple = "x86_64-unknown-linux-gnu" - -declare void @MyNoReturnFunc(i32) noreturn - -define i32 @Call1(i8* nocapture %arg) uwtable sanitize_address { -entry: - call void @MyNoReturnFunc(i32 1) noreturn ; The call insn has noreturn attr. -; CHECK: @Call1 -; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: call void @MyNoReturnFunc -; CHECK-NEXT: unreachable - unreachable -} - -define i32 @Call2(i8* nocapture %arg) uwtable sanitize_address { -entry: - call void @MyNoReturnFunc(i32 1) ; No noreturn attribure on the call. -; CHECK: @Call2 -; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: call void @MyNoReturnFunc -; CHECK-NEXT: unreachable - unreachable -} - -declare i32 @__gxx_personality_v0(...) - -define i64 @Invoke1(i8** %esc) nounwind uwtable ssp sanitize_address personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { -entry: - invoke void @MyNoReturnFunc(i32 1) - to label %invoke.cont unwind label %lpad - -invoke.cont: - ret i64 0 - -lpad: - %0 = landingpad { i8*, i32 } - filter [0 x i8*] zeroinitializer - ret i64 1 -} -; CHECK: @Invoke1 -; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: invoke void @MyNoReturnFunc -; CHECK: ret i64 0 -; CHECK: ret i64 1 Index: llvm/test/Instrumentation/AddressSanitizer/lifetime-throw.ll =================================================================== --- llvm/test/Instrumentation/AddressSanitizer/lifetime-throw.ll +++ llvm/test/Instrumentation/AddressSanitizer/lifetime-throw.ll @@ -28,6 +28,7 @@ ; CHECK-NEXT: @llvm.lifetime.start %exception = call i8* @__cxa_allocate_exception(i64 4) + call void @__asan_handle_no_return() invoke void @__cxa_throw(i8* %exception, i8* bitcast ({ i8*, i8* }* @_ZTI3ABC to i8*), i8* bitcast (void (%struct.ABC*)* @_ZN3ABCD2Ev to i8*)) noreturn to label %unreachable unwind label %lpad ; CHECK: call void @__asan_handle_no_return @@ -82,6 +83,7 @@ ; CHECK-NEXT: @llvm.lifetime.start %1 = bitcast %struct.ABC* %tmp to i8* + call void @__asan_handle_no_return() invoke void @_CxxThrowException(i8* %1, %eh.ThrowInfo* nonnull @"_TI1?AUABC@@") noreturn to label %unreachable unwind label %ehcleanup ; CHECK: call void @__asan_handle_no_return @@ -112,3 +114,4 @@ declare void @"\01??1ABC@@QEAA@XZ"(%struct.ABC* %this) declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) declare i32 @__CxxFrameHandler3(...) +declare void @__asan_handle_no_return() nounwind Index: llvm/test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll =================================================================== --- llvm/test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll +++ llvm/test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll @@ -61,6 +61,7 @@ ; CHECK: ret void entry: %a = alloca i32, align 4 + call void @__asan_handle_no_return() %call = call i32 @_setjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @_ZL3buf, i32 0, i32 0)) nounwind returns_twice %cmp = icmp eq i32 0, %call br i1 %cmp, label %if.then, label %if.end @@ -77,3 +78,4 @@ declare i32 @_setjmp(%struct.__jmp_buf_tag*) nounwind returns_twice declare void @longjmp(%struct.__jmp_buf_tag*, i32) noreturn nounwind declare void @_Z10escape_ptrPi(i32*) +declare void @__asan_handle_no_return() nounwind