Skip to content

Commit

Permalink
Merge pull request #280 from immunant/sjc/pkey_allocator
Browse files Browse the repository at this point in the history
partition-alloc: Add compartment support to upstream
  • Loading branch information
rinon authored Sep 26, 2023
2 parents 272272c + 498359e commit 8f8ab23
Show file tree
Hide file tree
Showing 30 changed files with 1,057 additions and 385 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ void DecommitPages(uintptr_t address, size_t size) {
void AddressPoolManager::Add(pool_handle handle, uintptr_t ptr, size_t length) {
PA_DCHECK(!(ptr & kSuperPageOffsetMask));
PA_DCHECK(!((ptr + length) & kSuperPageOffsetMask));
PA_CHECK(handle > 0 && handle <= std::size(pools_));
// Checked in GetPool
// PA_CHECK(handle > 0 && handle <= std::size(pools_));

Pool* pool = GetPool(handle);
PA_CHECK(!pool->IsInitialized());
Expand Down Expand Up @@ -308,7 +309,9 @@ bool AddressPoolManager::GetStats(AddressSpaceStats* stats) {
GetPoolStats(kConfigurablePoolHandle, &stats->configurable_pool_stats);
}
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
GetPoolStats(kThreadIsolatedPoolHandle, &stats->thread_isolated_pool_stats);
for (size_t i = 0; i < kNumCompartments; ++i) {
GetPoolStats(PoolHandleForCompartment(i), &stats->compartment_pool_stats[i]);
}
#endif
return true;
}
Expand Down Expand Up @@ -555,15 +558,12 @@ void AddressPoolManager::DumpStats(AddressSpaceStatsDumper* dumper) {
}
}

// TODO(SJC): Can we assert layout for the compartments here?
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
// This function just exists to static_assert the layout of the private fields
// in Pool.
void AddressPoolManager::AssertThreadIsolatedLayout() {
constexpr size_t last_pool_offset =
offsetof(AddressPoolManager, pools_) + sizeof(Pool) * (kNumPools - 1);
constexpr size_t alloc_bitset_offset =
last_pool_offset + offsetof(Pool, alloc_bitset_);
static_assert(alloc_bitset_offset % PA_THREAD_ISOLATED_ALIGN_SZ == 0);
static_assert(offsetof(AddressPoolManager, pools_) % PA_THREAD_ISOLATED_ALIGN_SZ == 0);
static_assert(sizeof(AddressPoolManager) % PA_THREAD_ISOLATED_ALIGN_SZ == 0);
}
#endif // BUILDFLAG(ENABLE_THREAD_ISOLATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,25 +172,33 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC)
#endif // BUILDFLAG(ENABLE_THREAD_ISOLATION)
};

struct PA_THREAD_ISOLATED_ALIGN AlignedPool {
Pool pool;
};

PA_ALWAYS_INLINE Pool* GetPool(pool_handle handle) {
PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools);
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
if (handle >= kCompartmentPool0Handle) {
return &protected_pools_[handle - kCompartmentPool0Handle].pool;
}
#endif
return &pools_[handle - 1];
}

// Gets the stats for the pool identified by `handle`, if
// initialized.
void GetPoolStats(pool_handle handle, PoolStats* stats);

// If thread isolation support is enabled, we need to write-protect the
// isolated pool (which needs to be last). For this, we need to add padding in
// front of the pools so that the isolated one starts on a page boundary.
// We also skip the Lock at the beginning of the pool since it needs to be
// used in contexts where we didn't enable write access to the pool memory.
char pad_[PA_THREAD_ISOLATED_ARRAY_PAD_SZ_WITH_OFFSET(
Pool,
kNumPools,
offsetof(Pool, alloc_bitset_))] = {};
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
// This should be the first member and the AddressPoolManager must be
// PA_THREAD_ISOLATED_ALIGN. Each AlignedPool is sized such that we can
// protect each independently.
AlignedPool protected_pools_[kNumCompartments];
Pool pools_[kNumPools - kNumCompartments];
#else
Pool pools_[kNumPools];
#endif

#endif // BUILDFLAG(HAS_64_BIT_POINTERS)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
#include "base/allocator/partition_allocator/partition_alloc_constants.h"

namespace partition_alloc {

Expand Down Expand Up @@ -38,7 +39,8 @@ struct AddressSpaceStats {
#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
#endif // BUILDFLAG(HAS_64_BIT_POINTERS)
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
PoolStats thread_isolated_pool_stats;
PoolStats compartment_pool_stats[internal::kNumCompartments];
// PoolStats thread_isolated_pool_stats;
#endif
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ void PartitionAddressSpace::InitConfigurablePool(uintptr_t pool_base,
// It's possible that the thread isolated pool has been initialized first, in
// which case the setup_ memory has been made read-only. Remove the protection
// temporarily.
if (IsThreadIsolatedPoolInitialized()) {
if (IsAnyThreadIsolatedPoolInitialized()) {
UnprotectThreadIsolatedGlobals();
}
#endif
Expand All @@ -312,7 +312,7 @@ void PartitionAddressSpace::InitConfigurablePool(uintptr_t pool_base,

#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
// Put the metadata protection back in place.
if (IsThreadIsolatedPoolInitialized()) {
if (IsAnyThreadIsolatedPoolInitialized()) {
WriteProtectThreadIsolatedGlobals(setup_.thread_isolation_);
}
#endif
Expand All @@ -321,35 +321,42 @@ void PartitionAddressSpace::InitConfigurablePool(uintptr_t pool_base,
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
void PartitionAddressSpace::InitThreadIsolatedPool(
ThreadIsolationOption thread_isolation) {
size_t compartment = thread_isolation.compartment;
PA_CHECK(compartment < kNumCompartments);

// The ThreadIsolated pool can't be initialized with conflicting settings.
if (IsThreadIsolatedPoolInitialized()) {
PA_CHECK(setup_.thread_isolation_ == thread_isolation);
if (IsThreadIsolatedPoolInitialized(compartment)) {
PA_CHECK(setup_.thread_isolation_[compartment] == thread_isolation);
return;
}

size_t pool_size = ThreadIsolatedPoolSize();
setup_.thread_isolated_pool_base_address_ =
setup_.thread_isolated_pool_base_address_[compartment] =
AllocPages(pool_size, pool_size,
PageAccessibilityConfiguration(
PageAccessibilityConfiguration::kInaccessible),
PageTag::kPartitionAlloc);
if (!setup_.thread_isolated_pool_base_address_) {
if (!setup_.thread_isolated_pool_base_address_[compartment]) {
HandlePoolAllocFailure();
}

PA_DCHECK(!(setup_.thread_isolated_pool_base_address_ & (pool_size - 1)));
setup_.thread_isolation_ = thread_isolation;
PA_DCHECK(!(setup_.thread_isolated_pool_base_address_[compartment] & (pool_size - 1)));
setup_.thread_isolation_[compartment] = thread_isolation;
AddressPoolManager::GetInstance().Add(
kThreadIsolatedPoolHandle, setup_.thread_isolated_pool_base_address_,
PoolHandleForCompartment(compartment),
setup_.thread_isolated_pool_base_address_[compartment],
pool_size);

PA_DCHECK(
!IsInThreadIsolatedPool(setup_.thread_isolated_pool_base_address_ - 1));
PA_DCHECK(IsInThreadIsolatedPool(setup_.thread_isolated_pool_base_address_));
PA_DCHECK(IsInThreadIsolatedPool(setup_.thread_isolated_pool_base_address_ +
pool_size - 1));
PA_DCHECK(!IsInThreadIsolatedPool(setup_.thread_isolated_pool_base_address_ +
pool_size));
PA_DCHECK(!IsInThreadIsolatedPool(
setup_.thread_isolated_pool_base_address_[compartment] - 1, compartment));
PA_DCHECK(IsInThreadIsolatedPool(
setup_.thread_isolated_pool_base_address_[compartment], compartment));
PA_DCHECK(IsInThreadIsolatedPool(
setup_.thread_isolated_pool_base_address_[compartment] + pool_size - 1,
compartment));
PA_DCHECK(!IsInThreadIsolatedPool(
setup_.thread_isolated_pool_base_address_[compartment] + pool_size,
compartment));

// TODO(1362969): support PA_ENABLE_SHADOW_METADATA
}
Expand Down Expand Up @@ -388,7 +395,7 @@ void PartitionAddressSpace::UninitConfigurablePoolForTesting() {
// It's possible that the thread isolated pool has been initialized first, in
// which case the setup_ memory has been made read-only. Remove the protection
// temporarily.
if (IsThreadIsolatedPoolInitialized()) {
if (IsAnyThreadIsolatedPoolInitialized()) {
UnprotectThreadIsolatedGlobals();
}
#endif
Expand All @@ -397,25 +404,31 @@ void PartitionAddressSpace::UninitConfigurablePoolForTesting() {
setup_.configurable_pool_base_mask_ = 0;
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
// Put the metadata protection back in place.
if (IsThreadIsolatedPoolInitialized()) {
if (IsAnyThreadIsolatedPoolInitialized()) {
WriteProtectThreadIsolatedGlobals(setup_.thread_isolation_);
}
#endif
}

#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
void PartitionAddressSpace::UninitThreadIsolatedPoolForTesting() {
if (IsThreadIsolatedPoolInitialized()) {
if (IsAnyThreadIsolatedPoolInitialized()) {
UnprotectThreadIsolatedGlobals();
#if BUILDFLAG(PA_DCHECK_IS_ON)
ThreadIsolationSettings::settings.enabled = false;
#endif

FreePages(setup_.thread_isolated_pool_base_address_,
ThreadIsolatedPoolSize());
AddressPoolManager::GetInstance().Remove(kThreadIsolatedPoolHandle);
setup_.thread_isolated_pool_base_address_ = kUninitializedPoolBaseAddress;
setup_.thread_isolation_.enabled = false;
for (auto thread_isolation : setup_.thread_isolation_) {
if (!IsThreadIsolatedPoolInitialized(thread_isolation.compartment)) {
continue;
}
Compartment c = thread_isolation.compartment;
FreePages(setup_.thread_isolated_pool_base_address_[c],
ThreadIsolatedPoolSize());
AddressPoolManager::GetInstance().Remove(PoolHandleForCompartment(c));
setup_.thread_isolated_pool_base_address_[c] =
kUninitializedPoolBaseAddress;
thread_isolation.enabled = false;
}
}
}
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ADDRESS_SPACE_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ADDRESS_SPACE_H_

#include <array>
#include <cstddef>
#include <optional>
#include <utility>

#include "base/allocator/partition_allocator/address_pool_manager_types.h"
Expand Down Expand Up @@ -75,9 +77,9 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace {
pool = kConfigurablePoolHandle;
base = setup_.configurable_pool_base_address_;
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
} else if (IsInThreadIsolatedPool(address)) {
pool = kThreadIsolatedPoolHandle;
base = setup_.thread_isolated_pool_base_address_;
} else if (auto compartment = GetCompartmentForAddress(address)) {
pool = internal::PoolHandleForCompartment(*compartment);
base = setup_.thread_isolated_pool_base_address_[*compartment];
#endif
} else {
PA_NOTREACHED();
Expand Down Expand Up @@ -126,8 +128,18 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace {
}

#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
PA_ALWAYS_INLINE static bool IsThreadIsolatedPoolInitialized() {
return setup_.thread_isolated_pool_base_address_ !=
PA_ALWAYS_INLINE static bool IsAnyThreadIsolatedPoolInitialized() {
for (Compartment c = 0; c < kNumCompartments; ++c) {
if (IsThreadIsolatedPoolInitialized(c)) {
return true;
}
}
return false;
}

PA_ALWAYS_INLINE static bool
IsThreadIsolatedPoolInitialized(Compartment compartment) {
return setup_.thread_isolated_pool_base_address_[compartment] !=
kUninitializedPoolBaseAddress;
}
#endif
Expand Down Expand Up @@ -203,8 +215,30 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace {
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
// Returns false for nullptr.
PA_ALWAYS_INLINE static bool IsInThreadIsolatedPool(uintptr_t address) {
for (size_t i = 0; i < kNumCompartments; ++i) {
if (IsInThreadIsolatedPool(address, i)) {
return true;
}
}
return false;
}

// Returns false for nullptr.
PA_ALWAYS_INLINE static bool IsInThreadIsolatedPool(uintptr_t address,
size_t compartment) {
PA_CHECK(compartment < kNumCompartments);
return (address & kThreadIsolatedPoolBaseMask) ==
setup_.thread_isolated_pool_base_address_;
setup_.thread_isolated_pool_base_address_[compartment];
}

PA_ALWAYS_INLINE static std::optional<Compartment>
GetCompartmentForAddress(uintptr_t address) {
for (size_t i = 0; i < kNumCompartments; ++i) {
if (IsInThreadIsolatedPool(address, i)) {
return {i};
}
}
return {};
}
#endif

Expand Down Expand Up @@ -332,8 +366,16 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace {
uintptr_t brp_pool_base_address_ = kUninitializedPoolBaseAddress;
uintptr_t configurable_pool_base_address_ = kUninitializedPoolBaseAddress;
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
uintptr_t thread_isolated_pool_base_address_ =
kUninitializedPoolBaseAddress;
uintptr_t thread_isolated_pool_base_address_[kNumCompartments] = {
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
kUninitializedPoolBaseAddress, kUninitializedPoolBaseAddress,
};
#endif
#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
uintptr_t regular_pool_base_mask_ = 0;
Expand All @@ -344,7 +386,7 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace {
#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
uintptr_t configurable_pool_base_mask_ = 0;
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
ThreadIsolationOption thread_isolation_;
std::array<ThreadIsolationOption, kNumCompartments> thread_isolation_;
#endif
};
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@
#define BUILDFLAG_INTERNAL_PCSCAN_STACK_SUPPORTED() (0)
#define BUILDFLAG_INTERNAL_ENABLE_PKEYS() (1)
#define BUILDFLAG_INTERNAL_ENABLE_THREAD_ISOLATION() (1)
#define BUILDFLAG_INTERNAL_WRAP_SHIM() (1)

#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BUILDFLAGS_H_
Loading

0 comments on commit 8f8ab23

Please sign in to comment.