xref: /freebsd/contrib/llvm-project/compiler-rt/lib/scudo/standalone/mem_map_linux.cpp (revision 95eb4b873b6a8b527c5bd78d7191975dfca38998)
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 
44 namespace scudo {
45 
46 static 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 
81 bool 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 
91 void 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 
108 bool 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 
116 void 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 
122 void 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 
129 bool 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 
141 void ReservedMemoryLinux::releaseImpl() {
142   if (munmap(reinterpret_cast<void *>(getBase()), getCapacity()) != 0)
143     reportUnmapError(getBase(), getCapacity());
144 }
145 
146 ReservedMemoryLinux::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