]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/hwasan/hwasan_linux.cc
Upgrade Unbound to 1.6.4. More to follow.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / hwasan / hwasan_linux.cc
1 //===-- hwasan_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 HWAddressSanitizer.
11 //
12 // Linux-, NetBSD- and FreeBSD-specific code.
13 //===----------------------------------------------------------------------===//
14
15 #include "sanitizer_common/sanitizer_platform.h"
16 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
17
18 #include "hwasan.h"
19 #include "hwasan_thread.h"
20
21 #include <elf.h>
22 #include <link.h>
23 #include <pthread.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <signal.h>
27 #include <unistd.h>
28 #include <unwind.h>
29 #include <sys/time.h>
30 #include <sys/resource.h>
31
32 #include "sanitizer_common/sanitizer_common.h"
33 #include "sanitizer_common/sanitizer_procmaps.h"
34
35 namespace __hwasan {
36
37 void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
38   CHECK_EQ((beg % GetMmapGranularity()), 0);
39   CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
40   uptr size = end - beg + 1;
41   DecreaseTotalMmap(size);  // Don't count the shadow against mmap_limit_mb.
42   void *res = MmapFixedNoReserve(beg, size, name);
43   if (res != (void *)beg) {
44     Report(
45         "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
46         "Perhaps you're using ulimit -v\n",
47         size);
48     Abort();
49   }
50   if (common_flags()->no_huge_pages_for_shadow) NoHugePagesInRegion(beg, size);
51   if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size);
52 }
53
54 static void ProtectGap(uptr addr, uptr size) {
55   void *res = MmapFixedNoAccess(addr, size, "shadow gap");
56   if (addr == (uptr)res) return;
57   // A few pages at the start of the address space can not be protected.
58   // But we really want to protect as much as possible, to prevent this memory
59   // being returned as a result of a non-FIXED mmap().
60   if (addr == 0) {
61     uptr step = GetMmapGranularity();
62     while (size > step) {
63       addr += step;
64       size -= step;
65       void *res = MmapFixedNoAccess(addr, size, "shadow gap");
66       if (addr == (uptr)res) return;
67     }
68   }
69
70   Report(
71       "ERROR: Failed to protect the shadow gap. "
72       "ASan cannot proceed correctly. ABORTING.\n");
73   DumpProcessMap();
74   Die();
75 }
76
77 bool InitShadow() {
78   const uptr maxVirtualAddress = GetMaxUserVirtualAddress();
79
80   // LowMem covers as much of the first 4GB as possible.
81   const uptr kLowMemEnd = 1UL<<32;
82   const uptr kLowShadowEnd = kLowMemEnd >> kShadowScale;
83   const uptr kLowShadowStart = kLowShadowEnd >> kShadowScale;
84
85   // HighMem covers the upper part of the address space.
86   const uptr kHighShadowEnd = (maxVirtualAddress >> kShadowScale) + 1;
87   const uptr kHighShadowStart = Max(kLowMemEnd, kHighShadowEnd >> kShadowScale);
88   CHECK(kHighShadowStart < kHighShadowEnd);
89
90   const uptr kHighMemStart = kHighShadowStart << kShadowScale;
91   CHECK(kHighShadowEnd <= kHighMemStart);
92
93   if (Verbosity()) {
94     Printf("|| `[%p, %p]` || HighMem    ||\n", (void *)kHighMemStart,
95            (void *)maxVirtualAddress);
96     if (kHighMemStart > kHighShadowEnd)
97       Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void *)kHighShadowEnd,
98              (void *)kHighMemStart);
99     Printf("|| `[%p, %p]` || HighShadow ||\n", (void *)kHighShadowStart,
100            (void *)kHighShadowEnd);
101     if (kHighShadowStart > kLowMemEnd)
102       Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void *)kHighShadowEnd,
103              (void *)kHighMemStart);
104     Printf("|| `[%p, %p]` || LowMem     ||\n", (void *)kLowShadowEnd,
105            (void *)kLowMemEnd);
106     Printf("|| `[%p, %p]` || LowShadow  ||\n", (void *)kLowShadowStart,
107            (void *)kLowShadowEnd);
108     Printf("|| `[%p, %p]` || ShadowGap1 ||\n", (void *)0,
109            (void *)kLowShadowStart);
110   }
111
112   ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd - 1, "low shadow");
113   ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd - 1, "high shadow");
114   ProtectGap(0, kLowShadowStart);
115   if (kHighShadowStart > kLowMemEnd)
116     ProtectGap(kLowMemEnd, kHighShadowStart - kLowMemEnd);
117   if (kHighMemStart > kHighShadowEnd)
118     ProtectGap(kHighShadowEnd, kHighMemStart - kHighShadowEnd);
119
120   return true;
121 }
122
123 static void HwasanAtExit(void) {
124   if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0))
125     ReportStats();
126   if (hwasan_report_count > 0) {
127     // ReportAtExitStatistics();
128     if (common_flags()->exitcode)
129       internal__exit(common_flags()->exitcode);
130   }
131 }
132
133 void InstallAtExitHandler() {
134   atexit(HwasanAtExit);
135 }
136
137 // ---------------------- TSD ---------------- {{{1
138
139 static pthread_key_t tsd_key;
140 static bool tsd_key_inited = false;
141
142 void HwasanTSDInit(void (*destructor)(void *tsd)) {
143   CHECK(!tsd_key_inited);
144   tsd_key_inited = true;
145   CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
146 }
147
148 HwasanThread *GetCurrentThread() {
149   return (HwasanThread*)pthread_getspecific(tsd_key);
150 }
151
152 void SetCurrentThread(HwasanThread *t) {
153   // Make sure that HwasanTSDDtor gets called at the end.
154   CHECK(tsd_key_inited);
155   // Make sure we do not reset the current HwasanThread.
156   CHECK_EQ(0, pthread_getspecific(tsd_key));
157   pthread_setspecific(tsd_key, (void *)t);
158 }
159
160 void HwasanTSDDtor(void *tsd) {
161   HwasanThread *t = (HwasanThread*)tsd;
162   if (t->destructor_iterations_ > 1) {
163     t->destructor_iterations_--;
164     CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
165     return;
166   }
167   // Make sure that signal handler can not see a stale current thread pointer.
168   atomic_signal_fence(memory_order_seq_cst);
169   HwasanThread::TSDDtor(tsd);
170 }
171
172 struct AccessInfo {
173   uptr addr;
174   uptr size;
175   bool is_store;
176   bool is_load;
177   bool recover;
178 };
179
180 #if defined(__aarch64__)
181 static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
182   // Access type is encoded in HLT immediate as 0x1XY,
183   // where X&1 is 1 for store, 0 for load,
184   // and X&2 is 1 if the error is recoverable.
185   // Valid values of Y are 0 to 4, which are interpreted as log2(access_size),
186   // and 0xF, which means that access size is stored in X1 register.
187   // Access address is always in X0 register.
188   AccessInfo ai;
189   uptr pc = (uptr)info->si_addr;
190   unsigned code = ((*(u32 *)pc) >> 5) & 0xffff;
191   if ((code & 0xff00) != 0x100)
192     return AccessInfo{0, 0, false, false}; // Not ours.
193   bool is_store = code & 0x10;
194   bool recover = code & 0x20;
195   unsigned size_log = code & 0xf;
196   if (size_log > 4 && size_log != 0xf)
197     return AccessInfo{0, 0, false, false}; // Not ours.
198
199   ai.is_store = is_store;
200   ai.is_load = !is_store;
201   ai.addr = uc->uc_mcontext.regs[0];
202   if (size_log == 0xf)
203     ai.size = uc->uc_mcontext.regs[1];
204   else
205     ai.size = 1U << size_log;
206   ai.recover = recover;
207   return ai;
208 }
209 #else
210 static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
211   return AccessInfo{0, 0, false, false};
212 }
213 #endif
214
215 static bool HwasanOnSIGILL(int signo, siginfo_t *info, ucontext_t *uc) {
216   SignalContext sig{info, uc};
217   AccessInfo ai = GetAccessInfo(info, uc);
218   if (!ai.is_store && !ai.is_load)
219     return false;
220
221   InternalScopedBuffer<BufferedStackTrace> stack_buffer(1);
222   BufferedStackTrace *stack = stack_buffer.data();
223   stack->Reset();
224   GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, uc,
225                 common_flags()->fast_unwind_on_fatal);
226
227   ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store);
228
229   ++hwasan_report_count;
230   if (flags()->halt_on_error || !ai.recover)
231     Die();
232
233   uc->uc_mcontext.pc += 4;
234   return true;
235 }
236
237 static void OnStackUnwind(const SignalContext &sig, const void *,
238                           BufferedStackTrace *stack) {
239   GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context,
240                 common_flags()->fast_unwind_on_fatal);
241 }
242
243 void HwasanOnDeadlySignal(int signo, void *info, void *context) {
244   // Probably a tag mismatch.
245   if (signo == SIGILL)
246     if (HwasanOnSIGILL(signo, (siginfo_t *)info, (ucontext_t*)context))
247       return;
248
249   HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr);
250 }
251
252
253 } // namespace __hwasan
254
255 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD