blob: 7446b8f3a5b0ddeb709bdfe49b7917d21c62582f [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines wrapper types for memory addresses that represent the start
// of a memory slot or slot span. These types (`SlotSpanStart`, `SlotStart`,
// `UntaggedSlotStart`) provide a more type-safe way to handle memory addresses
// within PartitionAlloc, especially considering Memory Tagging Extension (MTE).
#ifndef PARTITION_ALLOC_SLOT_START_H_
#define PARTITION_ALLOC_SLOT_START_H_
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_dcheck_helper.h"
#include "partition_alloc/tagging.h"
namespace partition_alloc {
class PartitionRoot;
namespace internal {
class UntaggedSlotStart;
// Represents an address of a slot span start.
// A slot span start is always also a slot start. This type is MTE-untagged.
// It can be safely converted to `UntaggedSlotStart` or `SlotStart`.
class SlotSpanStart {
public:
PA_ALWAYS_INLINE constexpr SlotSpanStart() = default;
PA_ALWAYS_INLINE explicit constexpr SlotSpanStart(uintptr_t address)
: address_(address) {}
PA_ALWAYS_INLINE explicit constexpr operator bool() const {
return address_ != 0;
}
PA_ALWAYS_INLINE constexpr uintptr_t value() const { return address_; }
PA_ALWAYS_INLINE constexpr ptrdiff_t offset(uintptr_t other) const {
return static_cast<ptrdiff_t>(other - address_);
}
PA_ALWAYS_INLINE constexpr ptrdiff_t offset(UntaggedSlotStart other) const;
PA_ALWAYS_INLINE constexpr UntaggedSlotStart AsSlotStart() const;
PA_ALWAYS_INLINE constexpr UntaggedSlotStart GetNthSlotStart(
size_t n,
size_t slot_size) const;
PA_ALWAYS_INLINE constexpr bool operator==(SlotSpanStart other) const {
return value() == other.value();
}
PA_ALWAYS_INLINE constexpr bool operator!=(SlotSpanStart other) const {
return !(*this == other);
}
PA_ALWAYS_INLINE constexpr bool operator==(uintptr_t other) const {
return value() == other;
}
PA_ALWAYS_INLINE constexpr bool operator!=(uintptr_t other) const {
return value() != other;
}
private:
uintptr_t address_ = 0;
};
static_assert(sizeof(SlotSpanStart) == sizeof(void*));
static_assert(std::is_trivially_copyable_v<SlotSpanStart>);
static_assert(std::is_trivially_destructible_v<SlotSpanStart>);
// Represents an address of a slot start, MTE-tagged.
// This type should be used when dealing with pointers that may carry an MTE
// tag. It provides methods for checked and unchecked construction, as well as
// untagging.
class SlotStart {
public:
PA_ALWAYS_INLINE constexpr SlotStart() = default;
// `SlotStart::Checked()` may perform runtime check to confirm that it is
// indeed a slot start. Prefer this variant for untrusted input or payload
// which can be corrupted by Use-after-Free etc. `SlotStart::Unchecked()` has
// no check at all and faster.
static SlotStart Checked(uintptr_t tagged_address,
const PartitionRoot* root) {
SlotStart ret(tagged_address);
ret.Check(root);
return ret;
}
PA_ALWAYS_INLINE static SlotStart Checked(void* ptr,
const PartitionRoot* root) {
return Checked(reinterpret_cast<uintptr_t>(ptr), root);
}
PA_ALWAYS_INLINE static constexpr SlotStart Unchecked(
uintptr_t tagged_address) {
return SlotStart(tagged_address);
}
PA_ALWAYS_INLINE static constexpr SlotStart Unchecked(const void* ptr) {
return SlotStart(reinterpret_cast<uintptr_t>(ptr));
}
PA_ALWAYS_INLINE explicit constexpr operator bool() const {
return address_ != 0;
}
PA_ALWAYS_INLINE constexpr uintptr_t value() const { return address_; }
// Check if this indeed points to the beginning of a slot.
PA_EXPORT_IF_DCHECK_IS_ON()
void Check(const PartitionRoot* root) const PA_EMPTY_BODY_IF_DCHECK_IS_OFF();
PA_ALWAYS_INLINE constexpr UntaggedSlotStart Untag() const;
template <typename T = unsigned char>
PA_ALWAYS_INLINE T* ToObject() const {
return reinterpret_cast<T*>(address_);
}
PA_ALWAYS_INLINE constexpr bool operator==(SlotStart other) const {
return value() == other.value();
}
PA_ALWAYS_INLINE constexpr bool operator!=(SlotStart other) const {
return !(*this == other);
}
PA_ALWAYS_INLINE constexpr bool operator==(uintptr_t other) const {
return value() == other;
}
PA_ALWAYS_INLINE constexpr bool operator!=(uintptr_t other) const {
return value() != other;
}
private:
PA_ALWAYS_INLINE explicit constexpr SlotStart(uintptr_t address)
: address_(address) {}
uintptr_t address_ = 0;
};
static_assert(sizeof(SlotStart) == sizeof(void*));
static_assert(std::is_trivially_copyable_v<SlotStart>);
static_assert(std::is_trivially_destructible_v<SlotStart>);
// Represents an address of a slot start, MTE-untagged.
// This type should be used when the MTE tag bits are not relevant or have been
// explicitly removed. It provides methods for checked and unchecked
// construction, as well as tagging.
class UntaggedSlotStart {
public:
PA_ALWAYS_INLINE constexpr UntaggedSlotStart() = default;
// `UntaggedSlotStart::Checked()` may perform runtime check to confirm that
// it is indeed a slot start. Prefer this variant for untrusted input or
// payload which can be corrupted by Use-after-Free etc.
// `UntaggedSlotStart::Unchecked()` has no check at all and faster.
static UntaggedSlotStart Checked(uintptr_t address,
const PartitionRoot* root) {
UntaggedSlotStart ret(address);
ret.Check(root);
return ret;
}
PA_ALWAYS_INLINE static constexpr UntaggedSlotStart Unchecked(
uintptr_t address) {
return UntaggedSlotStart(address);
}
PA_ALWAYS_INLINE explicit constexpr operator bool() const {
return address_ != 0;
}
PA_ALWAYS_INLINE constexpr uintptr_t value() const { return address_; }
PA_ALWAYS_INLINE constexpr ptrdiff_t offset(uintptr_t other) const {
return static_cast<ptrdiff_t>(other - address_);
}
// Check if this indeed points to the beginning of a slot.
PA_EXPORT_IF_DCHECK_IS_ON()
void Check(const PartitionRoot* root) const PA_EMPTY_BODY_IF_DCHECK_IS_OFF();
PA_ALWAYS_INLINE SlotStart Tag() const;
PA_ALWAYS_INLINE constexpr bool operator==(UntaggedSlotStart other) const {
return value() == other.value();
}
PA_ALWAYS_INLINE constexpr bool operator!=(UntaggedSlotStart other) const {
return !(*this == other);
}
PA_ALWAYS_INLINE constexpr bool operator==(uintptr_t other) const {
return value() == other;
}
PA_ALWAYS_INLINE constexpr bool operator!=(uintptr_t other) const {
return value() != other;
}
private:
PA_ALWAYS_INLINE explicit constexpr UntaggedSlotStart(uintptr_t address)
: address_(address) {}
uintptr_t address_ = 0;
};
static_assert(sizeof(UntaggedSlotStart) == sizeof(void*));
static_assert(std::is_trivially_copyable_v<UntaggedSlotStart>);
static_assert(std::is_trivially_destructible_v<UntaggedSlotStart>);
constexpr ptrdiff_t SlotSpanStart::offset(UntaggedSlotStart other) const {
return offset(other.value());
}
constexpr UntaggedSlotStart SlotSpanStart::AsSlotStart() const {
return UntaggedSlotStart::Unchecked(address_);
}
constexpr UntaggedSlotStart SlotSpanStart::GetNthSlotStart(
size_t n,
size_t slot_size) const {
return UntaggedSlotStart::Unchecked(address_ + n * slot_size);
}
SlotStart UntaggedSlotStart::Tag() const {
return SlotStart::Unchecked(reinterpret_cast<uintptr_t>(TagAddr(address_)));
}
constexpr UntaggedSlotStart SlotStart::Untag() const {
return UntaggedSlotStart::Unchecked(UntagAddr(address_));
}
} // namespace internal
} // namespace partition_alloc
#endif // PARTITION_ALLOC_SLOT_START_H_