1 //===-- linux.cpp -----------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
14 #include "internal_defs.h"
17 #include "report_linux.h"
18 #include "string_utils.h"
22 #include <linux/futex.h>
29 #include <sys/syscall.h>
35 #include <sys/prctl.h>
36 // Definitions of prctl arguments to set a vma name in Android kernels.
37 #define ANDROID_PR_SET_VMA 0x53564d41
38 #define ANDROID_PR_SET_VMA_ANON_NAME 0
43 uptr
getPageSize() { return static_cast<uptr
>(sysconf(_SC_PAGESIZE
)); }
45 void NORETURN
die() { abort(); }
47 // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
48 void *map(void *Addr
, uptr Size
, UNUSED
const char *Name
, uptr Flags
,
49 UNUSED MapPlatformData
*Data
) {
50 int MmapFlags
= MAP_PRIVATE
| MAP_ANONYMOUS
;
52 if (Flags
& MAP_NOACCESS
) {
53 MmapFlags
|= MAP_NORESERVE
;
56 MmapProt
= PROT_READ
| PROT_WRITE
;
58 #if defined(__aarch64__)
62 if (Flags
& MAP_MEMTAG
)
66 MmapFlags
|= MAP_FIXED
;
67 void *P
= mmap(Addr
, Size
, MmapProt
, MmapFlags
, -1, 0);
68 if (P
== MAP_FAILED
) {
69 if (!(Flags
& MAP_ALLOWNOMEM
) || errno
!= ENOMEM
)
70 reportMapError(errno
== ENOMEM
? Size
: 0);
75 prctl(ANDROID_PR_SET_VMA
, ANDROID_PR_SET_VMA_ANON_NAME
, P
, Size
, Name
);
80 // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
81 void unmap(void *Addr
, uptr Size
, UNUSED uptr Flags
,
82 UNUSED MapPlatformData
*Data
) {
83 if (munmap(Addr
, Size
) != 0)
84 reportUnmapError(reinterpret_cast<uptr
>(Addr
), Size
);
87 // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
88 void setMemoryPermission(uptr Addr
, uptr Size
, uptr Flags
,
89 UNUSED MapPlatformData
*Data
) {
90 int Prot
= (Flags
& MAP_NOACCESS
) ? PROT_NONE
: (PROT_READ
| PROT_WRITE
);
91 if (mprotect(reinterpret_cast<void *>(Addr
), Size
, Prot
) != 0)
92 reportProtectError(Addr
, Size
, Prot
);
95 // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
96 void releasePagesToOS(uptr BaseAddress
, uptr Offset
, uptr Size
,
97 UNUSED MapPlatformData
*Data
) {
98 void *Addr
= reinterpret_cast<void *>(BaseAddress
+ Offset
);
100 while (madvise(Addr
, Size
, MADV_DONTNEED
) == -1 && errno
== EAGAIN
) {
104 // Calling getenv should be fine (c)(tm) at any time.
105 const char *getEnv(const char *Name
) { return getenv(Name
); }
108 enum State
: u32
{ Unlocked
= 0, Locked
= 1, Sleeping
= 2 };
111 bool HybridMutex::tryLock() {
112 return atomic_compare_exchange_strong(&M
, Unlocked
, Locked
,
113 memory_order_acquire
) == Unlocked
;
116 // The following is based on https://akkadia.org/drepper/futex.pdf.
117 void HybridMutex::lockSlow() {
118 u32 V
= atomic_compare_exchange_strong(&M
, Unlocked
, Locked
,
119 memory_order_acquire
);
123 V
= atomic_exchange(&M
, Sleeping
, memory_order_acquire
);
124 while (V
!= Unlocked
) {
125 syscall(SYS_futex
, reinterpret_cast<uptr
>(&M
), FUTEX_WAIT_PRIVATE
, Sleeping
,
126 nullptr, nullptr, 0);
127 V
= atomic_exchange(&M
, Sleeping
, memory_order_acquire
);
131 void HybridMutex::unlock() {
132 if (atomic_fetch_sub(&M
, 1U, memory_order_release
) != Locked
) {
133 atomic_store(&M
, Unlocked
, memory_order_release
);
134 syscall(SYS_futex
, reinterpret_cast<uptr
>(&M
), FUTEX_WAKE_PRIVATE
, 1,
135 nullptr, nullptr, 0);
139 void HybridMutex::assertHeldImpl() {
140 CHECK(atomic_load(&M
, memory_order_acquire
) != Unlocked
);
143 u64
getMonotonicTime() {
145 clock_gettime(CLOCK_MONOTONIC
, &TS
);
146 return static_cast<u64
>(TS
.tv_sec
) * (1000ULL * 1000 * 1000) +
147 static_cast<u64
>(TS
.tv_nsec
);
150 u64
getMonotonicTimeFast() {
151 #if defined(CLOCK_MONOTONIC_COARSE)
153 clock_gettime(CLOCK_MONOTONIC_COARSE
, &TS
);
154 return static_cast<u64
>(TS
.tv_sec
) * (1000ULL * 1000 * 1000) +
155 static_cast<u64
>(TS
.tv_nsec
);
157 return getMonotonicTime();
161 u32
getNumberOfCPUs() {
163 // sched_getaffinity can fail for a variety of legitimate reasons (lack of
164 // CAP_SYS_NICE, syscall filtering, etc), in which case we shall return 0.
165 if (sched_getaffinity(0, sizeof(cpu_set_t
), &CPUs
) != 0)
167 return static_cast<u32
>(CPU_COUNT(&CPUs
));
172 return static_cast<u32
>(gettid());
174 return static_cast<u32
>(syscall(SYS_gettid
));
178 // Blocking is possibly unused if the getrandom block is not compiled in.
179 bool getRandom(void *Buffer
, uptr Length
, UNUSED
bool Blocking
) {
180 if (!Buffer
|| !Length
|| Length
> MaxRandomLength
)
183 #if defined(SYS_getrandom)
184 #if !defined(GRND_NONBLOCK)
185 #define GRND_NONBLOCK 1
187 // Up to 256 bytes, getrandom will not be interrupted.
189 syscall(SYS_getrandom
, Buffer
, Length
, Blocking
? 0 : GRND_NONBLOCK
);
190 if (ReadBytes
== static_cast<ssize_t
>(Length
))
192 #endif // defined(SYS_getrandom)
193 // Up to 256 bytes, a read off /dev/urandom will not be interrupted.
194 // Blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
195 const int FileDesc
= open("/dev/urandom", O_RDONLY
);
198 ReadBytes
= read(FileDesc
, Buffer
, Length
);
200 return (ReadBytes
== static_cast<ssize_t
>(Length
));
203 // Allocation free syslog-like API.
204 extern "C" WEAK
int async_safe_write_log(int pri
, const char *tag
,
207 void outputRaw(const char *Buffer
) {
208 if (&async_safe_write_log
) {
209 constexpr s32 AndroidLogInfo
= 4;
210 constexpr uptr MaxLength
= 1024U;
211 char LocalBuffer
[MaxLength
];
212 while (strlen(Buffer
) > MaxLength
) {
214 for (P
= MaxLength
- 1; P
> 0; P
--) {
215 if (Buffer
[P
] == '\n') {
216 memcpy(LocalBuffer
, Buffer
, P
);
217 LocalBuffer
[P
] = '\0';
218 async_safe_write_log(AndroidLogInfo
, "scudo", LocalBuffer
);
219 Buffer
= &Buffer
[P
+ 1];
223 // If no newline was found, just log the buffer.
227 async_safe_write_log(AndroidLogInfo
, "scudo", Buffer
);
229 (void)write(2, Buffer
, strlen(Buffer
));
233 extern "C" WEAK
void android_set_abort_message(const char *);
235 void setAbortMessage(const char *Message
) {
236 if (&android_set_abort_message
)
237 android_set_abort_message(Message
);
242 #endif // SCUDO_LINUX