1 //===-- tsan_platform_posix.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 ThreadSanitizer (TSan), a race detector.
10 //
11 // POSIX-specific code.
12 //===----------------------------------------------------------------------===//
13 
14 #include "sanitizer_common/sanitizer_platform.h"
15 #if SANITIZER_POSIX
16 
17 #  include <dlfcn.h>
18 
19 #  include "sanitizer_common/sanitizer_common.h"
20 #  include "sanitizer_common/sanitizer_errno.h"
21 #  include "sanitizer_common/sanitizer_libc.h"
22 #  include "sanitizer_common/sanitizer_procmaps.h"
23 #  include "tsan_platform.h"
24 #  include "tsan_rtl.h"
25 
26 namespace __tsan {
27 
28 static const char kShadowMemoryMappingWarning[] =
29     "FATAL: %s can not madvise shadow region [%zx, %zx] with %s (errno: %d)\n";
30 static const char kShadowMemoryMappingHint[] =
31     "HINT: if %s is not supported in your environment, you may set "
32     "TSAN_OPTIONS=%s=0\n";
33 
34 #  if !SANITIZER_GO
35 void DontDumpShadow(uptr addr, uptr size) {
36   if (common_flags()->use_madv_dontdump)
37     if (!DontDumpShadowMemory(addr, size)) {
38       Printf(kShadowMemoryMappingWarning, SanitizerToolName, addr, addr + size,
39              "MADV_DONTDUMP", errno);
40       Printf(kShadowMemoryMappingHint, "MADV_DONTDUMP", "use_madv_dontdump");
41       Die();
42     }
43 }
44 
45 void InitializeShadowMemory() {
46   // Map memory shadow.
47   if (!MmapFixedSuperNoReserve(ShadowBeg(), ShadowEnd() - ShadowBeg(),
48                                "shadow")) {
49     Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
50     Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
51     Die();
52   }
53   // This memory range is used for thread stacks and large user mmaps.
54   // Frequently a thread uses only a small part of stack and similarly
55   // a program uses a small part of large mmap. On some programs
56   // we see 20% memory usage reduction without huge pages for this range.
57   DontDumpShadow(ShadowBeg(), ShadowEnd() - ShadowBeg());
58   DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
59       ShadowBeg(), ShadowEnd(),
60       (ShadowEnd() - ShadowBeg()) >> 30);
61 
62   // Map meta shadow.
63   const uptr meta = MetaShadowBeg();
64   const uptr meta_size = MetaShadowEnd() - meta;
65   if (!MmapFixedSuperNoReserve(meta, meta_size, "meta shadow")) {
66     Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
67     Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
68     Die();
69   }
70   DontDumpShadow(meta, meta_size);
71   DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
72       meta, meta + meta_size, meta_size >> 30);
73 
74   InitializeShadowMemoryPlatform();
75 
76   on_initialize = reinterpret_cast<void (*)(void)>(
77       dlsym(RTLD_DEFAULT, "__tsan_on_initialize"));
78   on_finalize =
79       reinterpret_cast<int (*)(int)>(dlsym(RTLD_DEFAULT, "__tsan_on_finalize"));
80 }
81 
82 static bool TryProtectRange(uptr beg, uptr end) {
83   CHECK_LE(beg, end);
84   if (beg == end)
85     return true;
86   return beg == (uptr)MmapFixedNoAccess(beg, end - beg);
87 }
88 
89 static void ProtectRange(uptr beg, uptr end) {
90   if (!TryProtectRange(beg, end)) {
91     Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
92     Printf("FATAL: Make sure you are not using unlimited stack\n");
93     Die();
94   }
95 }
96 
97 void CheckAndProtect() {
98   // Ensure that the binary is indeed compiled with -pie.
99   MemoryMappingLayout proc_maps(true);
100   MemoryMappedSegment segment;
101   while (proc_maps.Next(&segment)) {
102     if (IsAppMem(segment.start)) continue;
103     if (segment.start >= HeapMemEnd() && segment.start < HeapEnd()) continue;
104     if (segment.protection == 0)  // Zero page or mprotected.
105       continue;
106     if (segment.start >= VdsoBeg())  // vdso
107       break;
108     Printf("FATAL: ThreadSanitizer: unexpected memory mapping 0x%zx-0x%zx\n",
109            segment.start, segment.end);
110     Die();
111   }
112 
113 #    if SANITIZER_IOS && !SANITIZER_IOSSIM
114   ProtectRange(HeapMemEnd(), ShadowBeg());
115   ProtectRange(ShadowEnd(), MetaShadowBeg());
116   ProtectRange(MetaShadowEnd(), HiAppMemBeg());
117 #    else
118   ProtectRange(LoAppMemEnd(), ShadowBeg());
119   ProtectRange(ShadowEnd(), MetaShadowBeg());
120   if (MidAppMemBeg()) {
121     ProtectRange(MetaShadowEnd(), MidAppMemBeg());
122     ProtectRange(MidAppMemEnd(), HeapMemBeg());
123   } else {
124     ProtectRange(MetaShadowEnd(), HeapMemBeg());
125   }
126   ProtectRange(HeapEnd(), HiAppMemBeg());
127 #    endif
128 
129 #    if defined(__s390x__)
130   // Protect the rest of the address space.
131   const uptr user_addr_max_l4 = 0x0020000000000000ull;
132   const uptr user_addr_max_l5 = 0xfffffffffffff000ull;
133   // All the maintained s390x kernels support at least 4-level page tables.
134   ProtectRange(HiAppMemEnd(), user_addr_max_l4);
135   // Older s390x kernels may not support 5-level page tables.
136   TryProtectRange(user_addr_max_l4, user_addr_max_l5);
137 #endif
138 }
139 #endif
140 
141 }  // namespace __tsan
142 
143 #endif  // SANITIZER_POSIX
144