llvm.org GIT mirror llvm / 2e1beed
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@352003 91177308-0d34-0410-b5e6-96231b3b80d8 Julian Lettner 1 year, 9 months ago
15 changed file(s) with 65 addition(s) and 25 deletion(s). Raw diff Collapse all Expand all
14571457 This function attribute indicates that the function never returns
14581458 normally. This produces undefined behavior at runtime if the
14591459 function ever does dynamically return.
1460 ``expect_noreturn``
1461 This function attribute indicates that the function is unlikely to return
1462 normally, but that it still allowed to do so. This is useful in cases where
1463 ``noreturn`` is too strong a guarantee.
14601464 ``norecurse``
14611465 This function attribute indicates that the function does not call itself
14621466 either directly or indirectly down any possible call path. This produces
602602 ATTR_KIND_OPT_FOR_FUZZING = 57,
603603 ATTR_KIND_SHADOWCALLSTACK = 58,
604604 ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
605 ATTR_KIND_EXPECT_NO_RETURN = 60,
605606 };
606607
607608 enum ComdatSelectionKindCodes {
104104
105105 /// Mark the function as not returning.
106106 def NoReturn : EnumAttr<"noreturn">;
107
108 /// Mark the function as unlikely to return. This is useful in cases where
109 /// `noreturn` is too strong a guarantee.
110 def ExpectNoReturn : EnumAttr<"expect_noreturn">;
107111
108112 /// Disable Indirect Branch Tracking.
109113 def NoCfCheck : EnumAttr<"nocf_check">;
655655 KEYWORD(nonnull);
656656 KEYWORD(noredzone);
657657 KEYWORD(noreturn);
658 KEYWORD(expect_noreturn);
658659 KEYWORD(nocf_check);
659660 KEYWORD(nounwind);
660661 KEYWORD(optforfuzzing);
12471247 case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
12481248 case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
12491249 case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break;
1250 case lltok::kw_expect_noreturn:
1251 B.addAttribute(Attribute::ExpectNoReturn); break;
12501252 case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
12511253 case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
12521254 case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
16101612 case lltok::kw_nonlazybind:
16111613 case lltok::kw_noredzone:
16121614 case lltok::kw_noreturn:
1615 case lltok::kw_expect_noreturn:
16131616 case lltok::kw_nocf_check:
16141617 case lltok::kw_nounwind:
16151618 case lltok::kw_optforfuzzing:
17071710 case lltok::kw_nonlazybind:
17081711 case lltok::kw_noredzone:
17091712 case lltok::kw_noreturn:
1713 case lltok::kw_expect_noreturn:
17101714 case lltok::kw_nocf_check:
17111715 case lltok::kw_nounwind:
17121716 case lltok::kw_optforfuzzing:
199199 kw_nonnull,
200200 kw_noredzone,
201201 kw_noreturn,
202 kw_expect_noreturn,
202203 kw_nocf_check,
203204 kw_nounwind,
204205 kw_optforfuzzing,
11851185 case Attribute::NoCfCheck: return 1ULL << 57;
11861186 case Attribute::OptForFuzzing: return 1ULL << 58;
11871187 case Attribute::ShadowCallStack: return 1ULL << 59;
1188 case Attribute::SpeculativeLoadHardening:
1189 return 1ULL << 60;
1188 case Attribute::SpeculativeLoadHardening: return 1ULL << 60;
1189 case Attribute::ExpectNoReturn: return 1ULL << 61;
11901190 case Attribute::Dereferenceable:
11911191 llvm_unreachable("dereferenceable attribute not supported in raw format");
11921192 break;
13651365 return Attribute::NoRedZone;
13661366 case bitc::ATTR_KIND_NO_RETURN:
13671367 return Attribute::NoReturn;
1368 case bitc::ATTR_KIND_EXPECT_NO_RETURN:
1369 return Attribute::ExpectNoReturn;
13681370 case bitc::ATTR_KIND_NOCF_CHECK:
13691371 return Attribute::NoCfCheck;
13701372 case bitc::ATTR_KIND_NO_UNWIND:
653653 return bitc::ATTR_KIND_NO_RED_ZONE;
654654 case Attribute::NoReturn:
655655 return bitc::ATTR_KIND_NO_RETURN;
656 case Attribute::ExpectNoReturn:
657 return bitc::ATTR_KIND_EXPECT_NO_RETURN;
656658 case Attribute::NoCfCheck:
657659 return bitc::ATTR_KIND_NOCF_CHECK;
658660 case Attribute::NoUnwind:
297297 return "noredzone";
298298 if (hasAttribute(Attribute::NoReturn))
299299 return "noreturn";
300 if (hasAttribute(Attribute::ExpectNoReturn))
301 return "expect_noreturn";
300302 if (hasAttribute(Attribute::NoCfCheck))
301303 return "nocf_check";
302304 if (hasAttribute(Attribute::NoRecurse))
14761476 static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
14771477 switch (Kind) {
14781478 case Attribute::NoReturn:
1479 case Attribute::ExpectNoReturn:
14791480 case Attribute::NoCfCheck:
14801481 case Attribute::NoUnwind:
14811482 case Attribute::NoInline:
4040 .Case("nonlazybind", Attribute::NonLazyBind)
4141 .Case("noredzone", Attribute::NoRedZone)
4242 .Case("noreturn", Attribute::NoReturn)
43 .Case("expect_noreturn", Attribute::ExpectNoReturn)
4344 .Case("nocf_check", Attribute::NoCfCheck)
4445 .Case("norecurse", Attribute::NoRecurse)
4546 .Case("nounwind", Attribute::NoUnwind)
25672567 if (CS) {
25682568 // A call inside BB.
25692569 TempsToInstrument.clear();
2570 if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction());
2570 if (CS.doesNotReturn() || CS.hasFnAttr(Attribute::ExpectNoReturn))
2571 NoReturnCalls.push_back(CS.getInstruction());
25712572 }
25722573 if (CallInst *CI = dyn_cast(&Inst))
25732574 maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI);
778778 case Attribute::NoBuiltin:
779779 case Attribute::NoCapture:
780780 case Attribute::NoReturn:
781 case Attribute::ExpectNoReturn:
781782 case Attribute::None:
782783 case Attribute::NonNull:
783784 case Attribute::ReadNone:
203203 ; CHECK: define void @f34()
204204 {
205205 call void @nobuiltin() nobuiltin
206 ; CHECK: call void @nobuiltin() #36
206 ; CHECK: call void @nobuiltin() #37
207207 ret void;
208208 }
209209
346346
347347 ; CHECK: define void @f59() #35
348348 define void @f59() shadowcallstack
349 {
350 ret void
351 }
352
353 ; CHECK: define void @f60() #36
354 define void @f60() expect_noreturn
349355 {
350356 ret void
351357 }
386392 ; CHECK: attributes #33 = { speculatable }
387393 ; CHECK: attributes #34 = { sanitize_hwaddress }
388394 ; CHECK: attributes #35 = { shadowcallstack }
389 ; CHECK: attributes #36 = { nobuiltin }
395 ; CHECK: attributes #36 = { expect_noreturn }
396 ; CHECK: attributes #37 = { nobuiltin }
None ; RUN: opt < %s -asan -asan-module -S | FileCheck %s
0 ; RUN: opt < %s -asan -S | FileCheck %s
11 ; AddressSanitizer must insert __asan_handle_no_return
22 ; before every noreturn call or invoke.
33
44 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"
55 target triple = "x86_64-unknown-linux-gnu"
66
7 declare void @MyNoReturnFunc(i32) noreturn
7 declare void @NormalFunc()
8 declare void @NoReturnFunc() noreturn
89
9 define i32 @Call1(i8* nocapture %arg) uwtable sanitize_address {
10 entry:
11 call void @MyNoReturnFunc(i32 1) noreturn ; The call insn has noreturn attr.
12 ; CHECK: @Call1
13 ; CHECK: call void @__asan_handle_no_return
14 ; CHECK-NEXT: call void @MyNoReturnFunc
15 ; CHECK-NEXT: unreachable
10 ; Instrument calls to noreturn functions (regardless of callsite)
11 define i32 @Call1() sanitize_address {
12 call void @NoReturnFunc()
1613 unreachable
1714 }
15 ; CHECK-LABEL: @Call1
16 ; CHECK: call void @__asan_handle_no_return
17 ; CHECK-NEXT: call void @NoReturnFunc
1818
19 define i32 @Call2(i8* nocapture %arg) uwtable sanitize_address {
20 entry:
21 call void @MyNoReturnFunc(i32 1) ; No noreturn attribure on the call.
22 ; CHECK: @Call2
23 ; CHECK: call void @__asan_handle_no_return
24 ; CHECK-NEXT: call void @MyNoReturnFunc
25 ; CHECK-NEXT: unreachable
19 ; Instrument noreturn call sites (regardless of function)
20 define i32 @Call2() sanitize_address {
21 call void @NormalFunc() noreturn
2622 unreachable
2723 }
24 ; CHECK-LABEL: @Call2
25 ; CHECK: call void @__asan_handle_no_return
26 ; CHECK-NEXT: call void @NormalFunc
27
28 ; Also instrument expect_noreturn call sites
29 define i32 @Call3() sanitize_address {
30 call void @NormalFunc() expect_noreturn
31 ret i32 0
32 }
33 ; CHECK-LABEL: @Call3
34 ; CHECK: call void @__asan_handle_no_return
35 ; CHECK-NEXT: call void @NormalFunc
2836
2937 declare i32 @__gxx_personality_v0(...)
3038
31 define i64 @Invoke1(i8** %esc) nounwind uwtable ssp sanitize_address personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
39 define i64 @Invoke1(i8** %esc) sanitize_address personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
3240 entry:
33 invoke void @MyNoReturnFunc(i32 1)
41 invoke void @NoReturnFunc()
3442 to label %invoke.cont unwind label %lpad
3543
3644 invoke.cont:
4149 filter [0 x i8*] zeroinitializer
4250 ret i64 1
4351 }
44 ; CHECK: @Invoke1
52 ; CHECK-LABEL: @Invoke1
4553 ; CHECK: call void @__asan_handle_no_return
46 ; CHECK-NEXT: invoke void @MyNoReturnFunc
54 ; CHECK-NEXT: invoke void @NoReturnFunc
4755 ; CHECK: ret i64 0
4856 ; CHECK: ret i64 1