xref: /freebsd/contrib/llvm-project/compiler-rt/lib/msan/msan_linux.cpp (revision e63d20b70ee1dbee9b075f29de6f30cdcfe1abe1)
1 //===-- msan_linux.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file is a part of MemorySanitizer.
10 //
11 // Linux-, NetBSD- and FreeBSD-specific code.
12 //===----------------------------------------------------------------------===//
13 
14 #include "sanitizer_common/sanitizer_platform.h"
15 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
16 
17 #  include <elf.h>
18 #  include <link.h>
19 #  include <pthread.h>
20 #  include <signal.h>
21 #  include <stdio.h>
22 #  include <stdlib.h>
23 #  if SANITIZER_LINUX
24 #    include <sys/personality.h>
25 #  endif
26 #  include <sys/resource.h>
27 #  include <sys/time.h>
28 #  include <unistd.h>
29 #  include <unwind.h>
30 
31 #  include "msan.h"
32 #  include "msan_allocator.h"
33 #  include "msan_chained_origin_depot.h"
34 #  include "msan_report.h"
35 #  include "msan_thread.h"
36 #  include "sanitizer_common/sanitizer_common.h"
37 #  include "sanitizer_common/sanitizer_procmaps.h"
38 #  include "sanitizer_common/sanitizer_stackdepot.h"
39 
40 namespace __msan {
41 
42 void ReportMapRange(const char *descr, uptr beg, uptr size) {
43   if (size > 0) {
44     uptr end = beg + size - 1;
45     VPrintf(1, "%s : 0x%zx - 0x%zx\n", descr, beg, end);
46   }
47 }
48 
49 static bool CheckMemoryRangeAvailability(uptr beg, uptr size, bool verbose) {
50   if (size > 0) {
51     uptr end = beg + size - 1;
52     if (!MemoryRangeIsAvailable(beg, end)) {
53       if (verbose)
54         Printf("FATAL: Memory range 0x%zx - 0x%zx is not available.\n", beg,
55                end);
56       return false;
57     }
58   }
59   return true;
60 }
61 
62 static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {
63   if (size > 0) {
64     void *addr = MmapFixedNoAccess(beg, size, name);
65     if (beg == 0 && addr) {
66       // Depending on the kernel configuration, we may not be able to protect
67       // the page at address zero.
68       uptr gap = 16 * GetPageSizeCached();
69       beg += gap;
70       size -= gap;
71       addr = MmapFixedNoAccess(beg, size, name);
72     }
73     if ((uptr)addr != beg) {
74       uptr end = beg + size - 1;
75       Printf("FATAL: Cannot protect memory range 0x%zx - 0x%zx (%s).\n", beg,
76              end, name);
77       return false;
78     }
79   }
80   return true;
81 }
82 
83 static void CheckMemoryLayoutSanity() {
84   uptr prev_end = 0;
85   for (unsigned i = 0; i < kMemoryLayoutSize; ++i) {
86     uptr start = kMemoryLayout[i].start;
87     uptr end = kMemoryLayout[i].end;
88     MappingDesc::Type type = kMemoryLayout[i].type;
89     CHECK_LT(start, end);
90     CHECK_EQ(prev_end, start);
91     CHECK(addr_is_type(start, type));
92     CHECK(addr_is_type((start + end) / 2, type));
93     CHECK(addr_is_type(end - 1, type));
94     if (type == MappingDesc::APP || type == MappingDesc::ALLOCATOR) {
95       uptr addr = start;
96       CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
97       CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
98       CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));
99 
100       addr = (start + end) / 2;
101       CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
102       CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
103       CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));
104 
105       addr = end - 1;
106       CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
107       CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
108       CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));
109     }
110     prev_end = end;
111   }
112 }
113 
114 static bool InitShadow(bool init_origins, bool dry_run) {
115   // Let user know mapping parameters first.
116   VPrintf(1, "__msan_init %p\n", reinterpret_cast<void *>(&__msan_init));
117   for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
118     VPrintf(1, "%s: %zx - %zx\n", kMemoryLayout[i].name, kMemoryLayout[i].start,
119             kMemoryLayout[i].end - 1);
120 
121   CheckMemoryLayoutSanity();
122 
123   if (!MEM_IS_APP(&__msan_init)) {
124     if (!dry_run)
125       Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",
126              reinterpret_cast<void *>(&__msan_init));
127     return false;
128   }
129 
130   const uptr maxVirtualAddress = GetMaxUserVirtualAddress();
131 
132   for (unsigned i = 0; i < kMemoryLayoutSize; ++i) {
133     uptr start = kMemoryLayout[i].start;
134     uptr end = kMemoryLayout[i].end;
135     uptr size = end - start;
136     MappingDesc::Type type = kMemoryLayout[i].type;
137 
138     // Check if the segment should be mapped based on platform constraints.
139     if (start >= maxVirtualAddress)
140       continue;
141 
142     bool map = type == MappingDesc::SHADOW ||
143                (init_origins && type == MappingDesc::ORIGIN);
144     bool protect = type == MappingDesc::INVALID ||
145                    (!init_origins && type == MappingDesc::ORIGIN);
146     CHECK(!(map && protect));
147     if (!map && !protect) {
148       CHECK(type == MappingDesc::APP || type == MappingDesc::ALLOCATOR);
149 
150       if (dry_run && type == MappingDesc::ALLOCATOR &&
151           !CheckMemoryRangeAvailability(start, size, !dry_run))
152         return false;
153     }
154     if (map) {
155       if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))
156         return false;
157       if (!dry_run &&
158           !MmapFixedSuperNoReserve(start, size, kMemoryLayout[i].name))
159         return false;
160       if (!dry_run && common_flags()->use_madv_dontdump)
161         DontDumpShadowMemory(start, size);
162     }
163     if (protect) {
164       if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))
165         return false;
166       if (!dry_run && !ProtectMemoryRange(start, size, kMemoryLayout[i].name))
167         return false;
168     }
169   }
170 
171   return true;
172 }
173 
174 bool InitShadowWithReExec(bool init_origins) {
175   // Start with dry run: check layout is ok, but don't print warnings because
176   // warning messages will cause tests to fail (even if we successfully re-exec
177   // after the warning).
178   bool success = InitShadow(__msan_get_track_origins(), true);
179   if (!success) {
180 #  if SANITIZER_LINUX
181     // Perhaps ASLR entropy is too high. If ASLR is enabled, re-exec without it.
182     int old_personality = personality(0xffffffff);
183     bool aslr_on =
184         (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);
185 
186     if (aslr_on) {
187       VReport(1,
188               "WARNING: MemorySanitizer: memory layout is incompatible, "
189               "possibly due to high-entropy ASLR.\n"
190               "Re-execing with fixed virtual address space.\n"
191               "N.B. reducing ASLR entropy is preferable.\n");
192       CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
193       ReExec();
194     }
195 #  endif
196   }
197 
198   // The earlier dry run didn't actually map or protect anything. Run again in
199   // non-dry run mode.
200   return success && InitShadow(__msan_get_track_origins(), false);
201 }
202 
203 static void MsanAtExit(void) {
204   if (flags()->print_stats && (flags()->atexit || msan_report_count > 0))
205     ReportStats();
206   if (msan_report_count > 0) {
207     ReportAtExitStatistics();
208     if (common_flags()->exitcode)
209       internal__exit(common_flags()->exitcode);
210   }
211 }
212 
213 void InstallAtExitHandler() {
214   atexit(MsanAtExit);
215 }
216 
217 // ---------------------- TSD ---------------- {{{1
218 
219 #if SANITIZER_NETBSD
220 // Thread Static Data cannot be used in early init on NetBSD.
221 // Reuse the MSan TSD API for compatibility with existing code
222 // with an alternative implementation.
223 
224 static void (*tsd_destructor)(void *tsd) = nullptr;
225 
226 struct tsd_key {
227   tsd_key() : key(nullptr) {}
228   ~tsd_key() {
229     CHECK(tsd_destructor);
230     if (key)
231       (*tsd_destructor)(key);
232   }
233   MsanThread *key;
234 };
235 
236 static thread_local struct tsd_key key;
237 
238 void MsanTSDInit(void (*destructor)(void *tsd)) {
239   CHECK(!tsd_destructor);
240   tsd_destructor = destructor;
241 }
242 
243 MsanThread *GetCurrentThread() {
244   CHECK(tsd_destructor);
245   return key.key;
246 }
247 
248 void SetCurrentThread(MsanThread *tsd) {
249   CHECK(tsd_destructor);
250   CHECK(tsd);
251   CHECK(!key.key);
252   key.key = tsd;
253 }
254 
255 void MsanTSDDtor(void *tsd) {
256   CHECK(tsd_destructor);
257   CHECK_EQ(key.key, tsd);
258   key.key = nullptr;
259   // Make sure that signal handler can not see a stale current thread pointer.
260   atomic_signal_fence(memory_order_seq_cst);
261   MsanThread::TSDDtor(tsd);
262 }
263 #else
264 static pthread_key_t tsd_key;
265 static bool tsd_key_inited = false;
266 
267 void MsanTSDInit(void (*destructor)(void *tsd)) {
268   CHECK(!tsd_key_inited);
269   tsd_key_inited = true;
270   CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
271 }
272 
273 static THREADLOCAL MsanThread* msan_current_thread;
274 
275 MsanThread *GetCurrentThread() {
276   return msan_current_thread;
277 }
278 
279 void SetCurrentThread(MsanThread *t) {
280   // Make sure we do not reset the current MsanThread.
281   CHECK_EQ(0, msan_current_thread);
282   msan_current_thread = t;
283   // Make sure that MsanTSDDtor gets called at the end.
284   CHECK(tsd_key_inited);
285   pthread_setspecific(tsd_key, (void *)t);
286 }
287 
288 void MsanTSDDtor(void *tsd) {
289   MsanThread *t = (MsanThread*)tsd;
290   if (t->destructor_iterations_ > 1) {
291     t->destructor_iterations_--;
292     CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
293     return;
294   }
295   msan_current_thread = nullptr;
296   // Make sure that signal handler can not see a stale current thread pointer.
297   atomic_signal_fence(memory_order_seq_cst);
298   MsanThread::TSDDtor(tsd);
299 }
300 #  endif
301 
302 static void BeforeFork() {
303   // Usually we lock ThreadRegistry, but msan does not have one.
304   LockAllocator();
305   StackDepotLockBeforeFork();
306   ChainedOriginDepotBeforeFork();
307 }
308 
309 static void AfterFork(bool fork_child) {
310   ChainedOriginDepotAfterFork(fork_child);
311   StackDepotUnlockAfterFork(fork_child);
312   UnlockAllocator();
313   // Usually we unlock ThreadRegistry, but msan does not have one.
314 }
315 
316 void InstallAtForkHandler() {
317   pthread_atfork(
318       &BeforeFork, []() { AfterFork(/* fork_child= */ false); },
319       []() { AfterFork(/* fork_child= */ true); });
320 }
321 
322 } // namespace __msan
323 
324 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
325