1 // Tests that __asan_handle_no_return properly unpoisons the signal alternate
4 // Don't optimize, otherwise the variables which create redzones might be
6 // RUN: %clangxx_asan -fexceptions -O0 %s -o %t -pthread
7 // RUN: %env_asan_opts=detect_stack_use_after_return=0 %run %t
24 #include <sanitizer/asan_interface.h>
34 TestContext defaultStack
;
35 TestContext signalStack
;
37 // Create a new stack frame to ensure that logically, the stack frame should be
38 // unpoisoned when the function exits. Exit is performed via jump, not return,
39 // such that we trigger __asan_handle_no_return and not ordinary unpoisoning.
41 void __attribute__((noinline
)) poisonStackAndJump(TestContext
&c
, Jump jump
) {
42 char Blob
[100]; // This variable must not be optimized out, because we use it
43 // to create redzones.
45 c
.LeftRedzone
= Blob
- 1;
46 c
.RightRedzone
= Blob
+ sizeof(Blob
);
48 assert(__asan_address_is_poisoned(c
.LeftRedzone
));
49 assert(__asan_address_is_poisoned(c
.RightRedzone
));
51 // Jump to avoid normal cleanup of redzone markers. Instead,
52 // __asan_handle_no_return is called which unpoisons the stacks.
56 void testOnCurrentStack() {
59 if (0 == setjmp(c
.JmpBuf
))
60 poisonStackAndJump(c
, [&] { longjmp(c
.JmpBuf
, 1); });
62 assert(0 == __asan_region_is_poisoned(c
.LeftRedzone
,
63 c
.RightRedzone
- c
.LeftRedzone
));
66 bool isOnSignalStack() {
68 sigaltstack(nullptr, &Stack
);
69 return Stack
.ss_flags
== SS_ONSTACK
;
72 void signalHandler(int, siginfo_t
*, void *) {
73 assert(isOnSignalStack());
75 // test on signal alternate stack
78 // test unpoisoning when jumping between stacks
79 poisonStackAndJump(signalStack
, [] { longjmp(defaultStack
.JmpBuf
, 1); });
82 void setSignalAlternateStack(void *AltStack
) {
83 sigaltstack((stack_t
const *)AltStack
, nullptr);
85 struct sigaction Action
= {};
86 Action
.sa_sigaction
= signalHandler
;
87 Action
.sa_flags
= SA_SIGINFO
| SA_NODEFER
| SA_ONSTACK
;
88 sigemptyset(&Action
.sa_mask
);
90 sigaction(SIGUSR1
, &Action
, nullptr);
93 // Main test function.
94 // Must be run on another thread to be able to control memory placement between
95 // default stack and alternate signal stack.
96 // If the alternate signal stack is placed in close proximity before the
97 // default stack, __asan_handle_no_return might unpoison both, even without
98 // being aware of the signal alternate stack.
99 // We want to test reliably that __asan_handle_no_return can properly unpoison
100 // the signal alternate stack.
101 void *threadFun(void *AltStack
) {
102 // first test on default stack (sanity check), no signal alternate stack set
103 testOnCurrentStack();
105 setSignalAlternateStack(AltStack
);
107 // test on default stack again, but now the signal alternate stack is set
108 testOnCurrentStack();
110 // set up jump to test unpoisoning when jumping between stacks
111 if (0 == setjmp(defaultStack
.JmpBuf
))
112 // Test on signal alternate stack, via signalHandler
113 poisonStackAndJump(defaultStack
, [] { raise(SIGUSR1
); });
115 assert(!isOnSignalStack());
117 assert(0 == __asan_region_is_poisoned(
118 defaultStack
.LeftRedzone
,
119 defaultStack
.RightRedzone
- defaultStack
.LeftRedzone
));
121 assert(0 == __asan_region_is_poisoned(
122 signalStack
.LeftRedzone
,
123 signalStack
.RightRedzone
- signalStack
.LeftRedzone
));
130 // Check that __asan_handle_no_return properly unpoisons a signal alternate
132 // __asan_handle_no_return tries to determine the stack boundaries and
133 // unpoisons all memory inside those. If this is not done properly, redzones for
134 // variables on can remain in shadow memory which might lead to false positive
135 // reports when the stack is reused.
137 size_t const PageSize
= sysconf(_SC_PAGESIZE
);
138 // The Solaris defaults of 4k (32-bit) and 8k (64-bit) are too small.
139 size_t const MinStackSize
= std::max
<size_t>(PTHREAD_STACK_MIN
, 16 * 1024);
140 // To align the alternate stack, we round this up to page_size.
141 size_t const DefaultStackSize
=
142 (MinStackSize
- 1 + PageSize
) & ~(PageSize
- 1);
143 // The alternate stack needs a certain size, or the signal handler segfaults.
144 size_t const AltStackSize
= 10 * PageSize
;
145 size_t const MappingSize
= DefaultStackSize
+ AltStackSize
;
146 // Using mmap guarantees proper alignment.
147 void *const Mapping
= mmap(nullptr, MappingSize
,
148 PROT_READ
| PROT_WRITE
,
149 MAP_PRIVATE
| MAP_ANONYMOUS
,
152 stack_t AltStack
= {};
153 AltStack
.ss_sp
= (char *)Mapping
+ DefaultStackSize
;
154 AltStack
.ss_flags
= 0;
155 AltStack
.ss_size
= AltStackSize
;
158 pthread_attr_t ThreadAttr
;
159 pthread_attr_init(&ThreadAttr
);
160 pthread_attr_setstack(&ThreadAttr
, Mapping
, DefaultStackSize
);
161 pthread_create(&Thread
, &ThreadAttr
, &threadFun
, (void *)&AltStack
);
162 pthread_attr_destroy(&ThreadAttr
);
164 pthread_join(Thread
, nullptr);
166 munmap(Mapping
, MappingSize
);