tsan_platform_linux.cc revision 48aee681c0d800deb6c00f546bfaa377ece37326
1//===-- tsan_platform_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 ThreadSanitizer (TSan), a race detector.
11//
12// Linux-specific code.
13//===----------------------------------------------------------------------===//
14
15#include "sanitizer_common/sanitizer_libc.h"
16#include "tsan_platform.h"
17#include "tsan_rtl.h"
18#include "tsan_flags.h"
19
20#include <asm/prctl.h>
21#include <fcntl.h>
22#include <pthread.h>
23#include <signal.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdarg.h>
28#include <sys/mman.h>
29#include <sys/prctl.h>
30#include <sys/syscall.h>
31#include <sys/time.h>
32#include <sys/types.h>
33#include <sys/resource.h>
34#include <sys/stat.h>
35#include <unistd.h>
36#include <errno.h>
37#include <sched.h>
38#include <dlfcn.h>
39
40using namespace __sanitizer;  // NOLINT
41
42extern "C" int arch_prctl(int code, __tsan::uptr *addr);
43
44namespace __tsan {
45
46static uptr g_tls_size;
47
48ScopedInRtl::ScopedInRtl()
49    : thr_(cur_thread()) {
50  in_rtl_ = thr_->in_rtl;
51  thr_->in_rtl++;
52  errno_ = errno;
53}
54
55ScopedInRtl::~ScopedInRtl() {
56  thr_->in_rtl--;
57  errno = errno_;
58  CHECK_EQ(in_rtl_, thr_->in_rtl);
59}
60
61void Die() {
62  _exit(1);
63}
64
65uptr GetShadowMemoryConsumption() {
66  return 0;
67}
68
69void FlushShadowMemory() {
70  madvise((void*)kLinuxShadowBeg,
71          kLinuxShadowEnd - kLinuxShadowBeg,
72          MADV_DONTNEED);
73}
74
75static void my_munmap(void *addr, size_t length) {
76  ScopedInRtl in_rtl;
77  syscall(__NR_munmap, addr, length);
78}
79
80void internal_yield() {
81  ScopedInRtl in_rtl;
82  syscall(__NR_sched_yield);
83}
84
85void internal_sleep_ms(u32 ms) {
86  usleep(ms * 1000);
87}
88
89fd_t internal_open(const char *name, bool write) {
90  ScopedInRtl in_rtl;
91  return syscall(__NR_open, name,
92      write ? O_WRONLY | O_CREAT | O_CLOEXEC : O_RDONLY, 0660);
93}
94
95void internal_close(fd_t fd) {
96  ScopedInRtl in_rtl;
97  syscall(__NR_close, fd);
98}
99
100uptr internal_filesize(fd_t fd) {
101  struct stat st = {};
102  if (syscall(__NR_fstat, fd, &st))
103    return -1;
104  return (uptr)st.st_size;
105}
106
107uptr internal_read(fd_t fd, void *p, uptr size) {
108  ScopedInRtl in_rtl;
109  return syscall(__NR_read, fd, p, size);
110}
111
112uptr internal_write(fd_t fd, const void *p, uptr size) {
113  ScopedInRtl in_rtl;
114  return syscall(__NR_write, fd, p, size);
115}
116
117int internal_dup2(int oldfd, int newfd) {
118  ScopedInRtl in_rtl;
119  return syscall(__NR_dup2, oldfd, newfd);
120}
121
122const char *internal_getpwd() {
123  return getenv("PWD");
124}
125
126static void ProtectRange(uptr beg, uptr end) {
127  ScopedInRtl in_rtl;
128  CHECK_LE(beg, end);
129  if (beg == end)
130    return;
131  if (beg != (uptr)internal_mmap((void*)(beg), end - beg,
132      PROT_NONE,
133      MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
134      -1, 0)) {
135    Printf("FATAL: ThreadSanitizer can not protect [%lx,%lx]\n", beg, end);
136    Printf("FATAL: Make sure you are not using unlimited stack\n");
137    Die();
138  }
139}
140
141void InitializeShadowMemory() {
142  const uptr kClosedLowBeg  = 0x200000;
143  const uptr kClosedLowEnd  = kLinuxShadowBeg - 1;
144  const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
145  const uptr kClosedMidEnd = kLinuxAppMemBeg - 1;
146  uptr shadow = (uptr)internal_mmap((void*)kLinuxShadowBeg,
147      kLinuxShadowEnd - kLinuxShadowBeg,
148      PROT_READ | PROT_WRITE,
149      MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
150      -1, 0);
151  if (shadow != kLinuxShadowBeg) {
152    Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
153    Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
154    Die();
155  }
156  ProtectRange(kClosedLowBeg, kClosedLowEnd);
157  ProtectRange(kClosedMidBeg, kClosedMidEnd);
158  DPrintf("kClosedLow   %lx-%lx (%luGB)\n",
159      kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
160  DPrintf("kLinuxShadow %lx-%lx (%luGB)\n",
161      kLinuxShadowBeg, kLinuxShadowEnd,
162      (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
163  DPrintf("kClosedMid   %lx-%lx (%luGB)\n",
164      kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
165  DPrintf("kLinuxAppMem %lx-%lx (%luGB)\n",
166      kLinuxAppMemBeg, kLinuxAppMemEnd,
167      (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
168  DPrintf("stack        %lx\n", (uptr)&shadow);
169}
170
171static void CheckPIE() {
172  // Ensure that the binary is indeed compiled with -pie.
173  fd_t fmaps = internal_open("/proc/self/maps", false);
174  if (fmaps == kInvalidFd)
175    return;
176  char buf[20];
177  if (internal_read(fmaps, buf, sizeof(buf)) == sizeof(buf)) {
178    buf[sizeof(buf) - 1] = 0;
179    u64 addr = strtoll(buf, 0, 16);
180    if ((u64)addr < kLinuxAppMemBeg) {
181      Printf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
182             "something is mapped at 0x%llx < 0x%lx)\n",
183             addr, kLinuxAppMemBeg);
184      Printf("FATAL: Make sure to compile with -fPIE"
185             " and to link with -pie.\n");
186      Die();
187    }
188  }
189  internal_close(fmaps);
190}
191
192#ifdef __i386__
193# define INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
194#else
195# define INTERNAL_FUNCTION
196#endif
197extern "C" void _dl_get_tls_static_info(size_t*, size_t*)
198    __attribute__((weak)) INTERNAL_FUNCTION;
199
200static int InitTlsSize() {
201  typedef void (*get_tls_func)(size_t*, size_t*) INTERNAL_FUNCTION;
202  get_tls_func get_tls = &_dl_get_tls_static_info;
203  if (get_tls == 0)
204    get_tls = (get_tls_func)dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
205  CHECK_NE(get_tls, 0);
206  size_t tls_size = 0;
207  size_t tls_align = 0;
208  get_tls(&tls_size, &tls_align);
209  return tls_size;
210}
211
212const char *InitializePlatform() {
213  void *p = 0;
214  if (sizeof(p) == 8) {
215    // Disable core dumps, dumping of 16TB usually takes a bit long.
216    // The following magic is to prevent clang from replacing it with memset.
217    volatile rlimit lim;
218    lim.rlim_cur = 0;
219    lim.rlim_max = 0;
220    setrlimit(RLIMIT_CORE, (rlimit*)&lim);
221  }
222
223  CheckPIE();
224  g_tls_size = (uptr)InitTlsSize();
225  return getenv("TSAN_OPTIONS");
226}
227
228void FinalizePlatform() {
229  fflush(0);
230}
231
232uptr GetTlsSize() {
233  return g_tls_size;
234}
235
236void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
237                          uptr *tls_addr, uptr *tls_size) {
238  arch_prctl(ARCH_GET_FS, tls_addr);
239  *tls_addr -= g_tls_size;
240  *tls_size = g_tls_size;
241
242  if (main) {
243    uptr kBufSize = 1 << 26;
244    char *buf = (char*)internal_mmap(0, kBufSize, PROT_READ | PROT_WRITE,
245                               MAP_PRIVATE | MAP_ANON, -1, 0);
246    fd_t maps = internal_open("/proc/self/maps", false);
247    if (maps == kInvalidFd) {
248      Printf("Failed to open /proc/self/maps\n");
249      Die();
250    }
251    char *end = buf;
252    while (end + kPageSize < buf + kBufSize) {
253      uptr read = internal_read(maps, end, kPageSize);
254      if ((int)read <= 0)
255        break;
256      end += read;
257    }
258    end[0] = 0;
259    end = (char*)internal_strstr(buf, "[stack]");
260    if (end == 0) {
261      Printf("Can't find [stack] in /proc/self/maps\n");
262      Die();
263    }
264    end[0] = 0;
265    char *pos = (char*)internal_strrchr(buf, '\n');
266    if (pos == 0) {
267      Printf("Can't find [stack] in /proc/self/maps\n");
268      Die();
269    }
270    pos = (char*)internal_strchr(pos, '-');
271    if (pos == 0) {
272      Printf("Can't find [stack] in /proc/self/maps\n");
273      Die();
274    }
275    uptr stack = 0;
276    for (; pos++;) {
277      uptr num = 0;
278      if (pos[0] >= '0' && pos[0] <= '9')
279        num = pos[0] - '0';
280      else if (pos[0] >= 'a' && pos[0] <= 'f')
281        num = pos[0] - 'a' + 10;
282      else
283        break;
284      stack = stack * 16 + num;
285    }
286    internal_close(maps);
287    my_munmap(buf, kBufSize);
288
289    struct rlimit rl;
290    CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
291    *stk_addr = stack - rl.rlim_cur;
292    *stk_size = rl.rlim_cur;
293  } else {
294    *stk_addr = 0;
295    *stk_size = 0;
296    pthread_attr_t attr;
297    if (pthread_getattr_np(pthread_self(), &attr) == 0) {
298      pthread_attr_getstack(&attr, (void**)stk_addr, (size_t*)stk_size);
299      pthread_attr_destroy(&attr);
300    }
301
302    // If stack and tls intersect, make them non-intersecting.
303    if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
304      CHECK_GT(*tls_addr + *tls_size, *stk_addr);
305      CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
306      *stk_size -= *tls_size;
307      *tls_addr = *stk_addr + *stk_size;
308    }
309  }
310}
311
312int GetPid() {
313  return getpid();
314}
315
316}  // namespace __tsan
317