Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -4393,12 +4393,16 @@ if (UnusedReturnSizePtr) PopCleanupBlock(); - // Strip away the noreturn attribute to better diagnose unreachable UB. + // Replace 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, llvm::Attribute::NoReturn); + CS.addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ExpectNoReturn); } EmitUnreachable(Loc); 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() [[CALL_SITE_ATTR:#[0-9]+]] 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({{.*}}) [[CALL_SITE_ATTR]] 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() [[CALL_SITE_ATTR]] does_not_return1(); // CHECK: __ubsan_handle_builtin_unreachable @@ -46,18 +43,18 @@ // CHECK-LABEL: define linkonce_odr void @_ZN1A5call3Ev void call3() { MemFn MF = &A::does_not_return2; + // CHECK: call void %{{[0-9]+\(.*}}) [[CALL_SITE_ATTR]] (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]+]] + // CHECK-SAME: [[USER_FN_ATTR:#[0-9]+]] void __attribute__((noreturn)) does_not_return2() { - // CHECK-NOT: call void @_Z5abortv(){{.*}}# + // CHECK: call void @_Z5abortv() [[CALL_SITE_ATTR]] 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() [[CALL_SITE_ATTR]] void force_irgen() { A a; @@ -77,5 +76,9 @@ a.call3(); } -// CHECK-NOT: [[ABORT_ATTR]] = {{[^}]+}}noreturn -// CHECK-NOT: [[DOES_NOT_RETURN_ATTR]] = {{[^}]+}}noreturn +// 1) 'noreturn' should be removed from functions and call sites +// 2) 'expect_noreturn' added to call sites +// CHECK-LABEL: attributes +// CHECK: [[USER_FN_ATTR]] = { {{.*[^noreturn].*}} } +// CHECK: [[EXTERN_FN_ATTR]] = { {{.*[^noreturn].*}} } +// CHECK: [[CALL_SITE_ATTR]] = { expect_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,16 @@ +// Ensure compatiblity of UBSan unreachable with ASan in the presence of +// noreturn functions +// RUN: %clang -O2 -fsanitize=address,unreachable %s -emit-llvm -S -o - | FileCheck %s +// REQUIRES: ubsan-asan + +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 @__asan_handle_no_return +// CHECK-NEXT: call void @__ubsan_handle_builtin_unreachable +// CHECK-NEXT: unreachable Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1458,6 +1458,10 @@ This function attribute indicates that the function never returns normally. This produces undefined behavior at runtime if the function ever does dynamically return. +``expect_noreturn`` + This function attribute indicates that the function is unlikely to return + normally, but that it still allowed to do so. This is useful in cases where + ``noreturn`` is too strong a guarantee. ``norecurse`` This function attribute indicates that the function does not call itself either directly or indirectly down any possible call path. This produces Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -602,6 +602,7 @@ ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_SHADOWCALLSTACK = 58, ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, + ATTR_KIND_EXPECT_NO_RETURN = 60, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -106,6 +106,10 @@ /// Mark the function as not returning. def NoReturn : EnumAttr<"noreturn">; +/// Mark the function as unlikely to return. This is useful in cases where +/// `noreturn` is too strong a guarantee. +def ExpectNoReturn : EnumAttr<"expect_noreturn">; + /// Disable Indirect Branch Tracking. def NoCfCheck : EnumAttr<"nocf_check">; Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -657,6 +657,7 @@ KEYWORD(nonnull); KEYWORD(noredzone); KEYWORD(noreturn); + KEYWORD(expect_noreturn); KEYWORD(nocf_check); KEYWORD(nounwind); KEYWORD(optforfuzzing); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1249,6 +1249,8 @@ case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break; case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break; case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break; + case lltok::kw_expect_noreturn: + B.addAttribute(Attribute::ExpectNoReturn); break; case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break; case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break; case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; @@ -1612,6 +1614,7 @@ case lltok::kw_nonlazybind: case lltok::kw_noredzone: case lltok::kw_noreturn: + case lltok::kw_expect_noreturn: case lltok::kw_nocf_check: case lltok::kw_nounwind: case lltok::kw_optforfuzzing: @@ -1709,6 +1712,7 @@ case lltok::kw_nonlazybind: case lltok::kw_noredzone: case lltok::kw_noreturn: + case lltok::kw_expect_noreturn: case lltok::kw_nocf_check: case lltok::kw_nounwind: case lltok::kw_optforfuzzing: Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -201,6 +201,7 @@ kw_nonnull, kw_noredzone, kw_noreturn, + kw_expect_noreturn, kw_nocf_check, kw_nounwind, kw_optforfuzzing, Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1185,8 +1185,8 @@ case Attribute::NoCfCheck: return 1ULL << 57; case Attribute::OptForFuzzing: return 1ULL << 58; case Attribute::ShadowCallStack: return 1ULL << 59; - case Attribute::SpeculativeLoadHardening: - return 1ULL << 60; + case Attribute::SpeculativeLoadHardening: return 1ULL << 60; + case Attribute::ExpectNoReturn: return 1ULL << 61; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; @@ -1365,6 +1365,8 @@ return Attribute::NoRedZone; case bitc::ATTR_KIND_NO_RETURN: return Attribute::NoReturn; + case bitc::ATTR_KIND_EXPECT_NO_RETURN: + return Attribute::ExpectNoReturn; case bitc::ATTR_KIND_NOCF_CHECK: return Attribute::NoCfCheck; case bitc::ATTR_KIND_NO_UNWIND: Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -653,6 +653,8 @@ return bitc::ATTR_KIND_NO_RED_ZONE; case Attribute::NoReturn: return bitc::ATTR_KIND_NO_RETURN; + case Attribute::ExpectNoReturn: + return bitc::ATTR_KIND_EXPECT_NO_RETURN; case Attribute::NoCfCheck: return bitc::ATTR_KIND_NOCF_CHECK; case Attribute::NoUnwind: Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -299,6 +299,8 @@ return "noredzone"; if (hasAttribute(Attribute::NoReturn)) return "noreturn"; + if (hasAttribute(Attribute::ExpectNoReturn)) + return "expect_noreturn"; if (hasAttribute(Attribute::NoCfCheck)) return "nocf_check"; if (hasAttribute(Attribute::NoRecurse)) Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1478,6 +1478,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) { switch (Kind) { case Attribute::NoReturn: + case Attribute::ExpectNoReturn: case Attribute::NoCfCheck: case Attribute::NoUnwind: case Attribute::NoInline: Index: llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -42,6 +42,7 @@ .Case("nonlazybind", Attribute::NonLazyBind) .Case("noredzone", Attribute::NoRedZone) .Case("noreturn", Attribute::NoReturn) + .Case("expect_noreturn", Attribute::ExpectNoReturn) .Case("nocf_check", Attribute::NoCfCheck) .Case("norecurse", Attribute::NoRecurse) .Case("nounwind", Attribute::NoUnwind) Index: llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -2569,7 +2569,8 @@ if (CS) { // A call inside BB. TempsToInstrument.clear(); - if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction()); + if (CS.doesNotReturn() || CS.hasFnAttr(Attribute::ExpectNoReturn)) + NoReturnCalls.push_back(CS.getInstruction()); } if (CallInst *CI = dyn_cast(&Inst)) maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -780,6 +780,7 @@ case Attribute::NoBuiltin: case Attribute::NoCapture: case Attribute::NoReturn: + case Attribute::ExpectNoReturn: case Attribute::None: case Attribute::NonNull: case Attribute::ReadNone: Index: llvm/test/Bitcode/attributes.ll =================================================================== --- llvm/test/Bitcode/attributes.ll +++ llvm/test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #36 +; CHECK: call void @nobuiltin() #37 ret void; } @@ -351,6 +351,12 @@ ret void } +; CHECK: define void @f60() #36 +define void @f60() expect_noreturn +{ + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -387,4 +393,5 @@ ; CHECK: attributes #33 = { speculatable } ; CHECK: attributes #34 = { sanitize_hwaddress } ; CHECK: attributes #35 = { shadowcallstack } -; CHECK: attributes #36 = { nobuiltin } +; CHECK: attributes #36 = { expect_noreturn } +; CHECK: attributes #37 = { nobuiltin } 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,45 @@ -; 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 -; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: call void @MyNoReturnFunc -; CHECK-NEXT: unreachable +; Instrument calls to noreturn functions (regardless of callsite) +define i32 @Call1() sanitize_address { + call void @NoReturnFunc() 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-LABEL: @Call1 ; CHECK: call void @__asan_handle_no_return -; CHECK-NEXT: call void @MyNoReturnFunc -; CHECK-NEXT: unreachable +; CHECK-NEXT: call void @NoReturnFunc + +; Instrument noreturn call sites (regardless of function) +define i32 @Call2() sanitize_address { + call void @NormalFunc() noreturn unreachable } +; CHECK-LABEL: @Call2 +; CHECK: call void @__asan_handle_no_return +; CHECK-NEXT: call void @NormalFunc + +; Also instrument expect_noreturn call sites +define i32 @Call3() sanitize_address { + call void @NormalFunc() expect_noreturn + ret i32 0 +} +; CHECK-LABEL: @Call3 +; CHECK: call void @__asan_handle_no_return +; CHECK-NEXT: call void @NormalFunc 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 +50,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