asan_linux.cc revision c549dd7b5fa5fb97270f57067797224cee0429f2
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// Linux-specific details.
13//===----------------------------------------------------------------------===//
14#ifdef __linux__
15
16#include "asan_interceptors.h"
17#include "asan_internal.h"
18#include "asan_procmaps.h"
19#include "asan_thread.h"
20
21#include <sys/time.h>
22#include <sys/resource.h>
23#include <sys/mman.h>
24#include <sys/syscall.h>
25#include <sys/types.h>
26#include <fcntl.h>
27#include <pthread.h>
28#include <stdio.h>
29#include <unistd.h>
30
31extern char _DYNAMIC[];
32
33namespace __asan {
34
35void *AsanDoesNotSupportStaticLinkage() {
36  // This will fail to link with -static.
37  return &_DYNAMIC;
38}
39
40static void *asan_mmap(void *addr, size_t length, int prot, int flags,
41                int fd, uint64_t offset) {
42# if __WORDSIZE == 64
43  return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
44# else
45  return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
46# endif
47}
48
49void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) {
50  size = RoundUpTo(size, kPageSize);
51  void *res = asan_mmap(0, size,
52                        PROT_READ | PROT_WRITE,
53                        MAP_PRIVATE | MAP_ANON, -1, 0);
54  if (res == (void*)-1) {
55    OutOfMemoryMessageAndDie(mem_type, size);
56  }
57  return res;
58}
59
60void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) {
61  return asan_mmap((void*)fixed_addr, size,
62                   PROT_READ | PROT_WRITE,
63                   MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
64                   0, 0);
65}
66
67void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) {
68  return asan_mmap((void*)fixed_addr, size,
69                   PROT_READ | PROT_WRITE,
70                   MAP_PRIVATE | MAP_ANON | MAP_FIXED,
71                   0, 0);
72}
73
74void *AsanMprotect(uintptr_t fixed_addr, size_t size) {
75  return asan_mmap((void*)fixed_addr, size,
76                   PROT_NONE,
77                   MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
78                   0, 0);
79}
80
81void AsanUnmapOrDie(void *addr, size_t size) {
82  if (!addr || !size) return;
83  int res = syscall(__NR_munmap, addr, size);
84  if (res != 0) {
85    Report("Failed to unmap\n");
86    ASAN_DIE;
87  }
88}
89
90ssize_t AsanWrite(int fd, const void *buf, size_t count) {
91  return (ssize_t)syscall(__NR_write, fd, buf, count);
92}
93
94int AsanOpenReadonly(const char* filename) {
95  return open(filename, O_RDONLY);
96}
97
98ssize_t AsanRead(int fd, void *buf, size_t count) {
99  return (ssize_t)syscall(__NR_read, fd, buf, count);
100}
101
102int AsanClose(int fd) {
103  return close(fd);
104}
105
106AsanProcMaps::AsanProcMaps() {
107  proc_self_maps_buff_len_ =
108      ReadFileToBuffer("/proc/self/maps", &proc_self_maps_buff_,
109                       &proc_self_maps_buff_mmaped_size_, 1 << 20);
110  CHECK(proc_self_maps_buff_len_ > 0);
111  // AsanWrite(2, proc_self_maps_buff_, proc_self_maps_buff_len_);
112  Reset();
113}
114
115AsanProcMaps::~AsanProcMaps() {
116  AsanUnmapOrDie(proc_self_maps_buff_, proc_self_maps_buff_mmaped_size_);
117}
118
119void AsanProcMaps::Reset() {
120  current_ = proc_self_maps_buff_;
121}
122
123bool AsanProcMaps::Next(uint64_t *start, uint64_t *end,
124                        uint64_t *offset, char filename[],
125                        size_t filename_size) {
126  char *last = proc_self_maps_buff_ + proc_self_maps_buff_len_;
127  if (current_ >= last) return false;
128  int consumed = 0;
129  char flags[10];
130  int major, minor;
131  uint64_t inode;
132  char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
133  if (next_line == NULL)
134    next_line = last;
135  if (SScanf(current_,
136             "%llx-%llx %4s %llx %x:%x %lld %n",
137             start, end, flags, offset, &major, &minor,
138             &inode, &consumed) != 7)
139    return false;
140  current_ += consumed;
141  // Skip spaces.
142  while (current_ < next_line && *current_ == ' ')
143    current_++;
144  // Fill in the filename.
145  size_t i = 0;
146  while (current_ < next_line) {
147    if (filename && i < filename_size - 1)
148      filename[i++] = *current_;
149    current_++;
150  }
151  if (filename && i < filename_size)
152    filename[i] = 0;
153  current_ = next_line + 1;
154  return true;
155}
156
157void AsanThread::SetThreadStackTopAndBottom() {
158  if (tid() == 0) {
159    // This is the main thread. Libpthread may not be initialized yet.
160    struct rlimit rl;
161    CHECK(getrlimit(RLIMIT_STACK, &rl) == 0);
162
163    // Find the mapping that contains a stack variable.
164    AsanProcMaps proc_maps;
165    uint64_t start, end, offset;
166    uint64_t prev_end = 0;
167    while (proc_maps.Next(&start, &end, &offset, NULL, 0)) {
168      if ((uintptr_t)&rl < end)
169        break;
170      prev_end = end;
171    }
172    CHECK((uintptr_t)&rl >= start && (uintptr_t)&rl < end);
173
174    // Get stacksize from rlimit, but clip it so that it does not overlap
175    // with other mappings.
176    size_t stacksize = rl.rlim_cur;
177    if (stacksize > end - prev_end)
178      stacksize = end - prev_end;
179    if (stacksize > kMaxThreadStackSize)
180      stacksize = kMaxThreadStackSize;
181    stack_top_ = end;
182    stack_bottom_ = end - stacksize;
183    CHECK(AddrIsInStack((uintptr_t)&rl));
184    return;
185  }
186  pthread_attr_t attr;
187  CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
188  size_t stacksize = 0;
189  void *stackaddr = NULL;
190  pthread_attr_getstack(&attr, &stackaddr, &stacksize);
191  pthread_attr_destroy(&attr);
192
193  stack_top_ = (uintptr_t)stackaddr + stacksize;
194  stack_bottom_ = (uintptr_t)stackaddr;
195  // When running with unlimited stack size, we still want to set some limit.
196  // The unlimited stack size is caused by 'ulimit -s unlimited'.
197  // Also, for some reason, GNU make spawns subrocesses with unlimited stack.
198  if (stacksize > kMaxThreadStackSize) {
199    stack_bottom_ = stack_top_ - kMaxThreadStackSize;
200  }
201  CHECK(AddrIsInStack((uintptr_t)&attr));
202}
203
204
205}  // namespace __asan
206
207#endif  // __linux__
208