1//===-- mem_map_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 "mem_map_linux.h"
14
15#include "common.h"
16#include "internal_defs.h"
17#include "linux.h"
18#include "mutex.h"
19#include "report_linux.h"
20#include "string_utils.h"
21
22#include <errno.h>
23#include <fcntl.h>
24#include <linux/futex.h>
25#include <sched.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/mman.h>
30#include <sys/stat.h>
31#include <sys/syscall.h>
32#include <sys/time.h>
33#include <time.h>
34#include <unistd.h>
35
36#if SCUDO_ANDROID
37// TODO(chiahungduan): Review if we still need the followings macros.
38#include <sys/prctl.h>
39// Definitions of prctl arguments to set a vma name in Android kernels.
40#define ANDROID_PR_SET_VMA 0x53564d41
41#define ANDROID_PR_SET_VMA_ANON_NAME 0
42#endif
43
44namespace scudo {
45
46static void *mmapWrapper(uptr Addr, uptr Size, const char *Name, uptr Flags) {
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 =
65      mmap(reinterpret_cast<void *>(Addr), Size, MmapProt, MmapFlags, -1, 0);
66  if (P == MAP_FAILED) {
67    if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)
68      reportMapError(errno == ENOMEM ? Size : 0);
69    return nullptr;
70  }
71#if SCUDO_ANDROID
72  if (Name)
73    prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, P, Size, Name);
74#else
75  (void)Name;
76#endif
77
78  return P;
79}
80
81bool MemMapLinux::mapImpl(uptr Addr, uptr Size, const char *Name, uptr Flags) {
82  void *P = mmapWrapper(Addr, Size, Name, Flags);
83  if (P == nullptr)
84    return false;
85
86  MapBase = reinterpret_cast<uptr>(P);
87  MapCapacity = Size;
88  return true;
89}
90
91void MemMapLinux::unmapImpl(uptr Addr, uptr Size) {
92  // If we unmap all the pages, also mark `MapBase` to 0 to indicate invalid
93  // status.
94  if (Size == MapCapacity) {
95    MapBase = MapCapacity = 0;
96  } else {
97    // This is partial unmap and is unmapping the pages from the beginning,
98    // shift `MapBase` to the new base.
99    if (MapBase == Addr)
100      MapBase = Addr + Size;
101    MapCapacity -= Size;
102  }
103
104  if (munmap(reinterpret_cast<void *>(Addr), Size) != 0)
105    reportUnmapError(Addr, Size);
106}
107
108bool MemMapLinux::remapImpl(uptr Addr, uptr Size, const char *Name,
109                            uptr Flags) {
110  void *P = mmapWrapper(Addr, Size, Name, Flags);
111  if (reinterpret_cast<uptr>(P) != Addr)
112    reportMapError();
113  return true;
114}
115
116void MemMapLinux::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {
117  int Prot = (Flags & MAP_NOACCESS) ? PROT_NONE : (PROT_READ | PROT_WRITE);
118  if (mprotect(reinterpret_cast<void *>(Addr), Size, Prot) != 0)
119    reportProtectError(Addr, Size, Prot);
120}
121
122void MemMapLinux::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {
123  void *Addr = reinterpret_cast<void *>(From);
124
125  while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
126  }
127}
128
129bool ReservedMemoryLinux::createImpl(uptr Addr, uptr Size, const char *Name,
130                                     uptr Flags) {
131  ReservedMemoryLinux::MemMapT MemMap;
132  if (!MemMap.map(Addr, Size, Name, Flags | MAP_NOACCESS))
133    return false;
134
135  MapBase = MemMap.getBase();
136  MapCapacity = MemMap.getCapacity();
137
138  return true;
139}
140
141void ReservedMemoryLinux::releaseImpl() {
142  if (munmap(reinterpret_cast<void *>(getBase()), getCapacity()) != 0)
143    reportUnmapError(getBase(), getCapacity());
144}
145
146ReservedMemoryLinux::MemMapT ReservedMemoryLinux::dispatchImpl(uptr Addr,
147                                                               uptr Size) {
148  return ReservedMemoryLinux::MemMapT(Addr, Size);
149}
150
151} // namespace scudo
152
153#endif // SCUDO_LINUX
154