blob: 0622222b53b45298f7c6e860209a9c1ee981c2e2 [file] [log] [blame]
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <sys/mman.h>
#include "build/build_config.h"
#if defined(OS_MACOSX)
#include <mach/mach.h>
#if defined(OS_LINUX)
#include <sys/resource.h>
#include <algorithm>
namespace pdfium {
namespace base {
// |mmap| uses a nearby address if the hint address is blocked.
constexpr bool kHintIsAdvisory = true;
std::atomic<int32_t> s_allocPageErrorCode{0};
int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
switch (accessibility) {
case PageRead:
return PROT_READ;
case PageReadWrite:
case PageReadExecute:
case PageReadWriteExecute:
case PageInaccessible:
return PROT_NONE;
#if defined(OS_LINUX) && defined(ARCH_CPU_64_BITS)
// Multiple guarded memory regions may exceed the process address space limit.
// This function will raise or lower the limit by |amount|.
bool AdjustAddressSpaceLimit(int64_t amount) {
struct rlimit old_rlimit;
if (getrlimit(RLIMIT_AS, &old_rlimit))
return false;
const rlim_t new_limit =
CheckAdd(old_rlimit.rlim_cur, amount).ValueOrDefault(old_rlimit.rlim_max);
const struct rlimit new_rlimit = {std::min(new_limit, old_rlimit.rlim_max),
// setrlimit will fail if limit > old_rlimit.rlim_max.
return setrlimit(RLIMIT_AS, &new_rlimit) == 0;
// Current WASM guarded memory regions have 8 GiB of address space. There are
// schemes that reduce that to 4 GiB.
constexpr size_t kMinimumGuardedMemorySize = 1ULL << 32; // 4 GiB
#endif // defined(OS_LINUX) && defined(ARCH_CPU_64_BITS)
void* SystemAllocPagesInternal(void* hint,
size_t length,
PageAccessibilityConfiguration accessibility,
PageTag page_tag,
bool commit) {
#if defined(OS_MACOSX)
// Use a custom tag to make it easier to distinguish Partition Alloc regions
// in vmmap(1). Tags between 240-255 are supported.
DCHECK(PageTag::kFirst <= page_tag);
DCHECK(PageTag::kLast >= page_tag);
int fd = VM_MAKE_TAG(static_cast<int>(page_tag));
int fd = -1;
int access_flag = GetAccessFlags(accessibility);
void* ret =
mmap(hint, length, access_flag, MAP_ANONYMOUS | MAP_PRIVATE, fd, 0);
if (ret == MAP_FAILED) {
s_allocPageErrorCode = errno;
ret = nullptr;
return ret;
void* TrimMappingInternal(void* base,
size_t base_length,
size_t trim_length,
PageAccessibilityConfiguration accessibility,
bool commit,
size_t pre_slack,
size_t post_slack) {
void* ret = base;
// We can resize the allocation run. Release unneeded memory before and after
// the aligned range.
if (pre_slack) {
int res = munmap(base, pre_slack);
ret = reinterpret_cast<char*>(base) + pre_slack;
if (post_slack) {
int res = munmap(reinterpret_cast<char*>(ret) + trim_length, post_slack);
return ret;
bool SetSystemPagesAccessInternal(
void* address,
size_t length,
PageAccessibilityConfiguration accessibility) {
return 0 == mprotect(address, length, GetAccessFlags(accessibility));
void FreePagesInternal(void* address, size_t length) {
CHECK(!munmap(address, length));
#if defined(OS_LINUX) && defined(ARCH_CPU_64_BITS)
// Restore the address space limit.
if (length >= kMinimumGuardedMemorySize) {
void DecommitSystemPagesInternal(void* address, size_t length) {
// In POSIX, there is no decommit concept. Discarding is an effective way of
// implementing the Windows semantics where the OS is allowed to not swap the
// pages in the region.
// TODO(ajwong): Also explore setting PageInaccessible to make the protection
// semantics consistent between Windows and POSIX. This might have a perf cost
// though as both decommit and recommit would incur an extra syscall.
DiscardSystemPages(address, length);
bool RecommitSystemPagesInternal(void* address,
size_t length,
PageAccessibilityConfiguration accessibility) {
#if defined(OS_MACOSX)
// On macOS, to update accounting, we need to make another syscall. For more
// details, see
madvise(address, length, MADV_FREE_REUSE);
// On POSIX systems, the caller need simply read the memory to recommit it.
// This has the correct behavior because the API requires the permissions to
// be the same as before decommitting and all configurations can read.
return true;
void DiscardSystemPagesInternal(void* address, size_t length) {
#if defined(OS_MACOSX)
int ret = madvise(address, length, MADV_FREE_REUSABLE);
if (ret) {
// MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED.
ret = madvise(address, length, MADV_DONTNEED);
CHECK(0 == ret);
// We have experimented with other flags, but with suboptimal results.
// MADV_FREE (Linux): Makes our memory measurements less predictable;
// performance benefits unclear.
// Therefore, we just do the simple thing: MADV_DONTNEED.
CHECK(!madvise(address, length, MADV_DONTNEED));
} // namespace base
} // namespace pdfium