| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 1 | //===-- asan_linux.cc -----------------------------------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file is a part of AddressSanitizer, an address sanity checker. |
| 11 | // |
| 12 | // Posix-specific details. |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | #if defined(__linux__) || defined(__APPLE__) |
| 15 | |
| 16 | #include "asan_internal.h" |
| 17 | #include "asan_interceptors.h" |
| Evgeniy Stepanov | c99f700 | 2012-05-23 15:21:50 +0000 | [diff] [blame] | 18 | #include "asan_mapping.h" |
| Alexey Samsonov | 7354509 | 2012-08-09 07:40:58 +0000 | [diff] [blame] | 19 | #include "asan_report.h" |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 20 | #include "asan_stack.h" |
| 21 | #include "asan_thread_registry.h" |
| Alexey Samsonov | 2221f55 | 2012-06-05 08:48:10 +0000 | [diff] [blame] | 22 | #include "sanitizer_common/sanitizer_libc.h" |
| Alexey Samsonov | 6895adc | 2012-06-07 06:15:12 +0000 | [diff] [blame] | 23 | #include "sanitizer_common/sanitizer_procmaps.h" |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 24 | |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 25 | #include <pthread.h> |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 26 | #include <signal.h> |
| Alexey Samsonov | b823e3c | 2012-02-22 14:07:06 +0000 | [diff] [blame] | 27 | #include <stdlib.h> |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 28 | #include <sys/time.h> |
| 29 | #include <sys/resource.h> |
| Alexey Samsonov | 0870028 | 2012-11-23 09:46:34 +0000 | [diff] [blame^] | 30 | #include <ucontext.h> |
| Kostya Serebryany | 0ecf5eb | 2012-01-09 23:11:26 +0000 | [diff] [blame] | 31 | #include <unistd.h> |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 32 | |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 33 | static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 34 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 35 | namespace __asan { |
| 36 | |
| 37 | static void MaybeInstallSigaction(int signum, |
| 38 | void (*handler)(int, siginfo_t *, void *)) { |
| 39 | if (!AsanInterceptsSignal(signum)) |
| 40 | return; |
| 41 | struct sigaction sigact; |
| Alexey Samsonov | 09672ca | 2012-02-08 13:45:31 +0000 | [diff] [blame] | 42 | REAL(memset)(&sigact, 0, sizeof(sigact)); |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 43 | sigact.sa_sigaction = handler; |
| 44 | sigact.sa_flags = SA_SIGINFO; |
| Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 45 | if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; |
| Alexey Samsonov | 09672ca | 2012-02-08 13:45:31 +0000 | [diff] [blame] | 46 | CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); |
| Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 47 | if (flags()->verbosity >= 1) { |
| Alexander Potapenko | a87bdaa | 2012-05-30 15:29:11 +0000 | [diff] [blame] | 48 | Report("Installed the sigaction for signal %d\n", signum); |
| 49 | } |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 53 | uptr addr = (uptr)siginfo->si_addr; |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 54 | // Write the first message using the bullet-proof write. |
| Alexey Samsonov | 47657ce | 2012-06-06 07:02:44 +0000 | [diff] [blame] | 55 | if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die(); |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 56 | uptr pc, sp, bp; |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 57 | GetPcSpBp(context, &pc, &sp, &bp); |
| Alexey Samsonov | 7354509 | 2012-08-09 07:40:58 +0000 | [diff] [blame] | 58 | ReportSIGSEGV(pc, sp, bp, addr); |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 59 | } |
| 60 | |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 61 | void SetAlternateSignalStack() { |
| 62 | stack_t altstack, oldstack; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 63 | CHECK(0 == sigaltstack(0, &oldstack)); |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 64 | // If the alternate stack is already in place, do nothing. |
| 65 | if ((oldstack.ss_flags & SS_DISABLE) == 0) return; |
| 66 | // TODO(glider): the mapped stack should have the MAP_STACK flag in the |
| 67 | // future. It is not required by man 2 sigaltstack now (they're using |
| 68 | // malloc()). |
| Alexey Samsonov | a25b346 | 2012-06-06 16:15:07 +0000 | [diff] [blame] | 69 | void* base = MmapOrDie(kAltStackSize, __FUNCTION__); |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 70 | altstack.ss_sp = base; |
| 71 | altstack.ss_flags = 0; |
| 72 | altstack.ss_size = kAltStackSize; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 73 | CHECK(0 == sigaltstack(&altstack, 0)); |
| Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 74 | if (flags()->verbosity > 0) { |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 75 | Report("Alternative stack for T%d set: [%p,%p)\n", |
| Kostya Serebryany | e0cff0b | 2012-06-06 15:06:58 +0000 | [diff] [blame] | 76 | asanThreadRegistry().GetCurrentTidOrInvalid(), |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 77 | altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | void UnsetAlternateSignalStack() { |
| 82 | stack_t altstack, oldstack; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 83 | altstack.ss_sp = 0; |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 84 | altstack.ss_flags = SS_DISABLE; |
| 85 | altstack.ss_size = 0; |
| 86 | CHECK(0 == sigaltstack(&altstack, &oldstack)); |
| Alexey Samsonov | a25b346 | 2012-06-06 16:15:07 +0000 | [diff] [blame] | 87 | UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 88 | } |
| 89 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 90 | void InstallSignalHandlers() { |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 91 | // Set the alternate signal stack for the main thread. |
| 92 | // This will cause SetAlternateSignalStack to be called twice, but the stack |
| 93 | // will be actually set only once. |
| Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 94 | if (flags()->use_sigaltstack) SetAlternateSignalStack(); |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 95 | MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV); |
| 96 | MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); |
| 97 | } |
| 98 | |
| Alexey Samsonov | 0870028 | 2012-11-23 09:46:34 +0000 | [diff] [blame^] | 99 | void ClearShadowMemoryForContext(void *context) { |
| 100 | ucontext_t *ucp = (ucontext_t*)context; |
| 101 | uptr sp = (uptr)ucp->uc_stack.ss_sp; |
| 102 | uptr size = ucp->uc_stack.ss_size; |
| 103 | // Align to page size. |
| 104 | uptr bottom = sp & ~(kPageSize - 1); |
| 105 | size += sp - bottom; |
| 106 | size = RoundUpTo(size, kPageSize); |
| 107 | PoisonShadow(bottom, size, 0); |
| 108 | } |
| 109 | |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 110 | // ---------------------- TSD ---------------- {{{1 |
| 111 | |
| 112 | static pthread_key_t tsd_key; |
| 113 | static bool tsd_key_inited = false; |
| Kostya Serebryany | f58f998 | 2012-02-07 00:27:15 +0000 | [diff] [blame] | 114 | void AsanTSDInit(void (*destructor)(void *tsd)) { |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 115 | CHECK(!tsd_key_inited); |
| 116 | tsd_key_inited = true; |
| Kostya Serebryany | f58f998 | 2012-02-07 00:27:15 +0000 | [diff] [blame] | 117 | CHECK(0 == pthread_key_create(&tsd_key, destructor)); |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | void *AsanTSDGet() { |
| 121 | CHECK(tsd_key_inited); |
| 122 | return pthread_getspecific(tsd_key); |
| 123 | } |
| 124 | |
| 125 | void AsanTSDSet(void *tsd) { |
| 126 | CHECK(tsd_key_inited); |
| 127 | pthread_setspecific(tsd_key, tsd); |
| 128 | } |
| 129 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 130 | } // namespace __asan |
| 131 | |
| 132 | #endif // __linux__ || __APPLE_ |