brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.3 KiB · 8e7d508 Raw
168 lines · cpp
1// Tests that __asan_handle_no_return properly unpoisons the signal alternate2// stack.3 4// Don't optimize, otherwise the variables which create redzones might be5// dropped.6// RUN: %clangxx_asan -fexceptions -O0 %s -o %t -pthread7// RUN: %env_asan_opts=detect_stack_use_after_return=0 %run %t8 9#include <algorithm>10#include <cassert>11#include <cerrno>12#include <csetjmp>13#include <cstdint>14#include <cstdio>15#include <cstdlib>16#include <cstring>17 18#include <limits.h>19#include <pthread.h>20#include <signal.h>21#include <sys/mman.h>22#include <unistd.h>23 24#include <sanitizer/asan_interface.h>25 26namespace {27 28struct TestContext {29  char *LeftRedzone;30  char *RightRedzone;31  std::jmp_buf JmpBuf;32};33 34TestContext defaultStack;35TestContext signalStack;36 37// Create a new stack frame to ensure that logically, the stack frame should be38// 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.40template <class Jump>41void __attribute__((noinline)) poisonStackAndJump(TestContext &c, Jump jump) {42  char Blob[100]; // This variable must not be optimized out, because we use it43                  // to create redzones.44 45  c.LeftRedzone = Blob - 1;46  c.RightRedzone = Blob + sizeof(Blob);47 48  assert(__asan_address_is_poisoned(c.LeftRedzone));49  assert(__asan_address_is_poisoned(c.RightRedzone));50 51  // Jump to avoid normal cleanup of redzone markers. Instead,52  // __asan_handle_no_return is called which unpoisons the stacks.53  jump();54}55 56void testOnCurrentStack() {57  TestContext c;58 59  if (0 == setjmp(c.JmpBuf))60    poisonStackAndJump(c, [&] { longjmp(c.JmpBuf, 1); });61 62  assert(0 == __asan_region_is_poisoned(c.LeftRedzone,63                                        c.RightRedzone - c.LeftRedzone));64}65 66bool isOnSignalStack() {67  stack_t Stack;68  sigaltstack(nullptr, &Stack);69  return Stack.ss_flags == SS_ONSTACK;70}71 72void signalHandler(int, siginfo_t *, void *) {73  assert(isOnSignalStack());74 75  // test on signal alternate stack76  testOnCurrentStack();77 78  // test unpoisoning when jumping between stacks79  poisonStackAndJump(signalStack, [] { longjmp(defaultStack.JmpBuf, 1); });80}81 82void setSignalAlternateStack(void *AltStack) {83  sigaltstack((stack_t const *)AltStack, nullptr);84 85  struct sigaction Action = {};86  Action.sa_sigaction = signalHandler;87  Action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;88  sigemptyset(&Action.sa_mask);89 90  sigaction(SIGUSR1, &Action, nullptr);91}92 93// Main test function.94// Must be run on another thread to be able to control memory placement between95// default stack and alternate signal stack.96// If the alternate signal stack is placed in close proximity before the97// default stack, __asan_handle_no_return might unpoison both, even without98// being aware of the signal alternate stack.99// We want to test reliably that __asan_handle_no_return can properly unpoison100// the signal alternate stack.101void *threadFun(void *AltStack) {102  // first test on default stack (sanity check), no signal alternate stack set103  testOnCurrentStack();104 105  setSignalAlternateStack(AltStack);106 107  // test on default stack again, but now the signal alternate stack is set108  testOnCurrentStack();109 110  // set up jump to test unpoisoning when jumping between stacks111  if (0 == setjmp(defaultStack.JmpBuf))112    // Test on signal alternate stack, via signalHandler113    poisonStackAndJump(defaultStack, [] { raise(SIGUSR1); });114 115  assert(!isOnSignalStack());116 117  assert(0 == __asan_region_is_poisoned(118                  defaultStack.LeftRedzone,119                  defaultStack.RightRedzone - defaultStack.LeftRedzone));120 121  assert(0 == __asan_region_is_poisoned(122                  signalStack.LeftRedzone,123                  signalStack.RightRedzone - signalStack.LeftRedzone));124 125  return nullptr;126}127 128} // namespace129 130// Check that __asan_handle_no_return properly unpoisons a signal alternate131// stack.132// __asan_handle_no_return tries to determine the stack boundaries and133// unpoisons all memory inside those. If this is not done properly, redzones for134// variables on can remain in shadow memory which might lead to false positive135// reports when the stack is reused.136int main() {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,150                             -1, 0);151 152  stack_t AltStack = {};153  AltStack.ss_sp = (char *)Mapping + DefaultStackSize;154  AltStack.ss_flags = 0;155  AltStack.ss_size = AltStackSize;156 157  pthread_t Thread;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);163 164  pthread_join(Thread, nullptr);165 166  munmap(Mapping, MappingSize);167}168