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:
parent
a5aba5c47a
commit
c814a6a425
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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">;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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))
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user