1//===-- sanitizer_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 shared between AddressSanitizer and ThreadSanitizer
11// run-time libraries and implements linux-specific functions from
12// sanitizer_libc.h.
13//===----------------------------------------------------------------------===//
14#ifdef __linux__
15
16#include "sanitizer_common.h"
17#include "sanitizer_internal_defs.h"
18#include "sanitizer_libc.h"
19#include "sanitizer_placement_new.h"
20#include "sanitizer_procmaps.h"
21
22#include <fcntl.h>
23#include <pthread.h>
24#include <sched.h>
25#include <sys/mman.h>
26#include <sys/resource.h>
27#include <sys/stat.h>
28#include <sys/syscall.h>
29#include <sys/time.h>
30#include <sys/types.h>
31#include <unistd.h>
32
33namespace __sanitizer {
34
35// --------------- sanitizer_libc.h
36void *internal_mmap(void *addr, uptr length, int prot, int flags,
37                    int fd, u64 offset) {
38#if __WORDSIZE == 64
39  return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
40#else
41  return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
42#endif
43}
44
45int internal_munmap(void *addr, uptr length) {
46  return syscall(__NR_munmap, addr, length);
47}
48
49int internal_close(fd_t fd) {
50  return syscall(__NR_close, fd);
51}
52
53fd_t internal_open(const char *filename, bool write) {
54  return syscall(__NR_open, filename,
55      write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
56}
57
58uptr internal_read(fd_t fd, void *buf, uptr count) {
59  return (uptr)syscall(__NR_read, fd, buf, count);
60}
61
62uptr internal_write(fd_t fd, const void *buf, uptr count) {
63  return (uptr)syscall(__NR_write, fd, buf, count);
64}
65
66uptr internal_filesize(fd_t fd) {
67#if __WORDSIZE == 64
68  struct stat st;
69  if (syscall(__NR_fstat, fd, &st))
70    return -1;
71#else
72  struct stat64 st;
73  if (syscall(__NR_fstat64, fd, &st))
74    return -1;
75#endif
76  return (uptr)st.st_size;
77}
78
79int internal_dup2(int oldfd, int newfd) {
80  return syscall(__NR_dup2, oldfd, newfd);
81}
82
83uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
84  return (uptr)syscall(__NR_readlink, path, buf, bufsize);
85}
86
87int internal_sched_yield() {
88  return syscall(__NR_sched_yield);
89}
90
91// ----------------- sanitizer_common.h
92void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
93                                uptr *stack_bottom) {
94  static const uptr kMaxThreadStackSize = 256 * (1 << 20);  // 256M
95  CHECK(stack_top);
96  CHECK(stack_bottom);
97  if (at_initialization) {
98    // This is the main thread. Libpthread may not be initialized yet.
99    struct rlimit rl;
100    CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
101
102    // Find the mapping that contains a stack variable.
103    MemoryMappingLayout proc_maps;
104    uptr start, end, offset;
105    uptr prev_end = 0;
106    while (proc_maps.Next(&start, &end, &offset, 0, 0)) {
107      if ((uptr)&rl < end)
108        break;
109      prev_end = end;
110    }
111    CHECK((uptr)&rl >= start && (uptr)&rl < end);
112
113    // Get stacksize from rlimit, but clip it so that it does not overlap
114    // with other mappings.
115    uptr stacksize = rl.rlim_cur;
116    if (stacksize > end - prev_end)
117      stacksize = end - prev_end;
118    // When running with unlimited stack size, we still want to set some limit.
119    // The unlimited stack size is caused by 'ulimit -s unlimited'.
120    // Also, for some reason, GNU make spawns subprocesses with unlimited stack.
121    if (stacksize > kMaxThreadStackSize)
122      stacksize = kMaxThreadStackSize;
123    *stack_top = end;
124    *stack_bottom = end - stacksize;
125    return;
126  }
127  pthread_attr_t attr;
128  CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
129  uptr stacksize = 0;
130  void *stackaddr = 0;
131  pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize);
132  pthread_attr_destroy(&attr);
133
134  *stack_top = (uptr)stackaddr + stacksize;
135  *stack_bottom = (uptr)stackaddr;
136  CHECK(stacksize < kMaxThreadStackSize);  // Sanity check.
137}
138
139// Like getenv, but reads env directly from /proc and does not use libc.
140// This function should be called first inside __asan_init.
141const char *GetEnv(const char *name) {
142  static char *environ;
143  static uptr len;
144  static bool inited;
145  if (!inited) {
146    inited = true;
147    uptr environ_size;
148    len = ReadFileToBuffer("/proc/self/environ",
149                           &environ, &environ_size, 1 << 26);
150  }
151  if (!environ || len == 0) return 0;
152  uptr namelen = internal_strlen(name);
153  const char *p = environ;
154  while (*p != '\0') {  // will happen at the \0\0 that terminates the buffer
155    // proc file has the format NAME=value\0NAME=value\0NAME=value\0...
156    const char* endp =
157        (char*)internal_memchr(p, '\0', len - (p - environ));
158    if (endp == 0)  // this entry isn't NUL terminated
159      return 0;
160    else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=')  // Match.
161      return p + namelen + 1;  // point after =
162    p = endp + 1;
163  }
164  return 0;  // Not found.
165}
166
167// ----------------- sanitizer_procmaps.h
168MemoryMappingLayout::MemoryMappingLayout() {
169  proc_self_maps_buff_len_ =
170      ReadFileToBuffer("/proc/self/maps", &proc_self_maps_buff_,
171                       &proc_self_maps_buff_mmaped_size_, 1 << 26);
172  CHECK_GT(proc_self_maps_buff_len_, 0);
173  // internal_write(2, proc_self_maps_buff_, proc_self_maps_buff_len_);
174  Reset();
175}
176
177MemoryMappingLayout::~MemoryMappingLayout() {
178  UnmapOrDie(proc_self_maps_buff_, proc_self_maps_buff_mmaped_size_);
179}
180
181void MemoryMappingLayout::Reset() {
182  current_ = proc_self_maps_buff_;
183}
184
185// Parse a hex value in str and update str.
186static uptr ParseHex(char **str) {
187  uptr x = 0;
188  char *s;
189  for (s = *str; ; s++) {
190    char c = *s;
191    uptr v = 0;
192    if (c >= '0' && c <= '9')
193      v = c - '0';
194    else if (c >= 'a' && c <= 'f')
195      v = c - 'a' + 10;
196    else if (c >= 'A' && c <= 'F')
197      v = c - 'A' + 10;
198    else
199      break;
200    x = x * 16 + v;
201  }
202  *str = s;
203  return x;
204}
205
206static bool IsOnOf(char c, char c1, char c2) {
207  return c == c1 || c == c2;
208}
209
210static bool IsDecimal(char c) {
211  return c >= '0' && c <= '9';
212}
213
214bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
215                               char filename[], uptr filename_size) {
216  char *last = proc_self_maps_buff_ + proc_self_maps_buff_len_;
217  if (current_ >= last) return false;
218  uptr dummy;
219  if (!start) start = &dummy;
220  if (!end) end = &dummy;
221  if (!offset) offset = &dummy;
222  char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
223  if (next_line == 0)
224    next_line = last;
225  // Example: 08048000-08056000 r-xp 00000000 03:0c 64593   /foo/bar
226  *start = ParseHex(&current_);
227  CHECK_EQ(*current_++, '-');
228  *end = ParseHex(&current_);
229  CHECK_EQ(*current_++, ' ');
230  CHECK(IsOnOf(*current_++, '-', 'r'));
231  CHECK(IsOnOf(*current_++, '-', 'w'));
232  CHECK(IsOnOf(*current_++, '-', 'x'));
233  CHECK(IsOnOf(*current_++, 's', 'p'));
234  CHECK_EQ(*current_++, ' ');
235  *offset = ParseHex(&current_);
236  CHECK_EQ(*current_++, ' ');
237  ParseHex(&current_);
238  CHECK_EQ(*current_++, ':');
239  ParseHex(&current_);
240  CHECK_EQ(*current_++, ' ');
241  while (IsDecimal(*current_))
242    current_++;
243  CHECK_EQ(*current_++, ' ');
244  // Skip spaces.
245  while (current_ < next_line && *current_ == ' ')
246    current_++;
247  // Fill in the filename.
248  uptr i = 0;
249  while (current_ < next_line) {
250    if (filename && i < filename_size - 1)
251      filename[i++] = *current_;
252    current_++;
253  }
254  if (filename && i < filename_size)
255    filename[i] = 0;
256  current_ = next_line + 1;
257  return true;
258}
259
260// Gets the object name and the offset by walking MemoryMappingLayout.
261bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
262                                                 char filename[],
263                                                 uptr filename_size) {
264  return IterateForObjectNameAndOffset(addr, offset, filename, filename_size);
265}
266
267}  // namespace __sanitizer
268
269#endif  // __linux__
270