Showing posts with label cpp_tag_pointer. Show all posts
Showing posts with label cpp_tag_pointer. Show all posts

Mar 25, 2026

[intel] 5-level paging (LA57) affects MSB pointer tagging

Resource:
https://en.wikipedia.org/wiki/Intel_5-level_paging

The adoption of 5-level paging (also known as LA57) allows the virtual address space to expand from the traditional 48 bits (256 TiB) to 57 bits (128 PiB).




In 5-level paging (LA57), the "free" or unused bits are indeed bits 57 through 63, which totals 7 bits. However, there is a catch: those bits aren't truly "free" for software to store random data (like tags or metadata) unless a specific hardware feature like Intel LAM (Linear Address Masking) or AMD UAI (Upper Address Ignore) is enabled.

The Breakdown of the 64-bit Address

Here is how the 64 bits are partitioned in a 5-level paging system:

  • Bits 0–11: Page Offset (4 KB boundaries).
  • Bits 12–56: The actual translation bits used by the five levels of page tables 9 x 5 = 45 bits
  • Bits 57–63: The 7 unused bits.


The "Canonical" Constraint

The CPU enforces a rule called Canonical Form. For an address to be valid:
Bits 57 through 63 must be an exact copy of bit 56.
If bit 56 is 0, then bits 57–63 must all be 0.
If bit 56 is 1, then bits 57–63 must all be 1.

If software tries to use an address where those 7 bits are "dirty" (containing random data), the CPU will trigger a General Protection Fault (#GP). This is why programmers can't simply use those 7 bits for pointers without the masking features mentioned above.

Jul 28, 2024

[C++] tag pointer

Reference:
Storing data in pointers

#include <cstddef>
#include <cstdint>
#include <iostream>


enum ListType {
  kNone,
  kReady,
  kDeleting,
};


static constexpr uintptr_t kListTypeMask = 0b11;
static constexpr uintptr_t kCleanPtrMask = ~kListTypeMask;

struct Tree;

struct Node{
  void set_list_type(ListType list_type) {
    uintptr_t t = static_cast<uintptr_t>(list_type);
    uintptr_t v = ptr_and_list_type_ & kCleanPtrMask;
    ptr_and_list_type_ = (v | t);
  }

  ListType list_type() const {
    return static_cast<ListType>(ptr_and_list_type_ & kListTypeMask);
  }

  void set_prev_next_ptr(Node** p) {
    uintptr_t t = ptr_and_list_type_ & kListTypeMask;
    uintptr_t v = reinterpret_cast<uintptr_t>(p);
    ptr_and_list_type_ = (v | t);
  }

  Node** prev_next_ptr() const {
    return reinterpret_cast<Node**>(ptr_and_list_type_ & kCleanPtrMask);
  }

  Tree* tree_ = nullptr;
  Node* parent_ = nullptr;
  Node* next_ = nullptr;
  uintptr_t ptr_and_list_type_ = 0;
};

struct Tree{
  void MarkReady(Node* p) {
    p->next_ = ready_;
    p->set_prev_next_ptr(&ready_);

    if (ready_ != nullptr) {
      ready_->set_prev_next_ptr(&p->next_);
    }

    ready_ = p;
    p->set_list_type(kReady);
  }
 
  int padding;
  Node* ready_ = nullptr;
};


int main() {
  // Print 8 since padding is type of int occupies 8 bytes.
  std::cout << "offset of ready_: " <<
    reinterpret_cast<void*>(&((Tree*)0)->ready_) << "\n";

  Tree* tree = new Tree{};
  std::cout << "tree: " << reinterpret_cast<void *>(&tree) << "\n";
  std::cout << "tree offset of ready: " <<
    reinterpret_cast<void *>(&tree->ready_) << "\n";

  Node p;
  p.tree_ = tree;
  tree->MarkReady(&p);
  std::cout << reinterpret_cast<void *>(p.prev_next_ptr()) << "\n";
}

MSB tagging vs. LSB tagging