xref: /llvm-project/compiler-rt/lib/scudo/standalone/linux.cpp (revision 4634a480e0e5aa3116b397369fe3877a8dfe4dc0)
16d46ebefSNico Weber //===-- linux.cpp -----------------------------------------------*- C++ -*-===//
26d46ebefSNico Weber //
36d46ebefSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46d46ebefSNico Weber // See https://llvm.org/LICENSE.txt for license information.
56d46ebefSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66d46ebefSNico Weber //
76d46ebefSNico Weber //===----------------------------------------------------------------------===//
86d46ebefSNico Weber 
96d46ebefSNico Weber #include "platform.h"
106d46ebefSNico Weber 
116d46ebefSNico Weber #if SCUDO_LINUX
126d46ebefSNico Weber 
136d46ebefSNico Weber #include "common.h"
146a4c3959SChia-hung Duan #include "internal_defs.h"
156d46ebefSNico Weber #include "linux.h"
166d46ebefSNico Weber #include "mutex.h"
1799d92d18SChristopher Ferris #include "report_linux.h"
186d46ebefSNico Weber #include "string_utils.h"
196d46ebefSNico Weber 
206d46ebefSNico Weber #include <errno.h>
216d46ebefSNico Weber #include <fcntl.h>
226d46ebefSNico Weber #include <linux/futex.h>
236d46ebefSNico Weber #include <sched.h>
24cc02d61bSBastian Kersting #include <stdio.h>
256d46ebefSNico Weber #include <stdlib.h>
266d46ebefSNico Weber #include <string.h>
276d46ebefSNico Weber #include <sys/mman.h>
286d46ebefSNico Weber #include <sys/stat.h>
296d46ebefSNico Weber #include <sys/syscall.h>
306d46ebefSNico Weber #include <sys/time.h>
316d46ebefSNico Weber #include <time.h>
326d46ebefSNico Weber #include <unistd.h>
336d46ebefSNico Weber 
34016e59bfSAlex Brachet #if SCUDO_ANDROID
356d46ebefSNico Weber #include <sys/prctl.h>
36016e59bfSAlex Brachet // Definitions of prctl arguments to set a vma name in Android kernels.
37016e59bfSAlex Brachet #define ANDROID_PR_SET_VMA 0x53564d41
38016e59bfSAlex Brachet #define ANDROID_PR_SET_VMA_ANON_NAME 0
396d46ebefSNico Weber #endif
406d46ebefSNico Weber 
416d46ebefSNico Weber namespace scudo {
426d46ebefSNico Weber 
43*4634a480SChristopher Ferris #if !defined(SCUDO_PAGE_SIZE)
44*4634a480SChristopher Ferris // This function is only used when page size is not hard-coded.
456d46ebefSNico Weber uptr getPageSize() { return static_cast<uptr>(sysconf(_SC_PAGESIZE)); }
46*4634a480SChristopher Ferris #endif
476d46ebefSNico Weber 
486d46ebefSNico Weber void NORETURN die() { abort(); }
496d46ebefSNico Weber 
50f5fffbe2SChia-hung Duan // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
516d46ebefSNico Weber void *map(void *Addr, uptr Size, UNUSED const char *Name, uptr Flags,
526d46ebefSNico Weber           UNUSED MapPlatformData *Data) {
538095449eSKostya Kortchinsky   int MmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
546d46ebefSNico Weber   int MmapProt;
556d46ebefSNico Weber   if (Flags & MAP_NOACCESS) {
566d46ebefSNico Weber     MmapFlags |= MAP_NORESERVE;
576d46ebefSNico Weber     MmapProt = PROT_NONE;
586d46ebefSNico Weber   } else {
596d46ebefSNico Weber     MmapProt = PROT_READ | PROT_WRITE;
603f71ce85SPeter Collingbourne   }
61dfa40840SPeter Collingbourne #if defined(__aarch64__)
62dfa40840SPeter Collingbourne #ifndef PROT_MTE
63dfa40840SPeter Collingbourne #define PROT_MTE 0x20
64dfa40840SPeter Collingbourne #endif
65c299d198SPeter Collingbourne   if (Flags & MAP_MEMTAG)
66c299d198SPeter Collingbourne     MmapProt |= PROT_MTE;
67c299d198SPeter Collingbourne #endif
6878e70ceeSVitaly Buka   if (Addr)
696d46ebefSNico Weber     MmapFlags |= MAP_FIXED;
706d46ebefSNico Weber   void *P = mmap(Addr, Size, MmapProt, MmapFlags, -1, 0);
716d46ebefSNico Weber   if (P == MAP_FAILED) {
726d46ebefSNico Weber     if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)
7399d92d18SChristopher Ferris       reportMapError(errno == ENOMEM ? Size : 0);
746d46ebefSNico Weber     return nullptr;
756d46ebefSNico Weber   }
76016e59bfSAlex Brachet #if SCUDO_ANDROID
773f71ce85SPeter Collingbourne   if (Name)
78016e59bfSAlex Brachet     prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, P, Size, Name);
79016e59bfSAlex Brachet #endif
806d46ebefSNico Weber   return P;
816d46ebefSNico Weber }
826d46ebefSNico Weber 
83f5fffbe2SChia-hung Duan // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
846d46ebefSNico Weber void unmap(void *Addr, uptr Size, UNUSED uptr Flags,
856d46ebefSNico Weber            UNUSED MapPlatformData *Data) {
866d46ebefSNico Weber   if (munmap(Addr, Size) != 0)
8799d92d18SChristopher Ferris     reportUnmapError(reinterpret_cast<uptr>(Addr), Size);
886d46ebefSNico Weber }
896d46ebefSNico Weber 
90f5fffbe2SChia-hung Duan // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
913f71ce85SPeter Collingbourne void setMemoryPermission(uptr Addr, uptr Size, uptr Flags,
923f71ce85SPeter Collingbourne                          UNUSED MapPlatformData *Data) {
933f71ce85SPeter Collingbourne   int Prot = (Flags & MAP_NOACCESS) ? PROT_NONE : (PROT_READ | PROT_WRITE);
943f71ce85SPeter Collingbourne   if (mprotect(reinterpret_cast<void *>(Addr), Size, Prot) != 0)
9599d92d18SChristopher Ferris     reportProtectError(Addr, Size, Prot);
963f71ce85SPeter Collingbourne }
973f71ce85SPeter Collingbourne 
98f5fffbe2SChia-hung Duan // TODO: Will be deprecated. Use the interfaces in MemMapLinux instead.
996d46ebefSNico Weber void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,
1006d46ebefSNico Weber                       UNUSED MapPlatformData *Data) {
1016d46ebefSNico Weber   void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);
1024458e8c4SVitaly Buka 
1036d46ebefSNico Weber   while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
1046d46ebefSNico Weber   }
1056d46ebefSNico Weber }
1066d46ebefSNico Weber 
1076d46ebefSNico Weber // Calling getenv should be fine (c)(tm) at any time.
1086d46ebefSNico Weber const char *getEnv(const char *Name) { return getenv(Name); }
1096d46ebefSNico Weber 
1106d46ebefSNico Weber namespace {
1116d46ebefSNico Weber enum State : u32 { Unlocked = 0, Locked = 1, Sleeping = 2 };
1126d46ebefSNico Weber }
1136d46ebefSNico Weber 
1146d46ebefSNico Weber bool HybridMutex::tryLock() {
1153ef766adSChia-hung Duan   return atomic_compare_exchange_strong(&M, Unlocked, Locked,
1163ef766adSChia-hung Duan                                         memory_order_acquire) == Unlocked;
1176d46ebefSNico Weber }
1186d46ebefSNico Weber 
1196d46ebefSNico Weber // The following is based on https://akkadia.org/drepper/futex.pdf.
1206d46ebefSNico Weber void HybridMutex::lockSlow() {
1213ef766adSChia-hung Duan   u32 V = atomic_compare_exchange_strong(&M, Unlocked, Locked,
1223ef766adSChia-hung Duan                                          memory_order_acquire);
1236d46ebefSNico Weber   if (V == Unlocked)
1246d46ebefSNico Weber     return;
1256d46ebefSNico Weber   if (V != Sleeping)
1266d46ebefSNico Weber     V = atomic_exchange(&M, Sleeping, memory_order_acquire);
1276d46ebefSNico Weber   while (V != Unlocked) {
1286d46ebefSNico Weber     syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAIT_PRIVATE, Sleeping,
1296d46ebefSNico Weber             nullptr, nullptr, 0);
1306d46ebefSNico Weber     V = atomic_exchange(&M, Sleeping, memory_order_acquire);
1316d46ebefSNico Weber   }
1326d46ebefSNico Weber }
1336d46ebefSNico Weber 
1346d46ebefSNico Weber void HybridMutex::unlock() {
1356d46ebefSNico Weber   if (atomic_fetch_sub(&M, 1U, memory_order_release) != Locked) {
1366d46ebefSNico Weber     atomic_store(&M, Unlocked, memory_order_release);
1376d46ebefSNico Weber     syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAKE_PRIVATE, 1,
1386d46ebefSNico Weber             nullptr, nullptr, 0);
1396d46ebefSNico Weber   }
1406d46ebefSNico Weber }
1416d46ebefSNico Weber 
1426a4c3959SChia-hung Duan void HybridMutex::assertHeldImpl() {
1436a4c3959SChia-hung Duan   CHECK(atomic_load(&M, memory_order_acquire) != Unlocked);
1446a4c3959SChia-hung Duan }
1456a4c3959SChia-hung Duan 
1466d46ebefSNico Weber u64 getMonotonicTime() {
1476d46ebefSNico Weber   timespec TS;
1486d46ebefSNico Weber   clock_gettime(CLOCK_MONOTONIC, &TS);
1496d46ebefSNico Weber   return static_cast<u64>(TS.tv_sec) * (1000ULL * 1000 * 1000) +
1506d46ebefSNico Weber          static_cast<u64>(TS.tv_nsec);
1516d46ebefSNico Weber }
1526d46ebefSNico Weber 
15332be3405SChristopher Ferris u64 getMonotonicTimeFast() {
15432be3405SChristopher Ferris #if defined(CLOCK_MONOTONIC_COARSE)
15532be3405SChristopher Ferris   timespec TS;
15632be3405SChristopher Ferris   clock_gettime(CLOCK_MONOTONIC_COARSE, &TS);
15732be3405SChristopher Ferris   return static_cast<u64>(TS.tv_sec) * (1000ULL * 1000 * 1000) +
15832be3405SChristopher Ferris          static_cast<u64>(TS.tv_nsec);
15932be3405SChristopher Ferris #else
16032be3405SChristopher Ferris   return getMonotonicTime();
16132be3405SChristopher Ferris #endif
16232be3405SChristopher Ferris }
16332be3405SChristopher Ferris 
1646d46ebefSNico Weber u32 getNumberOfCPUs() {
1656d46ebefSNico Weber   cpu_set_t CPUs;
166561fa844SKostya Kortchinsky   // sched_getaffinity can fail for a variety of legitimate reasons (lack of
167561fa844SKostya Kortchinsky   // CAP_SYS_NICE, syscall filtering, etc), in which case we shall return 0.
168561fa844SKostya Kortchinsky   if (sched_getaffinity(0, sizeof(cpu_set_t), &CPUs) != 0)
169561fa844SKostya Kortchinsky     return 0;
1706d46ebefSNico Weber   return static_cast<u32>(CPU_COUNT(&CPUs));
1716d46ebefSNico Weber }
1726d46ebefSNico Weber 
17321d50019SPeter Collingbourne u32 getThreadID() {
17421d50019SPeter Collingbourne #if SCUDO_ANDROID
17521d50019SPeter Collingbourne   return static_cast<u32>(gettid());
17621d50019SPeter Collingbourne #else
17721d50019SPeter Collingbourne   return static_cast<u32>(syscall(SYS_gettid));
17821d50019SPeter Collingbourne #endif
17921d50019SPeter Collingbourne }
18021d50019SPeter Collingbourne 
1816d46ebefSNico Weber // Blocking is possibly unused if the getrandom block is not compiled in.
1826d46ebefSNico Weber bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {
1836d46ebefSNico Weber   if (!Buffer || !Length || Length > MaxRandomLength)
1846d46ebefSNico Weber     return false;
1856d46ebefSNico Weber   ssize_t ReadBytes;
1866d46ebefSNico Weber #if defined(SYS_getrandom)
1876d46ebefSNico Weber #if !defined(GRND_NONBLOCK)
1886d46ebefSNico Weber #define GRND_NONBLOCK 1
1896d46ebefSNico Weber #endif
1906d46ebefSNico Weber   // Up to 256 bytes, getrandom will not be interrupted.
1916d46ebefSNico Weber   ReadBytes =
1926d46ebefSNico Weber       syscall(SYS_getrandom, Buffer, Length, Blocking ? 0 : GRND_NONBLOCK);
1936d46ebefSNico Weber   if (ReadBytes == static_cast<ssize_t>(Length))
1946d46ebefSNico Weber     return true;
1956d46ebefSNico Weber #endif // defined(SYS_getrandom)
1966d46ebefSNico Weber   // Up to 256 bytes, a read off /dev/urandom will not be interrupted.
1976d46ebefSNico Weber   // Blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
1986d46ebefSNico Weber   const int FileDesc = open("/dev/urandom", O_RDONLY);
1996d46ebefSNico Weber   if (FileDesc == -1)
2006d46ebefSNico Weber     return false;
2016d46ebefSNico Weber   ReadBytes = read(FileDesc, Buffer, Length);
2026d46ebefSNico Weber   close(FileDesc);
2036d46ebefSNico Weber   return (ReadBytes == static_cast<ssize_t>(Length));
2046d46ebefSNico Weber }
2056d46ebefSNico Weber 
206a1f6ff26SKostya Kortchinsky // Allocation free syslog-like API.
207a1f6ff26SKostya Kortchinsky extern "C" WEAK int async_safe_write_log(int pri, const char *tag,
208a1f6ff26SKostya Kortchinsky                                          const char *msg);
209a1f6ff26SKostya Kortchinsky 
2106d46ebefSNico Weber void outputRaw(const char *Buffer) {
211a1f6ff26SKostya Kortchinsky   if (&async_safe_write_log) {
212a1f6ff26SKostya Kortchinsky     constexpr s32 AndroidLogInfo = 4;
21315754accSKostya Kortchinsky     constexpr uptr MaxLength = 1024U;
21415754accSKostya Kortchinsky     char LocalBuffer[MaxLength];
21515754accSKostya Kortchinsky     while (strlen(Buffer) > MaxLength) {
21615754accSKostya Kortchinsky       uptr P;
21715754accSKostya Kortchinsky       for (P = MaxLength - 1; P > 0; P--) {
21815754accSKostya Kortchinsky         if (Buffer[P] == '\n') {
21915754accSKostya Kortchinsky           memcpy(LocalBuffer, Buffer, P);
22015754accSKostya Kortchinsky           LocalBuffer[P] = '\0';
22115754accSKostya Kortchinsky           async_safe_write_log(AndroidLogInfo, "scudo", LocalBuffer);
22215754accSKostya Kortchinsky           Buffer = &Buffer[P + 1];
22315754accSKostya Kortchinsky           break;
22415754accSKostya Kortchinsky         }
22515754accSKostya Kortchinsky       }
22615754accSKostya Kortchinsky       // If no newline was found, just log the buffer.
22715754accSKostya Kortchinsky       if (P == 0)
22815754accSKostya Kortchinsky         break;
22915754accSKostya Kortchinsky     }
230a1f6ff26SKostya Kortchinsky     async_safe_write_log(AndroidLogInfo, "scudo", Buffer);
231a1f6ff26SKostya Kortchinsky   } else {
2321ef0e94dSFangrui Song     (void)write(2, Buffer, strlen(Buffer));
2336d46ebefSNico Weber   }
234a1f6ff26SKostya Kortchinsky }
2356d46ebefSNico Weber 
2366d46ebefSNico Weber extern "C" WEAK void android_set_abort_message(const char *);
2376d46ebefSNico Weber 
2386d46ebefSNico Weber void setAbortMessage(const char *Message) {
2396d46ebefSNico Weber   if (&android_set_abort_message)
2406d46ebefSNico Weber     android_set_abort_message(Message);
2416d46ebefSNico Weber }
2426d46ebefSNico Weber 
2436d46ebefSNico Weber } // namespace scudo
2446d46ebefSNico Weber 
2456d46ebefSNico Weber #endif // SCUDO_LINUX
246