blob: 7b2e19956c7962d5abc601f42355cb4c54b02a43 [file] [log] [blame]
//===-- guarded_pool_allocator_posix.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 "gwp_asan/common.h"
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h"
#include "gwp_asan/utilities.h"
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#ifdef ANDROID
#include <sys/prctl.h>
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#endif // ANDROID
namespace {
void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) {
#ifdef ANDROID
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Mapping, Size, Name);
#endif // ANDROID
// Anonymous mapping names are only supported on Android.
return;
}
} // anonymous namespace
namespace gwp_asan {
void GuardedPoolAllocator::initPRNG() {
getThreadLocals()->RandomState =
static_cast<uint32_t>(time(nullptr) + getThreadID());
}
void *GuardedPoolAllocator::map(size_t Size, const char *Name) const {
assert((Size % State.PageSize) == 0);
void *Ptr = mmap(nullptr, Size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
checkWithErrorCode(Ptr != MAP_FAILED,
"Failed to map guarded pool allocator memory", errno);
MaybeSetMappingName(Ptr, Size, Name);
return Ptr;
}
void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const {
assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
assert((Size % State.PageSize) == 0);
checkWithErrorCode(munmap(Ptr, Size) == 0,
"Failed to unmap guarded pool allocator memory.", errno);
}
void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) {
assert((Size % State.PageSize) == 0);
void *Ptr =
mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
checkWithErrorCode(Ptr != MAP_FAILED,
"Failed to reserve guarded pool allocator memory", errno);
MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName);
return Ptr;
}
void GuardedPoolAllocator::unreserveGuardedPool() {
unmap(reinterpret_cast<void *>(State.GuardedPagePool),
State.GuardedPagePoolEnd - State.GuardedPagePool);
}
void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const {
assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
assert((Size % State.PageSize) == 0);
checkWithErrorCode(mprotect(Ptr, Size, PROT_READ | PROT_WRITE) == 0,
"Failed to allocate in guarded pool allocator memory",
errno);
MaybeSetMappingName(Ptr, Size, kGwpAsanAliveSlotName);
}
void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr,
size_t Size) const {
assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
assert((Size % State.PageSize) == 0);
// mmap() a PROT_NONE page over the address to release it to the system, if
// we used mprotect() here the system would count pages in the quarantine
// against the RSS.
checkWithErrorCode(
mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1,
0) != MAP_FAILED,
"Failed to deallocate in guarded pool allocator memory", errno);
MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName);
}
size_t GuardedPoolAllocator::getPlatformPageSize() {
return sysconf(_SC_PAGESIZE);
}
void GuardedPoolAllocator::installAtFork() {
static bool AtForkInstalled = false;
if (AtForkInstalled)
return;
AtForkInstalled = true;
auto Disable = []() {
if (auto *S = getSingleton())
S->disable();
};
auto Enable = []() {
if (auto *S = getSingleton())
S->enable();
};
pthread_atfork(Disable, Enable, Enable);
}
} // namespace gwp_asan