#ifndef PARTITION_ALLOC_RESERVATION_OFFSET_TABLE_H_
#define PARTITION_ALLOC_RESERVATION_OFFSET_TABLE_H_
#include <cstddef>
#include <cstdint>
#include <limits>
#include "partition_alloc/address_pool_manager.h"
#include "partition_alloc/build_config.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/partition_address_space.h"
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/component_export.h"
#include "partition_alloc/partition_alloc_check.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/tagging.h"
#include "partition_alloc/thread_isolation/alignment.h"
namespace partition_alloc::internal {
class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ReservationOffsetTable {
static constexpr uint16_t kOffsetTagNotAllocated =
std::numeric_limits<uint16_t>::max();
static constexpr uint16_t kOffsetTagNormalBuckets =
std::numeric_limits<uint16_t>::max() - 1;
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
static constexpr size_t kRegularOffsetTableLength =
PartitionAddressSpace::CorePoolMaxSize() >> kSuperPageShift;
static_assert(kRegularOffsetTableLength < kOffsetTagNormalBuckets,
"Offsets should be smaller than kOffsetTagNormalBuckets.");
static constexpr size_t kBRPOffsetTableLength =
PartitionAddressSpace::CorePoolMaxSize() >> kSuperPageShift;
static_assert(kBRPOffsetTableLength < kOffsetTagNormalBuckets,
"Offsets should be smaller than kOffsetTagNormalBuckets.");
static constexpr size_t kConfigurableOffsetTableLength =
PartitionAddressSpace::ConfigurablePoolMaxSize() >> kSuperPageShift;
static_assert(kConfigurableOffsetTableLength < kOffsetTagNormalBuckets,
"Offsets should be smaller than kOffsetTagNormalBuckets.");
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
public:
static constexpr size_t kThreadIsolatedOffsetTableLength =
PartitionAddressSpace::ThreadIsolatedPoolSize() >> kSuperPageShift;
static_assert(kThreadIsolatedOffsetTableLength < kOffsetTagNormalBuckets,
"Offsets should be smaller than kOffsetTagNormalBuckets.");
private:
static constexpr size_t kThreadIsolatedOffsetTablePaddingSize =
base::bits::AlignUp(kThreadIsolatedOffsetTableLength * sizeof(uint16_t),
SystemPageSize()) -
kThreadIsolatedOffsetTableLength * sizeof(uint16_t);
#endif #else
static constexpr uint64_t kGiB = 1024 * 1024 * 1024ull;
static constexpr size_t kReservationOffsetTableLength =
4 * kGiB / kSuperPageSize;
static_assert(kReservationOffsetTableLength < kOffsetTagNormalBuckets,
"Offsets should be smaller than kOffsetTagNormalBuckets.");
#endif
template <size_t length, size_t padding_size = 0>
struct _ReservationOffsetTable {
static_assert(
length <= std::numeric_limits<uint16_t>::max(),
"Length of the reservation offset table must be less than MAX_UINT16");
uint16_t offsets[length] = {};
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
#endif
char pad_[padding_size] = {};
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
constexpr _ReservationOffsetTable() {
for (uint16_t& offset : offsets) {
offset = kOffsetTagNotAllocated;
}
}
};
template <size_t length, size_t padding_size>
PA_ALWAYS_INLINE explicit ReservationOffsetTable(
_ReservationOffsetTable<length, padding_size>& table
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
,
PoolOffsetLookup offset_lookup
#endif )
: table_begin_(table.offsets)
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
,
table_end_(PA_UNSAFE_TODO(table.offsets + length))
#endif #if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
,
offset_lookup_(offset_lookup)
#endif {
}
public:
PA_ALWAYS_INLINE ReservationOffsetTable() = default;
PA_ALWAYS_INLINE static ReservationOffsetTable Get(pool_handle handle) {
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
PoolOffsetLookup offset_lookup =
PartitionAddressSpace::GetOffsetLookup(handle);
switch (handle) {
case kRegularPoolHandle:
return ReservationOffsetTable(regular_pool_table_, offset_lookup);
case kBRPPoolHandle:
return ReservationOffsetTable(brp_pool_table_, offset_lookup);
case kConfigurablePoolHandle:
return ReservationOffsetTable(configurable_pool_table_, offset_lookup);
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
case kThreadIsolatedPoolHandle:
return ReservationOffsetTable(thread_isolated_pool_table_,
offset_lookup);
#endif default:
PA_NOTREACHED();
}
#else
return ReservationOffsetTable(reservation_offset_table_);
#endif }
PA_ALWAYS_INLINE static ReservationOffsetTable Get(uintptr_t address) {
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
return ReservationOffsetTable::Get(
PartitionAddressSpace::GetPoolInfo(address).handle);
#else
return ReservationOffsetTable(reservation_offset_table_);
#endif }
PA_ALWAYS_INLINE void* GetData() const { return table_begin_; }
PA_ALWAYS_INLINE void SetNotAllocatedTag(uintptr_t reservation_start,
size_t reservation_size = 1) const {
PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
PA_DCHECK(reservation_size > 0);
uint16_t* offset_ptr = GetOffsetPointer(reservation_start);
PA_DCHECK((reservation_size - 1) >> kSuperPageShift <=
std::numeric_limits<uint16_t>::max());
const uint16_t offset_end =
static_cast<uint16_t>((reservation_size - 1) >> kSuperPageShift);
for (uint16_t offset = 0; offset <= offset_end; ++offset) {
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
PA_DCHECK(offset_ptr < table_end_);
#endif *PA_UNSAFE_TODO(offset_ptr++) = kOffsetTagNotAllocated;
}
}
PA_ALWAYS_INLINE void SetNormalBucketsTag(uintptr_t reservation_start) const {
PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
*GetOffsetPointer(reservation_start) = kOffsetTagNormalBuckets;
}
PA_ALWAYS_INLINE void SetDirectMapReservationStart(
uintptr_t reservation_start,
size_t reservation_size) const {
PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
PA_DCHECK(reservation_size > 0);
uint16_t* offset_ptr = GetOffsetPointer(reservation_start);
PA_DCHECK((reservation_size - 1) >> kSuperPageShift <=
std::numeric_limits<uint16_t>::max());
const uint16_t offset_end =
static_cast<uint16_t>((reservation_size - 1) >> kSuperPageShift);
for (uint16_t offset = 0; offset <= offset_end; ++offset) {
PA_DCHECK(offset < kOffsetTagNormalBuckets);
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
PA_DCHECK(offset_ptr < table_end_);
#endif *PA_UNSAFE_TODO(offset_ptr++) = offset;
}
}
PA_ALWAYS_INLINE uintptr_t GetDirectMapReservationStart(uintptr_t address) {
uint16_t* offset_ptr = GetOffsetPointer(address);
PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
if (*offset_ptr == kOffsetTagNormalBuckets) {
return 0;
}
uintptr_t reservation_start =
(address & kSuperPageBaseMask) -
(static_cast<size_t>(*offset_ptr) << kSuperPageShift);
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
PA_DCHECK(offset_lookup_.Includes(reservation_start));
#endif PA_DCHECK(*GetOffsetPointer(reservation_start) == 0);
#endif
return reservation_start;
}
PA_ALWAYS_INLINE bool IsReservationStart(uintptr_t address) const {
uint16_t* offset_ptr = GetOffsetPointer(address);
PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
return ((*offset_ptr == kOffsetTagNormalBuckets) || (*offset_ptr == 0)) &&
(address % kSuperPageSize == 0);
}
PA_ALWAYS_INLINE bool IsManagedByNormalBuckets(uintptr_t address) const {
uint16_t* offset_ptr = GetOffsetPointer(address);
return *offset_ptr == kOffsetTagNormalBuckets;
}
PA_ALWAYS_INLINE bool IsManagedByDirectMap(uintptr_t address) const {
uint16_t* offset_ptr = GetOffsetPointer(address);
return *offset_ptr != kOffsetTagNormalBuckets &&
*offset_ptr != kOffsetTagNotAllocated;
}
PA_ALWAYS_INLINE bool IsManagedByNormalBucketsOrDirectMap(
uintptr_t address) const {
uint16_t* offset_ptr = GetOffsetPointer(address);
return *offset_ptr != kOffsetTagNotAllocated;
}
PA_ALWAYS_INLINE uint16_t* GetOffsetPointerForTesting(
uintptr_t address) const {
return GetOffsetPointer(address);
}
private:
PA_ALWAYS_INLINE uint16_t* GetOffsetPointer(uintptr_t address) const {
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
size_t table_index = offset_lookup_.GetOffset(address) >> kSuperPageShift;
#else
size_t table_index = address >> kSuperPageShift;
#endif uint16_t* offset_ptr = &PA_UNSAFE_TODO(table_begin_[table_index]);
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
PA_DCHECK(offset_ptr < table_end_);
#endif return offset_ptr;
}
uint16_t* table_begin_ = nullptr;
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
uint16_t* table_end_ = nullptr;
#endif
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
PoolOffsetLookup offset_lookup_;
#endif
#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
PA_CONSTINIT static _ReservationOffsetTable<kRegularOffsetTableLength>
regular_pool_table_;
PA_CONSTINIT static _ReservationOffsetTable<kBRPOffsetTableLength>
brp_pool_table_;
PA_CONSTINIT static _ReservationOffsetTable<kConfigurableOffsetTableLength>
configurable_pool_table_;
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
PA_THREAD_ISOLATED_ALIGN
PA_CONSTINIT static _ReservationOffsetTable<
kThreadIsolatedOffsetTableLength,
kThreadIsolatedOffsetTablePaddingSize>
thread_isolated_pool_table_;
#endif
#else
PA_CONSTINIT static _ReservationOffsetTable<kReservationOffsetTableLength>
reservation_offset_table_;
#endif };
}
#endif