Christopher Ferris 99d92d18e3
[scudo] Add specific die functions for linux specific failures. (#68650)
While running into failures on unmap calls, it becomes difficult to
figure out what's wrong. Break the dieOnMapUnmapError into specific
versions for map, unmap, and then one for mprotect.

Also, put these in a common linux space so that all linux derived code
can reuse this code.
2023-10-13 12:19:11 -07:00

154 lines
4.1 KiB
C++

//===-- mem_map_linux.cpp ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "platform.h"
#if SCUDO_LINUX
#include "mem_map_linux.h"
#include "common.h"
#include "internal_defs.h"
#include "linux.h"
#include "mutex.h"
#include "report_linux.h"
#include "string_utils.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/futex.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#if SCUDO_ANDROID
// TODO(chiahungduan): Review if we still need the followings macros.
#include <sys/prctl.h>
// Definitions of prctl arguments to set a vma name in Android kernels.
#define ANDROID_PR_SET_VMA 0x53564d41
#define ANDROID_PR_SET_VMA_ANON_NAME 0
#endif
namespace scudo {
static void *mmapWrapper(uptr Addr, uptr Size, const char *Name, uptr Flags) {
int MmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
int MmapProt;
if (Flags & MAP_NOACCESS) {
MmapFlags |= MAP_NORESERVE;
MmapProt = PROT_NONE;
} else {
MmapProt = PROT_READ | PROT_WRITE;
}
#if defined(__aarch64__)
#ifndef PROT_MTE
#define PROT_MTE 0x20
#endif
if (Flags & MAP_MEMTAG)
MmapProt |= PROT_MTE;
#endif
if (Addr)
MmapFlags |= MAP_FIXED;
void *P =
mmap(reinterpret_cast<void *>(Addr), Size, MmapProt, MmapFlags, -1, 0);
if (P == MAP_FAILED) {
if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)
reportMapError(errno == ENOMEM ? Size : 0);
return nullptr;
}
#if SCUDO_ANDROID
if (Name)
prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, P, Size, Name);
#else
(void)Name;
#endif
return P;
}
bool MemMapLinux::mapImpl(uptr Addr, uptr Size, const char *Name, uptr Flags) {
void *P = mmapWrapper(Addr, Size, Name, Flags);
if (P == nullptr)
return false;
MapBase = reinterpret_cast<uptr>(P);
MapCapacity = Size;
return true;
}
void MemMapLinux::unmapImpl(uptr Addr, uptr Size) {
// If we unmap all the pages, also mark `MapBase` to 0 to indicate invalid
// status.
if (Size == MapCapacity) {
MapBase = MapCapacity = 0;
} else {
// This is partial unmap and is unmapping the pages from the beginning,
// shift `MapBase` to the new base.
if (MapBase == Addr)
MapBase = Addr + Size;
MapCapacity -= Size;
}
if (munmap(reinterpret_cast<void *>(Addr), Size) != 0)
reportUnmapError(Addr, Size);
}
bool MemMapLinux::remapImpl(uptr Addr, uptr Size, const char *Name,
uptr Flags) {
void *P = mmapWrapper(Addr, Size, Name, Flags);
if (reinterpret_cast<uptr>(P) != Addr)
reportMapError();
return true;
}
void MemMapLinux::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {
int Prot = (Flags & MAP_NOACCESS) ? PROT_NONE : (PROT_READ | PROT_WRITE);
if (mprotect(reinterpret_cast<void *>(Addr), Size, Prot) != 0)
reportProtectError(Addr, Size, Prot);
}
void MemMapLinux::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {
void *Addr = reinterpret_cast<void *>(From);
while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
}
}
bool ReservedMemoryLinux::createImpl(uptr Addr, uptr Size, const char *Name,
uptr Flags) {
ReservedMemoryLinux::MemMapT MemMap;
if (!MemMap.map(Addr, Size, Name, Flags | MAP_NOACCESS))
return false;
MapBase = MemMap.getBase();
MapCapacity = MemMap.getCapacity();
return true;
}
void ReservedMemoryLinux::releaseImpl() {
if (munmap(reinterpret_cast<void *>(getBase()), getCapacity()) != 0)
reportUnmapError(getBase(), getCapacity());
}
ReservedMemoryLinux::MemMapT ReservedMemoryLinux::dispatchImpl(uptr Addr,
uptr Size) {
return ReservedMemoryLinux::MemMapT(Addr, Size);
}
} // namespace scudo
#endif // SCUDO_LINUX