#include "partition_alloc/partition_address_space.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <ostream>
#include <string>
#include "partition_alloc/address_pool_manager.h"
#include "partition_alloc/build_config.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/compressed_pointer.h"
#include "partition_alloc/page_allocator.h"
#include "partition_alloc/partition_alloc_base/bits.h"
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/debug/alias.h"
#include "partition_alloc/partition_alloc_check.h"
#include "partition_alloc/partition_alloc_config.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/thread_isolation/thread_isolation.h"
#if PA_BUILDFLAG(IS_IOS)
#include <mach-o/dyld.h>
#endif
#if PA_BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
#if PA_CONFIG(ENABLE_SHADOW_METADATA) || PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
#include <sys/mman.h>
#endif
namespace partition_alloc::internal {
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
namespace {
#if PA_BUILDFLAG(IS_WIN)
PA_NOINLINE void HandlePoolAllocFailureOutOfVASpace() {
PA_NO_CODE_FOLDING();
PA_CHECK(false);
}
PA_NOINLINE void HandlePoolAllocFailureOutOfCommitCharge() {
PA_NO_CODE_FOLDING();
PA_CHECK(false);
}
#endif
PA_NOINLINE void HandlePoolAllocFailure() { … }
}
PartitionAddressSpace::PoolSetup PartitionAddressSpace::setup_;
#if PA_CONFIG(ENABLE_SHADOW_METADATA)
std::ptrdiff_t PartitionAddressSpace::regular_pool_shadow_offset_ = 0;
std::ptrdiff_t PartitionAddressSpace::brp_pool_shadow_offset_ = 0;
std::ptrdiff_t PartitionAddressSpace::configurable_pool_shadow_offset_ = 0;
int PartitionAddressSpace::regular_pool_fd_ = -1;
int PartitionAddressSpace::brp_pool_fd_ = -1;
int PartitionAddressSpace::configurable_pool_fd_ = -1;
uintptr_t PartitionAddressSpace::pool_shadow_address_ =
PartitionAddressSpace::kUninitializedPoolBaseAddress;
#endif
#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
#if !PA_BUILDFLAG(IS_IOS)
#error Dynamic pool size is only supported on iOS.
#endif
namespace {
bool IsIOSTestProcess() {
constexpr size_t path_buffer_size = 8192;
char executable_path[path_buffer_size];
uint32_t executable_length = path_buffer_size;
int rv = _NSGetExecutablePath(executable_path, &executable_length);
PA_CHECK(!rv);
size_t executable_path_length =
std::char_traits<char>::length(executable_path);
auto has_suffix = [&](const char* suffix) -> bool {
size_t suffix_length = std::char_traits<char>::length(suffix);
if (executable_path_length < suffix_length) {
return false;
}
return std::char_traits<char>::compare(
executable_path + (executable_path_length - suffix_length),
suffix, suffix_length) == 0;
};
return has_suffix("Runner") || has_suffix("ios_web_view_inttests");
}
}
PA_ALWAYS_INLINE size_t PartitionAddressSpace::RegularPoolSize() {
return IsIOSTestProcess() ? kRegularPoolSizeForIOSTestProcess
: kRegularPoolSize;
}
PA_ALWAYS_INLINE size_t PartitionAddressSpace::BRPPoolSize() {
return IsIOSTestProcess() ? kBRPPoolSizeForIOSTestProcess : kBRPPoolSize;
}
#endif
#if PA_CONFIG(ENABLE_SHADOW_METADATA)
size_t PartitionAddressSpace::RegularPoolShadowSize() {
return (RegularPoolSize() >> kSuperPageShift) << SystemPageShift();
}
size_t PartitionAddressSpace::BRPPoolShadowSize() {
return (BRPPoolSize() >> kSuperPageShift) << SystemPageShift();
}
size_t PartitionAddressSpace::ConfigurablePoolShadowSize() {
return (kConfigurablePoolMaxSize >> kSuperPageShift) << SystemPageShift();
}
#endif
void PartitionAddressSpace::Init() { … }
void PartitionAddressSpace::InitConfigurablePool(uintptr_t pool_base,
size_t size) { … }
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
void PartitionAddressSpace::InitThreadIsolatedPool(
ThreadIsolationOption thread_isolation) { … }
#endif
void PartitionAddressSpace::UninitForTesting() { … }
void PartitionAddressSpace::UninitConfigurablePoolForTesting() { … }
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
void PartitionAddressSpace::UninitThreadIsolatedPoolForTesting() { … }
#endif
#if PA_CONFIG(ENABLE_SHADOW_METADATA)
namespace {
int CreateAnonymousFileForMapping([[maybe_unused]] const char* name,
[[maybe_unused]] size_t size) {
int fd = -1;
#if PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_CHROMEOS)
fd = memfd_create(name, MFD_CLOEXEC);
PA_CHECK(0 == ftruncate(fd, size));
#else
PA_NOTREACHED();
#endif
return fd;
}
}
void PartitionAddressSpace::InitShadowMetadata(PoolHandleMask mask) {
if (pool_shadow_address_ == kUninitializedPoolBaseAddress) {
const size_t shadow_pool_size =
std::max(ConfigurablePoolShadowSize(),
std::max(RegularPoolShadowSize(), BRPPoolShadowSize()));
uintptr_t pool_shadow_address =
AllocPages(shadow_pool_size, PageAllocationGranularity(),
PageAccessibilityConfiguration(
PageAccessibilityConfiguration::kInaccessible),
PageTag::kPartitionAlloc);
if (!pool_shadow_address) {
HandlePoolAllocFailure();
}
pool_shadow_address_ = pool_shadow_address;
}
if (ContainsFlags(mask, PoolHandleMask::kConfigurable)) {
if (configurable_pool_fd_ == -1) {
PA_DCHECK(pool_shadow_address_);
PA_DCHECK(configurable_pool_shadow_offset_ == 0);
configurable_pool_fd_ = CreateAnonymousFileForMapping(
"configurable_pool_shadow", ConfigurablePoolShadowSize());
configurable_pool_shadow_offset_ =
pool_shadow_address_ - ConfigurablePoolBase() +
SystemPageSize() * kSystemPageOffsetOfConfigurablePoolShadow;
}
}
if (ContainsFlags(mask, PoolHandleMask::kBRP)) {
if (brp_pool_fd_ == -1) {
PA_DCHECK(pool_shadow_address_);
PA_DCHECK(brp_pool_shadow_offset_ == 0);
brp_pool_fd_ =
CreateAnonymousFileForMapping("brp_pool_shadow", BRPPoolShadowSize());
brp_pool_shadow_offset_ =
pool_shadow_address_ - BRPPoolBase() +
SystemPageSize() * kSystemPageOffsetOfBRPPoolShadow;
}
}
if (ContainsFlags(mask, PoolHandleMask::kRegular)) {
if (regular_pool_fd_ == -1) {
PA_DCHECK(pool_shadow_address_);
PA_DCHECK(regular_pool_shadow_offset_ == 0);
regular_pool_fd_ = CreateAnonymousFileForMapping("regular_pool_shadow",
RegularPoolShadowSize());
regular_pool_shadow_offset_ =
pool_shadow_address_ - RegularPoolBase() +
SystemPageSize() * kSystemPageOffsetOfRegularPoolShadow;
}
}
}
void PartitionAddressSpace::MapMetadata(uintptr_t super_page,
bool copy_metadata) {
PA_DCHECK(pool_shadow_address_);
PA_DCHECK(0u == (super_page & kSuperPageOffsetMask));
std::ptrdiff_t offset;
int pool_fd = -1;
uintptr_t base_address;
if (IsInRegularPool(super_page)) {
pool_fd = regular_pool_fd_;
offset = regular_pool_shadow_offset_;
base_address = RegularPoolBase();
} else if (IsInBRPPool(super_page)) {
offset = brp_pool_shadow_offset_;
pool_fd = brp_pool_fd_;
base_address = BRPPoolBase();
} else if (IsInConfigurablePool(super_page)) {
offset = configurable_pool_shadow_offset_;
pool_fd = configurable_pool_fd_;
base_address = ConfigurablePoolBase();
} else {
PA_NOTREACHED();
}
uintptr_t metadata = super_page + SystemPageSize();
size_t file_offset = (super_page - base_address) >> kSuperPageShift
<< SystemPageShift();
#if PA_BUILDFLAG(IS_POSIX)
uintptr_t writable_metadata = metadata + offset;
void* ptr = mmap(reinterpret_cast<void*>(writable_metadata), SystemPageSize(),
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, pool_fd,
file_offset);
PA_CHECK(ptr != MAP_FAILED);
PA_CHECK(ptr == reinterpret_cast<void*>(writable_metadata));
if (copy_metadata) [[unlikely]] {
memcpy(reinterpret_cast<void*>(writable_metadata),
reinterpret_cast<void*>(metadata), SystemPageSize());
}
ptr = mmap(reinterpret_cast<void*>(metadata), SystemPageSize(), PROT_READ,
MAP_FIXED | MAP_SHARED, pool_fd, file_offset);
PA_CHECK(ptr != MAP_FAILED);
PA_CHECK(ptr == reinterpret_cast<void*>(metadata));
#else
PA_NOTREACHED();
#endif
}
void PartitionAddressSpace::UnmapShadowMetadata(uintptr_t super_page,
pool_handle pool) {
PA_DCHECK(0u == (super_page & kSuperPageOffsetMask));
std::ptrdiff_t offset;
switch (pool) {
case kRegularPoolHandle:
PA_DCHECK(RegularPoolBase() <= super_page);
PA_DCHECK((super_page - RegularPoolBase()) < RegularPoolSize());
PA_DCHECK(IsShadowMetadataEnabled(kRegularPoolHandle));
offset = regular_pool_shadow_offset_;
break;
case kBRPPoolHandle:
PA_DCHECK(BRPPoolBase() <= super_page);
PA_DCHECK((super_page - BRPPoolBase()) < BRPPoolSize());
PA_DCHECK(IsShadowMetadataEnabled(kBRPPoolHandle));
offset = brp_pool_shadow_offset_;
break;
case kConfigurablePoolHandle:
PA_DCHECK(IsShadowMetadataEnabled(kConfigurablePoolHandle));
offset = configurable_pool_shadow_offset_;
break;
default:
return;
}
uintptr_t writable_metadata = super_page + SystemPageSize() + offset;
void* ptr = reinterpret_cast<void*>(writable_metadata);
memset(ptr, 0, SystemPageSize());
#if PA_BUILDFLAG(IS_POSIX)
void* ret = mmap(ptr, SystemPageSize(), PROT_NONE,
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
PA_CHECK(ret != MAP_FAILED);
PA_CHECK(ret == ptr);
#else
PA_NOTREACHED();
#endif
}
#endif
#if defined(PARTITION_ALLOCATOR_CONSTANTS_POSIX_NONCONST_PAGE_SIZE)
PageCharacteristics page_characteristics;
#endif
#endif
}