Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -4398,10 +4398,23 @@ // Strip away the noreturn attribute to better diagnose unreachable UB. if (SanOpts.has(SanitizerKind::Unreachable)) { + // Also remove from function since CI->hasFnAttr(..) also checks attributes + // of the called function. if (auto *F = CI->getCalledFunction()) F->removeFnAttr(llvm::Attribute::NoReturn); CI->removeAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoReturn); + + // Avoid incompatibility with ASan which relies on the `noreturn` + // attribute to insert handler calls. + if (SanOpts.has(SanitizerKind::Address)) { + SanitizerScope SanScope(this); + Builder.SetInsertPoint(CI); + auto *FnType = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); + auto *Fn = CGM.CreateRuntimeFunction(FnType, "__asan_handle_no_return"); + EmitNounwindRuntimeCall(Fn); + Builder.SetInsertPoint(CI->getParent()); + } } EmitUnreachable(Loc); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -4084,8 +4084,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: test/CodeGen/ubsan-asan-noreturn.c =================================================================== --- test/CodeGen/ubsan-asan-noreturn.c +++ test/CodeGen/ubsan-asan-noreturn.c @@ -0,0 +1,21 @@ +// Ensure compatiblity of UBSan unreachable with ASan in the presence of +// noreturn functions. +// RUN: %clang_cc1 -fsanitize=unreachable,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: @__asan_handle_no_return{{.*}} !nosanitize + // CHECK-NEXT: @my_longjmp(){{[^#]*}} + // CHECK: @__asan_handle_no_return() + // CHECK-NEXT: @__ubsan_handle_builtin_unreachable{{.*}} !nosanitize + // CHECK-NEXT: unreachable +} + +// CHECK: declare void @my_longjmp() [[FN_ATTR:#[0-9]+]] +// CHECK: declare void @__asan_handle_no_return() + +// CHECK-LABEL: attributes +// CHECK-NOT: [[FN_ATTR]] = { {{.*noreturn.*}} } Index: test/CodeGenCXX/ubsan-unreachable.cpp =================================================================== --- test/CodeGenCXX/ubsan-unreachable.cpp +++ 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-NOT: [[USER_FN_ATTR]] = { {{.*noreturn.*}} } +// CHECK-NOT: [[EXTERN_FN_ATTR]] = { {{.*noreturn.*}} }