| 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" |
| Alexander Potapenko | 99d17eb | 2012-02-22 09:11:55 +0000 | [diff] [blame] | 19 | #include "asan_procmaps.h" |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 20 | #include "asan_stack.h" |
| 21 | #include "asan_thread_registry.h" |
| 22 | |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 23 | #include <pthread.h> |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 24 | #include <signal.h> |
| Alexey Samsonov | b823e3c | 2012-02-22 14:07:06 +0000 | [diff] [blame] | 25 | #include <stdlib.h> |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 26 | #include <sys/time.h> |
| 27 | #include <sys/resource.h> |
| Kostya Serebryany | 0ecf5eb | 2012-01-09 23:11:26 +0000 | [diff] [blame] | 28 | #include <unistd.h> |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 29 | |
| Kostya Serebryany | dde7c33 | 2012-01-11 02:39:16 +0000 | [diff] [blame] | 30 | #ifdef ANDROID |
| 31 | #include <sys/atomics.h> |
| 32 | #endif |
| 33 | |
| Kostya Serebryany | 25c7178 | 2012-03-10 01:30:01 +0000 | [diff] [blame] | 34 | // Should not add dependency on libstdc++, |
| 35 | // since most of the stuff here is inlinable. |
| 36 | #include <algorithm> |
| 37 | |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 38 | static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 39 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 40 | namespace __asan { |
| 41 | |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 42 | static inline bool IntervalsAreSeparate(uptr start1, uptr end1, |
| 43 | uptr start2, uptr end2) { |
| Evgeniy Stepanov | c99f700 | 2012-05-23 15:21:50 +0000 | [diff] [blame] | 44 | CHECK(start1 <= end1); |
| 45 | CHECK(start2 <= end2); |
| 46 | return (end1 < start2) || (end2 < start1); |
| 47 | } |
| 48 | |
| 49 | // FIXME: this is thread-unsafe, but should not cause problems most of the time. |
| 50 | // When the shadow is mapped only a single thread usually exists (plus maybe |
| 51 | // several worker threads on Mac, which aren't expected to map big chunks of |
| 52 | // memory). |
| 53 | bool AsanShadowRangeIsAvailable() { |
| 54 | AsanProcMaps procmaps; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 55 | uptr start, end; |
| 56 | uptr shadow_start = kLowShadowBeg; |
| Evgeniy Stepanov | c99f700 | 2012-05-23 15:21:50 +0000 | [diff] [blame] | 57 | if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 58 | uptr shadow_end = kHighShadowEnd; |
| Evgeniy Stepanov | c99f700 | 2012-05-23 15:21:50 +0000 | [diff] [blame] | 59 | while (procmaps.Next(&start, &end, |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 60 | /*offset*/0, /*filename*/0, /*filename_size*/0)) { |
| Evgeniy Stepanov | c99f700 | 2012-05-23 15:21:50 +0000 | [diff] [blame] | 61 | if (!IntervalsAreSeparate(start, end, shadow_start, shadow_end)) |
| 62 | return false; |
| 63 | } |
| 64 | return true; |
| 65 | } |
| 66 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 67 | static void MaybeInstallSigaction(int signum, |
| 68 | void (*handler)(int, siginfo_t *, void *)) { |
| 69 | if (!AsanInterceptsSignal(signum)) |
| 70 | return; |
| 71 | struct sigaction sigact; |
| Alexey Samsonov | 09672ca | 2012-02-08 13:45:31 +0000 | [diff] [blame] | 72 | REAL(memset)(&sigact, 0, sizeof(sigact)); |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 73 | sigact.sa_sigaction = handler; |
| 74 | sigact.sa_flags = SA_SIGINFO; |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 75 | if (FLAG_use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; |
| Alexey Samsonov | 09672ca | 2012-02-08 13:45:31 +0000 | [diff] [blame] | 76 | CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); |
| Alexander Potapenko | a87bdaa | 2012-05-30 15:29:11 +0000 | [diff] [blame] | 77 | if (FLAG_v >= 1) { |
| 78 | Report("Installed the sigaction for signal %d\n", signum); |
| 79 | } |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 83 | uptr addr = (uptr)siginfo->si_addr; |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 84 | // Write the first message using the bullet-proof write. |
| Kostya Serebryany | 0ecf5eb | 2012-01-09 23:11:26 +0000 | [diff] [blame] | 85 | if (13 != AsanWrite(2, "ASAN:SIGSEGV\n", 13)) AsanDie(); |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 86 | uptr pc, sp, bp; |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 87 | GetPcSpBp(context, &pc, &sp, &bp); |
| 88 | Report("ERROR: AddressSanitizer crashed on unknown address %p" |
| 89 | " (pc %p sp %p bp %p T%d)\n", |
| 90 | addr, pc, sp, bp, |
| 91 | asanThreadRegistry().GetCurrentTidOrMinusOne()); |
| 92 | Printf("AddressSanitizer can not provide additional info. ABORTING\n"); |
| Evgeniy Stepanov | 9cfa194 | 2012-01-19 11:34:18 +0000 | [diff] [blame] | 93 | GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 94 | stack.PrintStack(); |
| 95 | ShowStatsAndAbort(); |
| 96 | } |
| 97 | |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 98 | void SetAlternateSignalStack() { |
| 99 | stack_t altstack, oldstack; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 100 | CHECK(0 == sigaltstack(0, &oldstack)); |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 101 | // If the alternate stack is already in place, do nothing. |
| 102 | if ((oldstack.ss_flags & SS_DISABLE) == 0) return; |
| 103 | // TODO(glider): the mapped stack should have the MAP_STACK flag in the |
| 104 | // future. It is not required by man 2 sigaltstack now (they're using |
| 105 | // malloc()). |
| 106 | void* base = AsanMmapSomewhereOrDie(kAltStackSize, __FUNCTION__); |
| 107 | altstack.ss_sp = base; |
| 108 | altstack.ss_flags = 0; |
| 109 | altstack.ss_size = kAltStackSize; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 110 | CHECK(0 == sigaltstack(&altstack, 0)); |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 111 | if (FLAG_v > 0) { |
| 112 | Report("Alternative stack for T%d set: [%p,%p)\n", |
| 113 | asanThreadRegistry().GetCurrentTidOrMinusOne(), |
| 114 | altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | void UnsetAlternateSignalStack() { |
| 119 | stack_t altstack, oldstack; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 120 | altstack.ss_sp = 0; |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 121 | altstack.ss_flags = SS_DISABLE; |
| 122 | altstack.ss_size = 0; |
| 123 | CHECK(0 == sigaltstack(&altstack, &oldstack)); |
| 124 | AsanUnmapOrDie(oldstack.ss_sp, oldstack.ss_size); |
| 125 | } |
| 126 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 127 | void InstallSignalHandlers() { |
| Alexander Potapenko | f03d8af | 2012-04-05 10:54:52 +0000 | [diff] [blame] | 128 | // Set the alternate signal stack for the main thread. |
| 129 | // This will cause SetAlternateSignalStack to be called twice, but the stack |
| 130 | // will be actually set only once. |
| 131 | if (FLAG_use_sigaltstack) SetAlternateSignalStack(); |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 132 | MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV); |
| 133 | MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); |
| 134 | } |
| 135 | |
| 136 | void AsanDisableCoreDumper() { |
| 137 | struct rlimit nocore; |
| 138 | nocore.rlim_cur = 0; |
| 139 | nocore.rlim_max = 0; |
| 140 | setrlimit(RLIMIT_CORE, &nocore); |
| 141 | } |
| 142 | |
| Alexander Potapenko | 99d17eb | 2012-02-22 09:11:55 +0000 | [diff] [blame] | 143 | void AsanDumpProcessMap() { |
| 144 | AsanProcMaps proc_maps; |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 145 | uptr start, end; |
| Alexander Potapenko | 99d17eb | 2012-02-22 09:11:55 +0000 | [diff] [blame] | 146 | const intptr_t kBufSize = 4095; |
| 147 | char filename[kBufSize]; |
| 148 | Report("Process memory map follows:\n"); |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 149 | while (proc_maps.Next(&start, &end, /* file_offset */0, |
| Alexander Potapenko | 99d17eb | 2012-02-22 09:11:55 +0000 | [diff] [blame] | 150 | filename, kBufSize)) { |
| 151 | Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); |
| 152 | } |
| 153 | Report("End of process memory map.\n"); |
| 154 | } |
| 155 | |
| Kostya Serebryany | 0ecf5eb | 2012-01-09 23:11:26 +0000 | [diff] [blame] | 156 | int GetPid() { |
| 157 | return getpid(); |
| 158 | } |
| 159 | |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 160 | uptr GetThreadSelf() { |
| 161 | return (uptr)pthread_self(); |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 162 | } |
| 163 | |
| Kostya Serebryany | e1fe0fd | 2012-02-13 21:24:29 +0000 | [diff] [blame] | 164 | void SleepForSeconds(int seconds) { |
| 165 | sleep(seconds); |
| 166 | } |
| 167 | |
| 168 | void Exit(int exitcode) { |
| Timur Iskhodzhanov | b55c88d | 2012-03-13 16:29:25 +0000 | [diff] [blame] | 169 | _exit(exitcode); |
| Kostya Serebryany | e1fe0fd | 2012-02-13 21:24:29 +0000 | [diff] [blame] | 170 | } |
| 171 | |
| Kostya Serebryany | f8e6fee | 2012-04-06 01:27:11 +0000 | [diff] [blame] | 172 | void Abort() { |
| 173 | abort(); |
| 174 | } |
| 175 | |
| Alexey Samsonov | b823e3c | 2012-02-22 14:07:06 +0000 | [diff] [blame] | 176 | int Atexit(void (*function)(void)) { |
| 177 | return atexit(function); |
| 178 | } |
| 179 | |
| Kostya Serebryany | dde7c33 | 2012-01-11 02:39:16 +0000 | [diff] [blame] | 180 | int AtomicInc(int *a) { |
| 181 | #ifdef ANDROID |
| 182 | return __atomic_inc(a) + 1; |
| 183 | #else |
| 184 | return __sync_add_and_fetch(a, 1); |
| 185 | #endif |
| 186 | } |
| 187 | |
| Kostya Serebryany | f1e82b8 | 2012-04-05 15:55:09 +0000 | [diff] [blame] | 188 | uint16_t AtomicExchange(uint16_t *a, uint16_t new_val) { |
| 189 | return __sync_lock_test_and_set(a, new_val); |
| 190 | } |
| 191 | |
| Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame^] | 192 | void SortArray(uptr *array, uptr size) { |
| Kostya Serebryany | 25c7178 | 2012-03-10 01:30:01 +0000 | [diff] [blame] | 193 | std::sort(array, array + size); |
| 194 | } |
| 195 | |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 196 | // ---------------------- TSD ---------------- {{{1 |
| 197 | |
| 198 | static pthread_key_t tsd_key; |
| 199 | static bool tsd_key_inited = false; |
| Kostya Serebryany | f58f998 | 2012-02-07 00:27:15 +0000 | [diff] [blame] | 200 | void AsanTSDInit(void (*destructor)(void *tsd)) { |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 201 | CHECK(!tsd_key_inited); |
| 202 | tsd_key_inited = true; |
| Kostya Serebryany | f58f998 | 2012-02-07 00:27:15 +0000 | [diff] [blame] | 203 | CHECK(0 == pthread_key_create(&tsd_key, destructor)); |
| Kostya Serebryany | cc4e686 | 2012-01-11 02:21:06 +0000 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | void *AsanTSDGet() { |
| 207 | CHECK(tsd_key_inited); |
| 208 | return pthread_getspecific(tsd_key); |
| 209 | } |
| 210 | |
| 211 | void AsanTSDSet(void *tsd) { |
| 212 | CHECK(tsd_key_inited); |
| 213 | pthread_setspecific(tsd_key, tsd); |
| 214 | } |
| 215 | |
| Kostya Serebryany | a7e760a | 2012-01-09 19:18:27 +0000 | [diff] [blame] | 216 | } // namespace __asan |
| 217 | |
| 218 | #endif // __linux__ || __APPLE_ |