1//===-- linux.cpp -----------------------------------------------*- C++ -*-===//
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#include "platform.h"
10
11#if SCUDO_LINUX
12
13#include "common.h"
14#include "linux.h"
15#include "mutex.h"
16#include "string_utils.h"
17
18#include <errno.h>
19#include <fcntl.h>
20#include <linux/futex.h>
21#include <sched.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/mman.h>
25#include <sys/stat.h>
26#include <sys/syscall.h>
27#include <sys/time.h>
28#include <time.h>
29#include <unistd.h>
30
31#if SCUDO_ANDROID
32#include <sys/prctl.h>
33// Definitions of prctl arguments to set a vma name in Android kernels.
34#define ANDROID_PR_SET_VMA 0x53564d41
35#define ANDROID_PR_SET_VMA_ANON_NAME 0
36#endif
37
38#ifdef ANDROID_EXPERIMENTAL_MTE
39#include <bionic/mte_kernel.h>
40#endif
41
42namespace scudo {
43
44uptr getPageSize() { return static_cast<uptr>(sysconf(_SC_PAGESIZE)); }
45
46void NORETURN die() { abort(); }
47
48void *map(void *Addr, uptr Size, UNUSED const char *Name, uptr Flags,
49          UNUSED MapPlatformData *Data) {
50  int MmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
51  int MmapProt;
52  if (Flags & MAP_NOACCESS) {
53    MmapFlags |= MAP_NORESERVE;
54    MmapProt = PROT_NONE;
55  } else {
56    MmapProt = PROT_READ | PROT_WRITE;
57#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
58    if (Flags & MAP_MEMTAG)
59      MmapProt |= PROT_MTE;
60#endif
61  }
62  if (Addr) {
63    // Currently no scenario for a noaccess mapping with a fixed address.
64    DCHECK_EQ(Flags & MAP_NOACCESS, 0);
65    MmapFlags |= MAP_FIXED;
66  }
67  void *P = mmap(Addr, Size, MmapProt, MmapFlags, -1, 0);
68  if (P == MAP_FAILED) {
69    if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)
70      dieOnMapUnmapError(errno == ENOMEM);
71    return nullptr;
72  }
73#if SCUDO_ANDROID
74  if (!(Flags & MAP_NOACCESS))
75    prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, P, Size, Name);
76#endif
77  return P;
78}
79
80void unmap(void *Addr, uptr Size, UNUSED uptr Flags,
81           UNUSED MapPlatformData *Data) {
82  if (munmap(Addr, Size) != 0)
83    dieOnMapUnmapError();
84}
85
86void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,
87                      UNUSED MapPlatformData *Data) {
88  void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);
89  while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
90  }
91}
92
93// Calling getenv should be fine (c)(tm) at any time.
94const char *getEnv(const char *Name) { return getenv(Name); }
95
96namespace {
97enum State : u32 { Unlocked = 0, Locked = 1, Sleeping = 2 };
98}
99
100bool HybridMutex::tryLock() {
101  return atomic_compare_exchange(&M, Unlocked, Locked) == Unlocked;
102}
103
104// The following is based on https://akkadia.org/drepper/futex.pdf.
105void HybridMutex::lockSlow() {
106  u32 V = atomic_compare_exchange(&M, Unlocked, Locked);
107  if (V == Unlocked)
108    return;
109  if (V != Sleeping)
110    V = atomic_exchange(&M, Sleeping, memory_order_acquire);
111  while (V != Unlocked) {
112    syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAIT_PRIVATE, Sleeping,
113            nullptr, nullptr, 0);
114    V = atomic_exchange(&M, Sleeping, memory_order_acquire);
115  }
116}
117
118void HybridMutex::unlock() {
119  if (atomic_fetch_sub(&M, 1U, memory_order_release) != Locked) {
120    atomic_store(&M, Unlocked, memory_order_release);
121    syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAKE_PRIVATE, 1,
122            nullptr, nullptr, 0);
123  }
124}
125
126u64 getMonotonicTime() {
127  timespec TS;
128  clock_gettime(CLOCK_MONOTONIC, &TS);
129  return static_cast<u64>(TS.tv_sec) * (1000ULL * 1000 * 1000) +
130         static_cast<u64>(TS.tv_nsec);
131}
132
133u32 getNumberOfCPUs() {
134  cpu_set_t CPUs;
135  // sched_getaffinity can fail for a variety of legitimate reasons (lack of
136  // CAP_SYS_NICE, syscall filtering, etc), in which case we shall return 0.
137  if (sched_getaffinity(0, sizeof(cpu_set_t), &CPUs) != 0)
138    return 0;
139  return static_cast<u32>(CPU_COUNT(&CPUs));
140}
141
142u32 getThreadID() {
143#if SCUDO_ANDROID
144  return static_cast<u32>(gettid());
145#else
146  return static_cast<u32>(syscall(SYS_gettid));
147#endif
148}
149
150// Blocking is possibly unused if the getrandom block is not compiled in.
151bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {
152  if (!Buffer || !Length || Length > MaxRandomLength)
153    return false;
154  ssize_t ReadBytes;
155#if defined(SYS_getrandom)
156#if !defined(GRND_NONBLOCK)
157#define GRND_NONBLOCK 1
158#endif
159  // Up to 256 bytes, getrandom will not be interrupted.
160  ReadBytes =
161      syscall(SYS_getrandom, Buffer, Length, Blocking ? 0 : GRND_NONBLOCK);
162  if (ReadBytes == static_cast<ssize_t>(Length))
163    return true;
164#endif // defined(SYS_getrandom)
165  // Up to 256 bytes, a read off /dev/urandom will not be interrupted.
166  // Blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
167  const int FileDesc = open("/dev/urandom", O_RDONLY);
168  if (FileDesc == -1)
169    return false;
170  ReadBytes = read(FileDesc, Buffer, Length);
171  close(FileDesc);
172  return (ReadBytes == static_cast<ssize_t>(Length));
173}
174
175// Allocation free syslog-like API.
176extern "C" WEAK int async_safe_write_log(int pri, const char *tag,
177                                         const char *msg);
178
179void outputRaw(const char *Buffer) {
180  if (&async_safe_write_log) {
181    constexpr s32 AndroidLogInfo = 4;
182    constexpr uptr MaxLength = 1024U;
183    char LocalBuffer[MaxLength];
184    while (strlen(Buffer) > MaxLength) {
185      uptr P;
186      for (P = MaxLength - 1; P > 0; P--) {
187        if (Buffer[P] == '\n') {
188          memcpy(LocalBuffer, Buffer, P);
189          LocalBuffer[P] = '\0';
190          async_safe_write_log(AndroidLogInfo, "scudo", LocalBuffer);
191          Buffer = &Buffer[P + 1];
192          break;
193        }
194      }
195      // If no newline was found, just log the buffer.
196      if (P == 0)
197        break;
198    }
199    async_safe_write_log(AndroidLogInfo, "scudo", Buffer);
200  } else {
201    write(2, Buffer, strlen(Buffer));
202  }
203}
204
205extern "C" WEAK void android_set_abort_message(const char *);
206
207void setAbortMessage(const char *Message) {
208  if (&android_set_abort_message)
209    android_set_abort_message(Message);
210}
211
212} // namespace scudo
213
214#endif // SCUDO_LINUX
215