1//===-- sanitizer_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 shared between AddressSanitizer and ThreadSanitizer
10// run-time libraries and implements POSIX-specific functions from
11// sanitizer_posix.h.
12//===----------------------------------------------------------------------===//
13
14#include "sanitizer_platform.h"
15
16#if SANITIZER_POSIX
17
18#include "sanitizer_common.h"
19#include "sanitizer_file.h"
20#include "sanitizer_flags.h"
21#include "sanitizer_libc.h"
22#include "sanitizer_posix.h"
23#include "sanitizer_procmaps.h"
24
25#include <errno.h>
26#include <fcntl.h>
27#include <signal.h>
28#include <sys/mman.h>
29
30#if SANITIZER_FREEBSD
31// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
32// that, it was never implemented.  So just define it to zero.
33#undef  MAP_NORESERVE
34#define MAP_NORESERVE 0
35#endif
36
37namespace __sanitizer {
38
39// ------------- sanitizer_common.h
40uptr GetMmapGranularity() {
41  return GetPageSize();
42}
43
44void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
45  size = RoundUpTo(size, GetPageSizeCached());
46  uptr res = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
47                       MAP_PRIVATE | MAP_ANON, mem_type);
48  int reserrno;
49  if (UNLIKELY(internal_iserror(res, &reserrno)))
50    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report);
51  IncreaseTotalMmap(size);
52  return (void *)res;
53}
54
55void UnmapOrDie(void *addr, uptr size) {
56  if (!addr || !size) return;
57  uptr res = internal_munmap(addr, size);
58  if (UNLIKELY(internal_iserror(res))) {
59    Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
60           SanitizerToolName, size, size, addr);
61    CHECK("unable to unmap" && 0);
62  }
63  DecreaseTotalMmap(size);
64}
65
66void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
67  size = RoundUpTo(size, GetPageSizeCached());
68  uptr res = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
69                       MAP_PRIVATE | MAP_ANON, mem_type);
70  int reserrno;
71  if (UNLIKELY(internal_iserror(res, &reserrno))) {
72    if (reserrno == ENOMEM)
73      return nullptr;
74    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
75  }
76  IncreaseTotalMmap(size);
77  return (void *)res;
78}
79
80// We want to map a chunk of address space aligned to 'alignment'.
81// We do it by mapping a bit more and then unmapping redundant pieces.
82// We probably can do it with fewer syscalls in some OS-dependent way.
83void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
84                                   const char *mem_type) {
85  CHECK(IsPowerOfTwo(size));
86  CHECK(IsPowerOfTwo(alignment));
87  uptr map_size = size + alignment;
88  uptr map_res = (uptr)MmapOrDieOnFatalError(map_size, mem_type);
89  if (UNLIKELY(!map_res))
90    return nullptr;
91  uptr map_end = map_res + map_size;
92  uptr res = map_res;
93  if (!IsAligned(res, alignment)) {
94    res = (map_res + alignment - 1) & ~(alignment - 1);
95    UnmapOrDie((void*)map_res, res - map_res);
96  }
97  uptr end = res + size;
98  if (end != map_end)
99    UnmapOrDie((void*)end, map_end - end);
100  return (void*)res;
101}
102
103void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
104  size = RoundUpTo(size, GetPageSizeCached());
105  uptr p = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
106                     MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, mem_type);
107  int reserrno;
108  if (UNLIKELY(internal_iserror(p, &reserrno)))
109    ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
110  IncreaseTotalMmap(size);
111  return (void *)p;
112}
113
114static void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem,
115                           const char *name) {
116  size = RoundUpTo(size, GetPageSizeCached());
117  fixed_addr = RoundDownTo(fixed_addr, GetPageSizeCached());
118  uptr p = MmapNamed((void *)fixed_addr, size, PROT_READ | PROT_WRITE,
119                     MAP_PRIVATE | MAP_ANON | MAP_FIXED, name);
120  int reserrno;
121  if (UNLIKELY(internal_iserror(p, &reserrno))) {
122    if (tolerate_enomem && reserrno == ENOMEM)
123      return nullptr;
124    char mem_type[40];
125    internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
126                      fixed_addr);
127    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
128  }
129  IncreaseTotalMmap(size);
130  return (void *)p;
131}
132
133void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) {
134  return MmapFixedImpl(fixed_addr, size, false /*tolerate_enomem*/, name);
135}
136
137void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) {
138  return MmapFixedImpl(fixed_addr, size, true /*tolerate_enomem*/, name);
139}
140
141bool MprotectNoAccess(uptr addr, uptr size) {
142  return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
143}
144
145bool MprotectReadOnly(uptr addr, uptr size) {
146  return 0 == internal_mprotect((void *)addr, size, PROT_READ);
147}
148
149#if !SANITIZER_MAC
150void MprotectMallocZones(void *addr, int prot) {}
151#endif
152
153fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
154  if (ShouldMockFailureToOpen(filename))
155    return kInvalidFd;
156  int flags;
157  switch (mode) {
158    case RdOnly: flags = O_RDONLY; break;
159    case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
160    case RdWr: flags = O_RDWR | O_CREAT; break;
161  }
162  fd_t res = internal_open(filename, flags, 0660);
163  if (internal_iserror(res, errno_p))
164    return kInvalidFd;
165  return ReserveStandardFds(res);
166}
167
168void CloseFile(fd_t fd) {
169  internal_close(fd);
170}
171
172bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
173                  error_t *error_p) {
174  uptr res = internal_read(fd, buff, buff_size);
175  if (internal_iserror(res, error_p))
176    return false;
177  if (bytes_read)
178    *bytes_read = res;
179  return true;
180}
181
182bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
183                 error_t *error_p) {
184  uptr res = internal_write(fd, buff, buff_size);
185  if (internal_iserror(res, error_p))
186    return false;
187  if (bytes_written)
188    *bytes_written = res;
189  return true;
190}
191
192void *MapFileToMemory(const char *file_name, uptr *buff_size) {
193  fd_t fd = OpenFile(file_name, RdOnly);
194  CHECK(fd != kInvalidFd);
195  uptr fsize = internal_filesize(fd);
196  CHECK_NE(fsize, (uptr)-1);
197  CHECK_GT(fsize, 0);
198  *buff_size = RoundUpTo(fsize, GetPageSizeCached());
199  uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
200  return internal_iserror(map) ? nullptr : (void *)map;
201}
202
203void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
204  uptr flags = MAP_SHARED;
205  if (addr) flags |= MAP_FIXED;
206  uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
207  int mmap_errno = 0;
208  if (internal_iserror(p, &mmap_errno)) {
209    Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
210           fd, (long long)offset, size, p, mmap_errno);
211    return nullptr;
212  }
213  return (void *)p;
214}
215
216static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
217                                        uptr start2, uptr end2) {
218  CHECK(start1 <= end1);
219  CHECK(start2 <= end2);
220  return (end1 < start2) || (end2 < start1);
221}
222
223// FIXME: this is thread-unsafe, but should not cause problems most of the time.
224// When the shadow is mapped only a single thread usually exists (plus maybe
225// several worker threads on Mac, which aren't expected to map big chunks of
226// memory).
227bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
228  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
229  if (proc_maps.Error())
230    return true; // and hope for the best
231  MemoryMappedSegment segment;
232  while (proc_maps.Next(&segment)) {
233    if (segment.start == segment.end) continue;  // Empty range.
234    CHECK_NE(0, segment.end);
235    if (!IntervalsAreSeparate(segment.start, segment.end - 1, range_start,
236                              range_end))
237      return false;
238  }
239  return true;
240}
241
242void DumpProcessMap() {
243  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
244  const sptr kBufSize = 4095;
245  char *filename = (char*)MmapOrDie(kBufSize, __func__);
246  MemoryMappedSegment segment(filename, kBufSize);
247  Report("Process memory map follows:\n");
248  while (proc_maps.Next(&segment)) {
249    Printf("\t%p-%p\t%s\n", (void *)segment.start, (void *)segment.end,
250           segment.filename);
251  }
252  Report("End of process memory map.\n");
253  UnmapOrDie(filename, kBufSize);
254}
255
256const char *GetPwd() {
257  return GetEnv("PWD");
258}
259
260bool IsPathSeparator(const char c) {
261  return c == '/';
262}
263
264bool IsAbsolutePath(const char *path) {
265  return path != nullptr && IsPathSeparator(path[0]);
266}
267
268void ReportFile::Write(const char *buffer, uptr length) {
269  SpinMutexLock l(mu);
270  ReopenIfNecessary();
271  internal_write(fd, buffer, length);
272}
273
274bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
275  MemoryMappingLayout proc_maps(/*cache_enabled*/false);
276  InternalScopedString buff(kMaxPathLength);
277  MemoryMappedSegment segment(buff.data(), kMaxPathLength);
278  while (proc_maps.Next(&segment)) {
279    if (segment.IsExecutable() &&
280        internal_strcmp(module, segment.filename) == 0) {
281      *start = segment.start;
282      *end = segment.end;
283      return true;
284    }
285  }
286  return false;
287}
288
289uptr SignalContext::GetAddress() const {
290  auto si = static_cast<const siginfo_t *>(siginfo);
291  return (uptr)si->si_addr;
292}
293
294bool SignalContext::IsMemoryAccess() const {
295  auto si = static_cast<const siginfo_t *>(siginfo);
296  return si->si_signo == SIGSEGV;
297}
298
299int SignalContext::GetType() const {
300  return static_cast<const siginfo_t *>(siginfo)->si_signo;
301}
302
303const char *SignalContext::Describe() const {
304  switch (GetType()) {
305    case SIGFPE:
306      return "FPE";
307    case SIGILL:
308      return "ILL";
309    case SIGABRT:
310      return "ABRT";
311    case SIGSEGV:
312      return "SEGV";
313    case SIGBUS:
314      return "BUS";
315    case SIGTRAP:
316      return "TRAP";
317  }
318  return "UNKNOWN SIGNAL";
319}
320
321fd_t ReserveStandardFds(fd_t fd) {
322  CHECK_GE(fd, 0);
323  if (fd > 2)
324    return fd;
325  bool used[3];
326  internal_memset(used, 0, sizeof(used));
327  while (fd <= 2) {
328    used[fd] = true;
329    fd = internal_dup(fd);
330  }
331  for (int i = 0; i <= 2; ++i)
332    if (used[i])
333      internal_close(i);
334  return fd;
335}
336
337bool ShouldMockFailureToOpen(const char *path) {
338  return common_flags()->test_only_emulate_no_memorymap &&
339         internal_strncmp(path, "/proc/", 6) == 0;
340}
341
342#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
343int GetNamedMappingFd(const char *name, uptr size, int *flags) {
344  if (!common_flags()->decorate_proc_maps || !name)
345    return -1;
346  char shmname[200];
347  CHECK(internal_strlen(name) < sizeof(shmname) - 10);
348  internal_snprintf(shmname, sizeof(shmname), "/dev/shm/%zu [%s]",
349                    internal_getpid(), name);
350  int fd = ReserveStandardFds(
351      internal_open(shmname, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRWXU));
352  CHECK_GE(fd, 0);
353  int res = internal_ftruncate(fd, size);
354  CHECK_EQ(0, res);
355  res = internal_unlink(shmname);
356  CHECK_EQ(0, res);
357  *flags &= ~(MAP_ANON | MAP_ANONYMOUS);
358  return fd;
359}
360#else
361int GetNamedMappingFd(const char *name, uptr size, int *flags) {
362  return -1;
363}
364#endif
365
366#if SANITIZER_ANDROID
367#define PR_SET_VMA 0x53564d41
368#define PR_SET_VMA_ANON_NAME 0
369void DecorateMapping(uptr addr, uptr size, const char *name) {
370  if (!common_flags()->decorate_proc_maps || !name)
371    return;
372  internal_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, size, (uptr)name);
373}
374#else
375void DecorateMapping(uptr addr, uptr size, const char *name) {
376}
377#endif
378
379uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name) {
380  int fd = GetNamedMappingFd(name, length, &flags);
381  uptr res = internal_mmap(addr, length, prot, flags, fd, 0);
382  if (!internal_iserror(res))
383    DecorateMapping(res, length, name);
384  return res;
385}
386
387
388} // namespace __sanitizer
389
390#endif // SANITIZER_POSIX
391