1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 12:41:49 +01:00

[Sanitizers] UBSan unreachable incompatible with ASan in the presence of noreturn calls

Summary:
UBSan wants to detect when unreachable code is actually reached, so it
adds instrumentation before every `unreachable` instruction. However,
the optimizer will remove code after calls to functions marked with
`noreturn`. To avoid this UBSan removes `noreturn` from both the call
instruction as well as from the function itself. Unfortunately, ASan
relies on this annotation to unpoison the stack by inserting calls to
`_asan_handle_no_return` before `noreturn` functions. This is important
for functions that do not return but access the the stack memory, e.g.,
unwinder functions *like* `longjmp` (`longjmp` itself is actually
"double-proofed" via its interceptor). The result is that when ASan and
UBSan are combined, the `noreturn` attributes are missing and ASan
cannot unpoison the stack, so it has false positives when stack
unwinding is used.

Changes:
  # UBSan now adds the `expect_noreturn` attribute whenever it removes
    the `noreturn` attribute from a function
  # ASan additionally checks for the presence of this attribute

Generated code:
```
call void @__asan_handle_no_return    // Additionally inserted to avoid false positives
call void @longjmp
call void @__asan_handle_no_return
call void @__ubsan_handle_builtin_unreachable
unreachable
```

The second call to `__asan_handle_no_return` is redundant. This will be
cleaned up in a follow-up patch.

rdar://problem/40723397

Reviewers: delcypher, eugenis

Tags: #sanitizers

Differential Revision: https://reviews.llvm.org/D56624

llvm-svn: 352003
This commit is contained in:
Julian Lettner 2019-01-24 01:06:19 +00:00
parent a5aba5c47a
commit c814a6a425
15 changed files with 65 additions and 25 deletions

View File

@ -1458,6 +1458,10 @@ example:
This function attribute indicates that the function never returns This function attribute indicates that the function never returns
normally. This produces undefined behavior at runtime if the normally. This produces undefined behavior at runtime if the
function ever does dynamically return. 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`` ``norecurse``
This function attribute indicates that the function does not call itself This function attribute indicates that the function does not call itself
either directly or indirectly down any possible call path. This produces either directly or indirectly down any possible call path. This produces

View File

@ -603,6 +603,7 @@ enum AttributeKindCodes {
ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_OPT_FOR_FUZZING = 57,
ATTR_KIND_SHADOWCALLSTACK = 58, ATTR_KIND_SHADOWCALLSTACK = 58,
ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
ATTR_KIND_EXPECT_NO_RETURN = 60,
}; };
enum ComdatSelectionKindCodes { enum ComdatSelectionKindCodes {

View File

@ -106,6 +106,10 @@ def NoRedZone : EnumAttr<"noredzone">;
/// Mark the function as not returning. /// Mark the function as not returning.
def NoReturn : EnumAttr<"noreturn">; 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. /// Disable Indirect Branch Tracking.
def NoCfCheck : EnumAttr<"nocf_check">; def NoCfCheck : EnumAttr<"nocf_check">;

View File

@ -656,6 +656,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nonnull); KEYWORD(nonnull);
KEYWORD(noredzone); KEYWORD(noredzone);
KEYWORD(noreturn); KEYWORD(noreturn);
KEYWORD(expect_noreturn);
KEYWORD(nocf_check); KEYWORD(nocf_check);
KEYWORD(nounwind); KEYWORD(nounwind);
KEYWORD(optforfuzzing); KEYWORD(optforfuzzing);

View File

@ -1248,6 +1248,8 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break; case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break; case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); 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_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break; case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
@ -1611,6 +1613,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_nonlazybind: case lltok::kw_nonlazybind:
case lltok::kw_noredzone: case lltok::kw_noredzone:
case lltok::kw_noreturn: case lltok::kw_noreturn:
case lltok::kw_expect_noreturn:
case lltok::kw_nocf_check: case lltok::kw_nocf_check:
case lltok::kw_nounwind: case lltok::kw_nounwind:
case lltok::kw_optforfuzzing: case lltok::kw_optforfuzzing:
@ -1708,6 +1711,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_nonlazybind: case lltok::kw_nonlazybind:
case lltok::kw_noredzone: case lltok::kw_noredzone:
case lltok::kw_noreturn: case lltok::kw_noreturn:
case lltok::kw_expect_noreturn:
case lltok::kw_nocf_check: case lltok::kw_nocf_check:
case lltok::kw_nounwind: case lltok::kw_nounwind:
case lltok::kw_optforfuzzing: case lltok::kw_optforfuzzing:

View File

@ -200,6 +200,7 @@ enum Kind {
kw_nonnull, kw_nonnull,
kw_noredzone, kw_noredzone,
kw_noreturn, kw_noreturn,
kw_expect_noreturn,
kw_nocf_check, kw_nocf_check,
kw_nounwind, kw_nounwind,
kw_optforfuzzing, kw_optforfuzzing,

View File

@ -1186,8 +1186,8 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
case Attribute::NoCfCheck: return 1ULL << 57; case Attribute::NoCfCheck: return 1ULL << 57;
case Attribute::OptForFuzzing: return 1ULL << 58; case Attribute::OptForFuzzing: return 1ULL << 58;
case Attribute::ShadowCallStack: return 1ULL << 59; case Attribute::ShadowCallStack: return 1ULL << 59;
case Attribute::SpeculativeLoadHardening: case Attribute::SpeculativeLoadHardening: return 1ULL << 60;
return 1ULL << 60; case Attribute::ExpectNoReturn: return 1ULL << 61;
case Attribute::Dereferenceable: case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format"); llvm_unreachable("dereferenceable attribute not supported in raw format");
break; break;
@ -1366,6 +1366,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::NoRedZone; return Attribute::NoRedZone;
case bitc::ATTR_KIND_NO_RETURN: case bitc::ATTR_KIND_NO_RETURN:
return Attribute::NoReturn; return Attribute::NoReturn;
case bitc::ATTR_KIND_EXPECT_NO_RETURN:
return Attribute::ExpectNoReturn;
case bitc::ATTR_KIND_NOCF_CHECK: case bitc::ATTR_KIND_NOCF_CHECK:
return Attribute::NoCfCheck; return Attribute::NoCfCheck;
case bitc::ATTR_KIND_NO_UNWIND: case bitc::ATTR_KIND_NO_UNWIND:

View File

@ -654,6 +654,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_NO_RED_ZONE; return bitc::ATTR_KIND_NO_RED_ZONE;
case Attribute::NoReturn: case Attribute::NoReturn:
return bitc::ATTR_KIND_NO_RETURN; return bitc::ATTR_KIND_NO_RETURN;
case Attribute::ExpectNoReturn:
return bitc::ATTR_KIND_EXPECT_NO_RETURN;
case Attribute::NoCfCheck: case Attribute::NoCfCheck:
return bitc::ATTR_KIND_NOCF_CHECK; return bitc::ATTR_KIND_NOCF_CHECK;
case Attribute::NoUnwind: case Attribute::NoUnwind:

View File

@ -298,6 +298,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "noredzone"; return "noredzone";
if (hasAttribute(Attribute::NoReturn)) if (hasAttribute(Attribute::NoReturn))
return "noreturn"; return "noreturn";
if (hasAttribute(Attribute::ExpectNoReturn))
return "expect_noreturn";
if (hasAttribute(Attribute::NoCfCheck)) if (hasAttribute(Attribute::NoCfCheck))
return "nocf_check"; return "nocf_check";
if (hasAttribute(Attribute::NoRecurse)) if (hasAttribute(Attribute::NoRecurse))

View File

@ -1477,6 +1477,7 @@ void Verifier::visitModuleFlagCGProfileEntry(const MDOperand &MDO) {
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) { static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
switch (Kind) { switch (Kind) {
case Attribute::NoReturn: case Attribute::NoReturn:
case Attribute::ExpectNoReturn:
case Attribute::NoCfCheck: case Attribute::NoCfCheck:
case Attribute::NoUnwind: case Attribute::NoUnwind:
case Attribute::NoInline: case Attribute::NoInline:

View File

@ -41,6 +41,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
.Case("nonlazybind", Attribute::NonLazyBind) .Case("nonlazybind", Attribute::NonLazyBind)
.Case("noredzone", Attribute::NoRedZone) .Case("noredzone", Attribute::NoRedZone)
.Case("noreturn", Attribute::NoReturn) .Case("noreturn", Attribute::NoReturn)
.Case("expect_noreturn", Attribute::ExpectNoReturn)
.Case("nocf_check", Attribute::NoCfCheck) .Case("nocf_check", Attribute::NoCfCheck)
.Case("norecurse", Attribute::NoRecurse) .Case("norecurse", Attribute::NoRecurse)
.Case("nounwind", Attribute::NoUnwind) .Case("nounwind", Attribute::NoUnwind)

View File

@ -2568,7 +2568,8 @@ bool AddressSanitizer::runOnFunction(Function &F) {
if (CS) { if (CS) {
// A call inside BB. // A call inside BB.
TempsToInstrument.clear(); 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<CallInst>(&Inst)) if (CallInst *CI = dyn_cast<CallInst>(&Inst))
maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI);

View File

@ -779,6 +779,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::NoBuiltin: case Attribute::NoBuiltin:
case Attribute::NoCapture: case Attribute::NoCapture:
case Attribute::NoReturn: case Attribute::NoReturn:
case Attribute::ExpectNoReturn:
case Attribute::None: case Attribute::None:
case Attribute::NonNull: case Attribute::NonNull:
case Attribute::ReadNone: case Attribute::ReadNone:

View File

@ -204,7 +204,7 @@ define void @f34()
; CHECK: define void @f34() ; CHECK: define void @f34()
{ {
call void @nobuiltin() nobuiltin call void @nobuiltin() nobuiltin
; CHECK: call void @nobuiltin() #36 ; CHECK: call void @nobuiltin() #37
ret void; ret void;
} }
@ -351,6 +351,12 @@ define void @f59() shadowcallstack
ret void ret void
} }
; CHECK: define void @f60() #36
define void @f60() expect_noreturn
{
ret void
}
; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone } ; CHECK: attributes #2 = { readnone }
@ -387,4 +393,5 @@ define void @f59() shadowcallstack
; CHECK: attributes #33 = { speculatable } ; CHECK: attributes #33 = { speculatable }
; CHECK: attributes #34 = { sanitize_hwaddress } ; CHECK: attributes #34 = { sanitize_hwaddress }
; CHECK: attributes #35 = { shadowcallstack } ; CHECK: attributes #35 = { shadowcallstack }
; CHECK: attributes #36 = { nobuiltin } ; CHECK: attributes #36 = { expect_noreturn }
; CHECK: attributes #37 = { nobuiltin }

View File

@ -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 ; AddressSanitizer must insert __asan_handle_no_return
; before every noreturn call or invoke. ; 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 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" 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 { ; Instrument calls to noreturn functions (regardless of callsite)
entry: define i32 @Call1() sanitize_address {
call void @MyNoReturnFunc(i32 1) noreturn ; The call insn has noreturn attr. call void @NoReturnFunc()
; CHECK: @Call1
; CHECK: call void @__asan_handle_no_return
; CHECK-NEXT: call void @MyNoReturnFunc
; CHECK-NEXT: unreachable
unreachable unreachable
} }
; CHECK-LABEL: @Call1
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: call void @__asan_handle_no_return
; CHECK-NEXT: call void @MyNoReturnFunc ; CHECK-NEXT: call void @NoReturnFunc
; CHECK-NEXT: unreachable
; Instrument noreturn call sites (regardless of function)
define i32 @Call2() sanitize_address {
call void @NormalFunc() noreturn
unreachable 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(...) 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: entry:
invoke void @MyNoReturnFunc(i32 1) invoke void @NoReturnFunc()
to label %invoke.cont unwind label %lpad to label %invoke.cont unwind label %lpad
invoke.cont: invoke.cont:
@ -42,8 +50,8 @@ lpad:
filter [0 x i8*] zeroinitializer filter [0 x i8*] zeroinitializer
ret i64 1 ret i64 1
} }
; CHECK: @Invoke1 ; CHECK-LABEL: @Invoke1
; CHECK: call void @__asan_handle_no_return ; CHECK: call void @__asan_handle_no_return
; CHECK-NEXT: invoke void @MyNoReturnFunc ; CHECK-NEXT: invoke void @NoReturnFunc
; CHECK: ret i64 0 ; CHECK: ret i64 0
; CHECK: ret i64 1 ; CHECK: ret i64 1