252 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "platform.h"10 11#if SCUDO_LINUX12 13#include "common.h"14#include "internal_defs.h"15#include "linux.h"16#include "mutex.h"17#include "report_linux.h"18#include "string_utils.h"19 20#include <errno.h>21#include <fcntl.h>22#include <linux/futex.h>23#include <sched.h>24#include <stdio.h>25#include <stdlib.h>26#include <string.h>27#include <sys/mman.h>28#include <sys/stat.h>29#include <sys/syscall.h>30#include <sys/time.h>31#include <time.h>32#include <unistd.h>33 34#if SCUDO_ANDROID35#include <sys/prctl.h>36// Definitions of prctl arguments to set a vma name in Android kernels.37#define ANDROID_PR_SET_VMA 0x53564d4138#define ANDROID_PR_SET_VMA_ANON_NAME 039#endif40 41namespace scudo {42 43#if !defined(SCUDO_PAGE_SIZE)44// This function is only used when page size is not hard-coded.45uptr getPageSize() { return static_cast<uptr>(sysconf(_SC_PAGESIZE)); }46#endif47 48void NORETURN die() { abort(); }49 50// TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.51void *map(void *Addr, uptr Size, UNUSED const char *Name, uptr Flags,52 UNUSED MapPlatformData *Data) {53 int MmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;54 int MmapProt;55 if (Flags & MAP_NOACCESS) {56 MmapFlags |= MAP_NORESERVE;57 MmapProt = PROT_NONE;58 } else {59 MmapProt = PROT_READ | PROT_WRITE;60 }61#if defined(__aarch64__)62#ifndef PROT_MTE63#define PROT_MTE 0x2064#endif65 if (Flags & MAP_MEMTAG)66 MmapProt |= PROT_MTE;67#endif68 if (Addr)69 MmapFlags |= MAP_FIXED;70 void *P = mmap(Addr, Size, MmapProt, MmapFlags, -1, 0);71 if (P == MAP_FAILED) {72 if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)73 reportMapError(errno == ENOMEM ? Size : 0);74 return nullptr;75 }76#if SCUDO_ANDROID77 if (Name)78 prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, P, Size, Name);79#endif80 return P;81}82 83// TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.84void unmap(void *Addr, uptr Size, UNUSED uptr Flags,85 UNUSED MapPlatformData *Data) {86 if (munmap(Addr, Size) != 0)87 reportUnmapError(reinterpret_cast<uptr>(Addr), Size);88}89 90// TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.91void setMemoryPermission(uptr Addr, uptr Size, uptr Flags,92 UNUSED MapPlatformData *Data) {93 int Prot = (Flags & MAP_NOACCESS) ? PROT_NONE : (PROT_READ | PROT_WRITE);94 if (mprotect(reinterpret_cast<void *>(Addr), Size, Prot) != 0)95 reportProtectError(Addr, Size, Prot);96}97 98// TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.99void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,100 UNUSED MapPlatformData *Data) {101 void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);102 103 while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {104 }105}106 107// Calling getenv should be fine (c)(tm) at any time.108const char *getEnv(const char *Name) { return getenv(Name); }109 110namespace {111enum State : u32 { Unlocked = 0, Locked = 1, Sleeping = 2 };112}113 114bool HybridMutex::tryLock() {115 return atomic_compare_exchange_strong(&M, Unlocked, Locked,116 memory_order_acquire) == Unlocked;117}118 119// The following is based on https://akkadia.org/drepper/futex.pdf.120void HybridMutex::lockSlow() {121 u32 V = atomic_compare_exchange_strong(&M, Unlocked, Locked,122 memory_order_acquire);123 if (V == Unlocked)124 return;125 if (V != Sleeping)126 V = atomic_exchange(&M, Sleeping, memory_order_acquire);127 while (V != Unlocked) {128 syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAIT_PRIVATE, Sleeping,129 nullptr, nullptr, 0);130 V = atomic_exchange(&M, Sleeping, memory_order_acquire);131 }132}133 134void HybridMutex::unlock() {135 if (atomic_fetch_sub(&M, 1U, memory_order_release) != Locked) {136 atomic_store(&M, Unlocked, memory_order_release);137 syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAKE_PRIVATE, 1,138 nullptr, nullptr, 0);139 }140}141 142void HybridMutex::assertHeldImpl() {143 CHECK(atomic_load(&M, memory_order_acquire) != Unlocked);144}145 146u64 getMonotonicTime() {147 timespec TS;148 clock_gettime(CLOCK_MONOTONIC, &TS);149 return static_cast<u64>(TS.tv_sec) * (1000ULL * 1000 * 1000) +150 static_cast<u64>(TS.tv_nsec);151}152 153u64 getMonotonicTimeFast() {154#if defined(CLOCK_MONOTONIC_COARSE)155 timespec TS;156 clock_gettime(CLOCK_MONOTONIC_COARSE, &TS);157 return static_cast<u64>(TS.tv_sec) * (1000ULL * 1000 * 1000) +158 static_cast<u64>(TS.tv_nsec);159#else160 return getMonotonicTime();161#endif162}163 164u32 getNumberOfCPUs() {165 cpu_set_t CPUs;166 // sched_getaffinity can fail for a variety of legitimate reasons (lack of167 // CAP_SYS_NICE, syscall filtering, etc), in which case we shall return 0.168 if (sched_getaffinity(0, sizeof(cpu_set_t), &CPUs) != 0)169 return 0;170 return static_cast<u32>(CPU_COUNT(&CPUs));171}172 173u32 getThreadID() {174#if SCUDO_ANDROID175 return static_cast<u32>(gettid());176#else177 return static_cast<u32>(syscall(SYS_gettid));178#endif179}180 181// Blocking is possibly unused if the getrandom block is not compiled in.182bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {183 if (!Buffer || !Length || Length > MaxRandomLength)184 return false;185 ssize_t ReadBytes;186#if defined(SYS_getrandom)187#if !defined(GRND_NONBLOCK)188#define GRND_NONBLOCK 1189#endif190 // Up to 256 bytes, getrandom will not be interrupted.191 ReadBytes =192 syscall(SYS_getrandom, Buffer, Length, Blocking ? 0 : GRND_NONBLOCK);193 if (ReadBytes == static_cast<ssize_t>(Length))194 return true;195 // If this system call is not implemented in the kernel, then we will try196 // and use /dev/urandom. Otherwise, if the syscall fails, return false197 // assuming that trying to read /dev/urandom will cause a delay waiting for198 // the random data to be usable.199 if (errno != ENOSYS)200 return false;201#endif // defined(SYS_getrandom)202 // Up to 256 bytes, a read off /dev/urandom will not be interrupted.203 // Blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.204 const int FileDesc = open("/dev/urandom", O_RDONLY);205 if (FileDesc == -1)206 return false;207 ReadBytes = read(FileDesc, Buffer, Length);208 close(FileDesc);209 return (ReadBytes == static_cast<ssize_t>(Length));210}211 212// Allocation free syslog-like API.213extern "C" WEAK int async_safe_write_log(int pri, const char *tag,214 const char *msg);215 216void outputRaw(const char *Buffer) {217 if (&async_safe_write_log) {218 constexpr s32 AndroidLogInfo = 4;219 constexpr uptr MaxLength = 1024U;220 char LocalBuffer[MaxLength];221 while (strlen(Buffer) > MaxLength) {222 uptr P;223 for (P = MaxLength - 1; P > 0; P--) {224 if (Buffer[P] == '\n') {225 memcpy(LocalBuffer, Buffer, P);226 LocalBuffer[P] = '\0';227 async_safe_write_log(AndroidLogInfo, "scudo", LocalBuffer);228 Buffer = &Buffer[P + 1];229 break;230 }231 }232 // If no newline was found, just log the buffer.233 if (P == 0)234 break;235 }236 async_safe_write_log(AndroidLogInfo, "scudo", Buffer);237 } else {238 (void)write(2, Buffer, strlen(Buffer));239 }240}241 242extern "C" WEAK void android_set_abort_message(const char *);243 244void setAbortMessage(const char *Message) {245 if (&android_set_abort_message)246 android_set_abort_message(Message);247}248 249} // namespace scudo250 251#endif // SCUDO_LINUX252