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 <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/mman.h>
26#include <sys/stat.h>
27#include <sys/syscall.h>
28#include <sys/time.h>
29#include <time.h>
30#include <unistd.h>
31
32#if SCUDO_ANDROID
33#include <sys/prctl.h>
34// Definitions of prctl arguments to set a vma name in Android kernels.
35#define ANDROID_PR_SET_VMA 0x53564d41
36#define ANDROID_PR_SET_VMA_ANON_NAME 0
37#endif
38
39namespace scudo {
40
41uptr getPageSize() { return static_cast<uptr>(sysconf(_SC_PAGESIZE)); }
42
43void NORETURN die() { abort(); }
44
45void *map(void *Addr, uptr Size, UNUSED const char *Name, uptr Flags,
46          UNUSED MapPlatformData *Data) {
47  int MmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
48  int MmapProt;
49  if (Flags & MAP_NOACCESS) {
50    MmapFlags |= MAP_NORESERVE;
51    MmapProt = PROT_NONE;
52  } else {
53    MmapProt = PROT_READ | PROT_WRITE;
54  }
55#if defined(__aarch64__)
56#ifndef PROT_MTE
57#define PROT_MTE 0x20
58#endif
59  if (Flags & MAP_MEMTAG)
60    MmapProt |= PROT_MTE;
61#endif
62  if (Addr)
63    MmapFlags |= MAP_FIXED;
64  void *P = mmap(Addr, Size, MmapProt, MmapFlags, -1, 0);
65  if (P == MAP_FAILED) {
66    if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)
67      dieOnMapUnmapError(errno == ENOMEM ? Size : 0);
68    return nullptr;
69  }
70#if SCUDO_ANDROID
71  if (Name)
72    prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, P, Size, Name);
73#endif
74  return P;
75}
76
77void unmap(void *Addr, uptr Size, UNUSED uptr Flags,
78           UNUSED MapPlatformData *Data) {
79  if (munmap(Addr, Size) != 0)
80    dieOnMapUnmapError();
81}
82
83void setMemoryPermission(uptr Addr, uptr Size, uptr Flags,
84                         UNUSED MapPlatformData *Data) {
85  int Prot = (Flags & MAP_NOACCESS) ? PROT_NONE : (PROT_READ | PROT_WRITE);
86  if (mprotect(reinterpret_cast<void *>(Addr), Size, Prot) != 0)
87    dieOnMapUnmapError();
88}
89
90void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,
91                      UNUSED MapPlatformData *Data) {
92  void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);
93
94  while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
95  }
96}
97
98// Calling getenv should be fine (c)(tm) at any time.
99const char *getEnv(const char *Name) { return getenv(Name); }
100
101namespace {
102enum State : u32 { Unlocked = 0, Locked = 1, Sleeping = 2 };
103}
104
105bool HybridMutex::tryLock() {
106  return atomic_compare_exchange(&M, Unlocked, Locked) == Unlocked;
107}
108
109// The following is based on https://akkadia.org/drepper/futex.pdf.
110void HybridMutex::lockSlow() {
111  u32 V = atomic_compare_exchange(&M, Unlocked, Locked);
112  if (V == Unlocked)
113    return;
114  if (V != Sleeping)
115    V = atomic_exchange(&M, Sleeping, memory_order_acquire);
116  while (V != Unlocked) {
117    syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAIT_PRIVATE, Sleeping,
118            nullptr, nullptr, 0);
119    V = atomic_exchange(&M, Sleeping, memory_order_acquire);
120  }
121}
122
123void HybridMutex::unlock() {
124  if (atomic_fetch_sub(&M, 1U, memory_order_release) != Locked) {
125    atomic_store(&M, Unlocked, memory_order_release);
126    syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAKE_PRIVATE, 1,
127            nullptr, nullptr, 0);
128  }
129}
130
131u64 getMonotonicTime() {
132  timespec TS;
133  clock_gettime(CLOCK_MONOTONIC, &TS);
134  return static_cast<u64>(TS.tv_sec) * (1000ULL * 1000 * 1000) +
135         static_cast<u64>(TS.tv_nsec);
136}
137
138u32 getNumberOfCPUs() {
139  cpu_set_t CPUs;
140  // sched_getaffinity can fail for a variety of legitimate reasons (lack of
141  // CAP_SYS_NICE, syscall filtering, etc), in which case we shall return 0.
142  if (sched_getaffinity(0, sizeof(cpu_set_t), &CPUs) != 0)
143    return 0;
144  return static_cast<u32>(CPU_COUNT(&CPUs));
145}
146
147u32 getThreadID() {
148#if SCUDO_ANDROID
149  return static_cast<u32>(gettid());
150#else
151  return static_cast<u32>(syscall(SYS_gettid));
152#endif
153}
154
155// Blocking is possibly unused if the getrandom block is not compiled in.
156bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {
157  if (!Buffer || !Length || Length > MaxRandomLength)
158    return false;
159  ssize_t ReadBytes;
160#if defined(SYS_getrandom)
161#if !defined(GRND_NONBLOCK)
162#define GRND_NONBLOCK 1
163#endif
164  // Up to 256 bytes, getrandom will not be interrupted.
165  ReadBytes =
166      syscall(SYS_getrandom, Buffer, Length, Blocking ? 0 : GRND_NONBLOCK);
167  if (ReadBytes == static_cast<ssize_t>(Length))
168    return true;
169#endif // defined(SYS_getrandom)
170  // Up to 256 bytes, a read off /dev/urandom will not be interrupted.
171  // Blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
172  const int FileDesc = open("/dev/urandom", O_RDONLY);
173  if (FileDesc == -1)
174    return false;
175  ReadBytes = read(FileDesc, Buffer, Length);
176  close(FileDesc);
177  return (ReadBytes == static_cast<ssize_t>(Length));
178}
179
180// Allocation free syslog-like API.
181extern "C" WEAK int async_safe_write_log(int pri, const char *tag,
182                                         const char *msg);
183
184static uptr GetRSSFromBuffer(const char *Buf) {
185  // The format of the file is:
186  // 1084 89 69 11 0 79 0
187  // We need the second number which is RSS in pages.
188  const char *Pos = Buf;
189  // Skip the first number.
190  while (*Pos >= '0' && *Pos <= '9')
191    Pos++;
192  // Skip whitespaces.
193  while (!(*Pos >= '0' && *Pos <= '9') && *Pos != 0)
194    Pos++;
195  // Read the number.
196  u64 Rss = 0;
197  for (; *Pos >= '0' && *Pos <= '9'; Pos++)
198    Rss = Rss * 10 + static_cast<u64>(*Pos) - '0';
199  return static_cast<uptr>(Rss * getPageSizeCached());
200}
201
202uptr GetRSS() {
203  // TODO: We currently use sanitizer_common's GetRSS which reads the
204  // RSS from /proc/self/statm by default. We might want to
205  // call getrusage directly, even if it's less accurate.
206  auto Fd = open("/proc/self/statm", O_RDONLY);
207  char Buf[64];
208  s64 Len = read(Fd, Buf, sizeof(Buf) - 1);
209  close(Fd);
210  if (Len <= 0)
211    return 0;
212  Buf[Len] = 0;
213
214  return GetRSSFromBuffer(Buf);
215}
216
217void outputRaw(const char *Buffer) {
218  if (&async_safe_write_log) {
219    constexpr s32 AndroidLogInfo = 4;
220    constexpr uptr MaxLength = 1024U;
221    char LocalBuffer[MaxLength];
222    while (strlen(Buffer) > MaxLength) {
223      uptr P;
224      for (P = MaxLength - 1; P > 0; P--) {
225        if (Buffer[P] == '\n') {
226          memcpy(LocalBuffer, Buffer, P);
227          LocalBuffer[P] = '\0';
228          async_safe_write_log(AndroidLogInfo, "scudo", LocalBuffer);
229          Buffer = &Buffer[P + 1];
230          break;
231        }
232      }
233      // If no newline was found, just log the buffer.
234      if (P == 0)
235        break;
236    }
237    async_safe_write_log(AndroidLogInfo, "scudo", Buffer);
238  } else {
239    (void)write(2, Buffer, strlen(Buffer));
240  }
241}
242
243extern "C" WEAK void android_set_abort_message(const char *);
244
245void setAbortMessage(const char *Message) {
246  if (&android_set_abort_message)
247    android_set_abort_message(Message);
248}
249
250} // namespace scudo
251
252#endif // SCUDO_LINUX
253