Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -4395,10 +4395,13 @@ // Strip away the noreturn attribute to better diagnose unreachable UB. if (SanOpts.has(SanitizerKind::Unreachable)) { + // Also remove from function since hasFnAttr checks attributes of + // instruction and called function. if (auto *F = CS.getCalledFunction()) F->removeFnAttr(llvm::Attribute::NoReturn); CS.removeAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoReturn); + CGM.getSanitizerMetadata()->markCallsiteAsNoReturn(CS.getInstruction()); } EmitUnreachable(Loc); Index: clang/lib/CodeGen/SanitizerMetadata.h =================================================================== --- clang/lib/CodeGen/SanitizerMetadata.h +++ clang/lib/CodeGen/SanitizerMetadata.h @@ -44,7 +44,9 @@ bool IsBlacklisted = false); void disableSanitizerForGlobal(llvm::GlobalVariable *GV); void disableSanitizerForInstruction(llvm::Instruction *I); + void markCallsiteAsNoReturn(llvm::Instruction *I); private: + void annotate(llvm::Instruction *I, StringRef Kind); llvm::MDNode *getLocationMetadata(SourceLocation Loc); }; } // end namespace CodeGen Index: clang/lib/CodeGen/SanitizerMetadata.cpp =================================================================== --- clang/lib/CodeGen/SanitizerMetadata.cpp +++ clang/lib/CodeGen/SanitizerMetadata.cpp @@ -14,6 +14,7 @@ #include "CodeGenModule.h" #include "clang/AST/Type.h" #include "llvm/ADT/StringRef.h" +#include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" using namespace clang; @@ -87,7 +88,16 @@ } void SanitizerMetadata::disableSanitizerForInstruction(llvm::Instruction *I) { - I->setMetadata(CGM.getModule().getMDKindID("nosanitize"), + annotate(I, "nosanitize"); +} + +void SanitizerMetadata::markCallsiteAsNoReturn(llvm::Instruction *I) { + assert(llvm::CallSite(I) && "Only callsites can be marked noreturn"); + annotate(I, "sanitizer_noreturn"); +} + +void SanitizerMetadata::annotate(llvm::Instruction *I, StringRef Kind) { + I->setMetadata(CGM.getModule().getMDKindID(Kind), llvm::MDNode::get(CGM.getLLVMContext(), None)); } Index: clang/test/CodeGenCXX/ubsan-unreachable.cpp =================================================================== --- clang/test/CodeGenCXX/ubsan-unreachable.cpp +++ clang/test/CodeGenCXX/ubsan-unreachable.cpp @@ -2,38 +2,35 @@ extern void __attribute__((noreturn)) abort(); -// CHECK-LABEL: define void @_Z14calls_noreturnv +// CHECK-LABEL: define void @_Z14calls_noreturnv() void calls_noreturn() { + // CHECK: call void @_Z5abortv(), !sanitizer_noreturn 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({{.*}}), !sanitizer_noreturn does_not_return2(); // CHECK: __ubsan_handle_builtin_unreachable // CHECK: unreachable } - // Test static members. + // Test static members. Checks are below after `struct A` scope ends. static void __attribute__((noreturn)) does_not_return1() { - // CHECK-NOT: call void @_Z5abortv{{.*}}# abort(); } // CHECK-LABEL: define linkonce_odr void @_ZN1A5call2Ev void call2() { - // CHECK-NOT: call void @_ZN1A16does_not_return1Ev{{.*}}# + // CHECK: call void @_ZN1A16does_not_return1Ev(), !sanitizer_noreturn does_not_return1(); // CHECK: __ubsan_handle_builtin_unreachable @@ -48,16 +45,16 @@ MemFn MF = &A::does_not_return2; (this->*MF)(); - // CHECK-NOT: call void %{{.*}}# + // CHECK: call void %{{[0-9]+\(.*}}), !sanitizer_noreturn // 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]+]] + // CHECK-SAME: [[USER_FN_ATTR:#[0-9]+]] void __attribute__((noreturn)) does_not_return2() { - // CHECK-NOT: call void @_Z5abortv(){{.*}}# + // CHECK: call void @_Z5abortv(), !sanitizer_noreturn abort(); // CHECK: call void @__ubsan_handle_builtin_unreachable @@ -68,7 +65,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(), !sanitizer_noreturn void force_irgen() { A a; @@ -77,5 +76,6 @@ a.call3(); } -// CHECK-NOT: [[ABORT_ATTR]] = {{[^}]+}}noreturn -// CHECK-NOT: [[DOES_NOT_RETURN_ATTR]] = {{[^}]+}}noreturn +// CHECK-LABEL: attributes +// CHECK: [[USER_FN_ATTR]] = { {{.*[^noreturn].*}}} +// CHECK: [[EXTERN_FN_ATTR]] = { {{.*[^noreturn].*}} Index: compiler-rt/test/asan/TestCases/ubsan_noreturn_compatibility.c =================================================================== --- /dev/null +++ compiler-rt/test/asan/TestCases/ubsan_noreturn_compatibility.c @@ -0,0 +1,14 @@ +// Test compatibility with -fsanitize=unreachable in the presence of noreturn +// functions +// RUN: %clang_asan -O2 -fsanitize=unreachable %s -emit-llvm -S -o - | FileCheck %s + +#include + +// CHECK-LABEL: define void @foo() +void foo() { +// CHECK: call void @__asan_handle_no_return +// CHECK-NEXT: call void @exit + exit(0); +// 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 @@ -2569,7 +2569,11 @@ if (CS) { // A call inside BB. TempsToInstrument.clear(); - if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction()); + bool NoReturn = + CS.doesNotReturn() || CS->getMetadata("sanitizer_noreturn"); + bool NoSanitize = CS->getMetadata("nosanitize"); + if (NoReturn && !NoSanitize) + NoReturnCalls.push_back(CS.getInstruction()); } if (CallInst *CI = dyn_cast(&Inst)) maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); Index: llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll =================================================================== --- llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll +++ llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll @@ -1,37 +1,58 @@ -; RUN: opt < %s -asan -asan-module -S | FileCheck %s +; RUN: opt < %s -asan -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 +declare void @NormalFunc() +declare void @NoReturnFunc() 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 +; Instrument noreturn callsites (regardless of function) +define i32 @Call1(i8* nocapture %arg) sanitize_address { + call void @NormalFunc() noreturn ; The call insn has noreturn attr. + unreachable +} +; CHECK-LABEL: @Call1 ; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: call void @MyNoReturnFunc -; CHECK-NEXT: unreachable +; CHECK-NEXT: call void @NormalFunc +; CHECK-NEXT: unreachable + +; Instrument calls to noreturn functions (regardless of callsite) +define i32 @Call2(i8* nocapture %arg) sanitize_address { + call void @NoReturnFunc() ; No noreturn attribure on the call. unreachable } +; CHECK-LABEL: @Call2 +; CHECK: call void @__asan_handle_no_return +; CHECK-NEXT: call void @NoReturnFunc +; CHECK-NEXT: unreachable -define i32 @Call2(i8* nocapture %arg) uwtable sanitize_address { -entry: - call void @MyNoReturnFunc(i32 1) ; No noreturn attribure on the call. -; CHECK: @Call2 +; Also instrument sanitizer_noreturn callsites +define i32 @Call3(i8* nocapture %arg) sanitize_address { + call void @NormalFunc(), !sanitizer_noreturn !{} + unreachable +} +; CHECK-LABEL: @Call3 ; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: call void @MyNoReturnFunc -; CHECK-NEXT: unreachable +; CHECK-NEXT: call void @NormalFunc +; CHECK-NEXT: unreachable + +; Do NOT instrument callsites marked with !nosanitize +define i32 @Call4(i8* nocapture %arg) sanitize_address { + call void @NoReturnFunc() noreturn, !sanitizer_noreturn !{}, !nosanitize !{} unreachable } +; CHECK-LABEL: @Call4 +; CHECK-NOT: call void @__asan_handle_no_return +; CHECK: call void @NoReturnFunc +; CHECK-NEXT: 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*) { +define i64 @Invoke1(i8** %esc) sanitize_address personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { entry: - invoke void @MyNoReturnFunc(i32 1) + invoke void @NoReturnFunc() to label %invoke.cont unwind label %lpad invoke.cont: @@ -42,8 +63,8 @@ filter [0 x i8*] zeroinitializer ret i64 1 } -; CHECK: @Invoke1 +; CHECK-LABEL: @Invoke1 ; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: invoke void @MyNoReturnFunc +; CHECK-NEXT: invoke void @NoReturnFunc ; CHECK: ret i64 0 ; CHECK: ret i64 1