[libc-commits] [libc] [libc] add option for hardened freelist (PR #205382)

via libc-commits libc-commits at lists.llvm.org
Wed Jul 1 12:01:28 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Schrodinger ZHU Yifan (SchrodingerZhu)

<details>
<summary>Changes</summary>

Introduce LIBC_COPT_HARDEN_FREELIST option together with a `santize_heap` interface.

The hardening/sanitization feature encode the prev pointers as `prev ^ K1`; and backward pointers as `ror(prev ^ K2, 17) ^ back ^ k3`.

When hardening option is provided:

- from a security perspective, it makes it harder for attackers to modify the free lists freely.
- for passive sanitization:
  - it detects BufferOverFlow as if the BOF happens to modify the freeblock after the allocated region, it alters the encoding and will be detected during some freelist walk
  - it detects DF, because DF will modify the backward pointers encoding; when following the original freelist, the encoding error will be detected
  - it detects UAF, for similar reasons

The exposed `santize_heap` interface can be called during tearing down or periodically to check the integrity of the heap.

---

Patch is 29.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/205382.diff


11 Files Affected:

- (modified) libc/src/__support/CMakeLists.txt (+1) 
- (modified) libc/src/__support/freelist.cpp (+46-15) 
- (modified) libc/src/__support/freelist.h (+98-6) 
- (modified) libc/src/__support/freelist_heap.h (+36-13) 
- (modified) libc/src/__support/freestore.h (+25-12) 
- (modified) libc/src/__support/freetrie.cpp (+14-2) 
- (modified) libc/src/__support/freetrie.h (+8-4) 
- (modified) libc/src/__support/macros/attributes.h (+12) 
- (modified) libc/test/src/__support/freelist_heap_test.cpp (+28) 
- (modified) libc/test/src/__support/freelist_test.cpp (+75-8) 
- (modified) libc/test/src/__support/freestore_test.cpp (+26-21) 


``````````diff
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index f4cb283976bc3..19d0d95873812 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -35,6 +35,7 @@ add_object_library(
     .block
     libc.src.__support.fixedvector
     libc.src.__support.CPP.array
+    libc.src.__support.CPP.bit
     libc.src.__support.CPP.cstddef
     libc.src.__support.CPP.new
     libc.src.__support.CPP.span
diff --git a/libc/src/__support/freelist.cpp b/libc/src/__support/freelist.cpp
index 28f73b2726d7d..430f2cd818e48 100644
--- a/libc/src/__support/freelist.cpp
+++ b/libc/src/__support/freelist.cpp
@@ -15,33 +15,64 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-void FreeList::push(Node *node) {
+void FreeList::push(Node *node, const FreeListSecrets &secrets) {
   if (begin_) {
-    LIBC_ASSERT(BlockRef::from_usable_space(node).outer_size() ==
-                    begin_->block().outer_size() &&
-                "freelist entries must have the same size");
-    // Since the list is circular, insert the node immediately before begin_.
-    node->prev = begin_->prev;
-    node->next = begin_;
-    begin_->prev->next = node;
-    begin_->prev = node;
+    Node *begin_prev = secrets.decrypt_prev(begin_, begin_->prev);
+
+    LIBC_HARDENING_ASSERT(secrets.decrypt_next(begin_prev->next) == begin_ &&
+                          "Corrupted free list links (push check)");
+
+    node->prev = secrets.encrypt_prev(node, begin_prev);
+    node->next = secrets.encrypt_next(begin_);
+    begin_prev->next = secrets.encrypt_next(node);
+    begin_->prev = secrets.encrypt_prev(begin_, node);
   } else {
-    begin_ = node->prev = node->next = node;
+    begin_ = node;
+    node->next = secrets.encrypt_next(node);
+    node->prev = secrets.encrypt_prev(node, node);
   }
 }
 
-void FreeList::remove(Node *node) {
+void FreeList::remove(Node *node, const FreeListSecrets &secrets) {
   LIBC_ASSERT(begin_ && "cannot remove from empty list");
-  if (node == node->next) {
+  Node *node_next = secrets.decrypt_next(node->next);
+  if (node == node_next) {
     LIBC_ASSERT(node == begin_ &&
                 "a self-referential node must be the only element");
     begin_ = nullptr;
   } else {
-    node->prev->next = node->next;
-    node->next->prev = node->prev;
+    Node *node_prev = secrets.decrypt_prev(node, node->prev);
+
+    LIBC_HARDENING_ASSERT(
+        secrets.decrypt_next(node_prev->next) == node &&
+        "Corrupted free list links (remove check prev->next)");
+    LIBC_HARDENING_ASSERT(
+        secrets.decrypt_prev(node_next, node_next->prev) == node &&
+        "Corrupted free list links (remove check next->prev)");
+
+    node_prev->next = secrets.encrypt_next(node_next);
+    node_next->prev = secrets.encrypt_prev(node_next, node_prev);
     if (begin_ == node)
-      begin_ = node->next;
+      begin_ = node_next;
   }
 }
 
+void FreeList::sanitize(const FreeListSecrets &secrets) const {
+  if (!begin_)
+    return;
+  Node *curr = begin_;
+  do {
+    Node *next_node = secrets.decrypt_next(curr->next);
+    Node *prev_node = secrets.decrypt_prev(curr, curr->prev);
+    (void)prev_node;
+    LIBC_HARDENING_ASSERT(
+        secrets.decrypt_next(prev_node->next) == curr &&
+        "Corrupted free list links (sanitize check prev->next)");
+    LIBC_HARDENING_ASSERT(
+        secrets.decrypt_prev(next_node, next_node->prev) == curr &&
+        "Corrupted free list links (sanitize check next->prev)");
+    curr = next_node;
+  } while (curr != begin_);
+}
+
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/freelist.h b/libc/src/__support/freelist.h
index 48e70c7c29df6..70e91305c4554 100644
--- a/libc/src/__support/freelist.h
+++ b/libc/src/__support/freelist.h
@@ -15,9 +15,86 @@
 #define LLVM_LIBC_SRC___SUPPORT_FREELIST_H
 
 #include "block.h"
+#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/libc_assert.h"
+#include "src/__support/macros/config.h"
+
+#ifndef LIBC_COPT_HARDEN_FREELIST
+#define LIBC_COPT_HARDEN_FREELIST false
+#endif
+
+#if LIBC_COPT_HARDEN_FREELIST
+#define LIBC_HARDENING_ASSERT(cond)                                            \
+  do {                                                                         \
+    if (LIBC_UNLIKELY(!(cond))) {                                              \
+      __builtin_trap();                                                        \
+    }                                                                          \
+  } while (0)
+#else
+#define LIBC_HARDENING_ASSERT(cond) LIBC_ASSERT(cond)
+#endif
 
 namespace LIBC_NAMESPACE_DECL {
 
+struct FreeListSecrets {
+  static constexpr int NODE_PTR_ROTATE_DISTANCE = 17;
+
+#if LIBC_COPT_HARDEN_FREELIST
+  uintptr_t k0;
+  uintptr_t k1;
+  uintptr_t k2;
+
+  LIBC_INLINE constexpr FreeListSecrets(uintptr_t k0, uintptr_t k1,
+                                        uintptr_t k2)
+      : k0(k0), k1(k1), k2(k2) {}
+  LIBC_INLINE constexpr FreeListSecrets() : k0(0), k1(0), k2(0) {}
+#else
+  LIBC_INLINE constexpr FreeListSecrets() = default;
+#endif
+
+  template <typename T> LIBC_INLINE T *decrypt_next(T *next_val) const {
+#if LIBC_COPT_HARDEN_FREELIST
+    return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(next_val) ^ k0);
+#else
+    return next_val;
+#endif
+  }
+
+  template <typename T>
+  LIBC_INLINE T *decrypt_prev([[maybe_unused]] const void *node,
+                              T *prev_val) const {
+#if LIBC_COPT_HARDEN_FREELIST
+    uintptr_t val = reinterpret_cast<uintptr_t>(prev_val) ^ k2 ^
+                    reinterpret_cast<uintptr_t>(node);
+    val = cpp::rotl(val, NODE_PTR_ROTATE_DISTANCE);
+    return reinterpret_cast<T *>(val ^ k1);
+#else
+    return prev_val;
+#endif
+  }
+
+  template <typename T> LIBC_INLINE T *encrypt_next(T *next_val) const {
+#if LIBC_COPT_HARDEN_FREELIST
+    return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(next_val) ^ k0);
+#else
+    return next_val;
+#endif
+  }
+
+  template <typename T>
+  LIBC_INLINE T *encrypt_prev([[maybe_unused]] const void *node,
+                              T *prev_val) const {
+#if LIBC_COPT_HARDEN_FREELIST
+    uintptr_t val = reinterpret_cast<uintptr_t>(prev_val) ^ k1;
+    val = cpp::rotr(val, NODE_PTR_ROTATE_DISTANCE);
+    return reinterpret_cast<T *>(val ^ reinterpret_cast<uintptr_t>(node) ^ k2);
+#else
+    return prev_val;
+#endif
+  }
+};
+
 /// A circularly-linked FIFO list storing free Blocks. All Blocks on a list
 /// are the same size. The blocks are referenced by Nodes in the list; the list
 /// refers to these, but it does not own them.
@@ -65,25 +142,40 @@ class FreeList {
   /// @returns The first block in the list.
   LIBC_INLINE BlockRef front() { return begin_->block(); }
 
+  LIBC_INLINE Node *next_node(const Node *node,
+                              const FreeListSecrets &secrets) const {
+    return node ? secrets.decrypt_next(node->next) : nullptr;
+  }
+
+  LIBC_INLINE Node *prev_node(const Node *node,
+                              const FreeListSecrets &secrets) const {
+    return node ? secrets.decrypt_prev(node, node->prev) : nullptr;
+  }
+
   /// Push a block to the back of the list.
   /// The block must be large enough to contain a node.
-  LIBC_INLINE void push(BlockRef block) {
+  LIBC_INLINE void push(BlockRef block, const FreeListSecrets &secrets) {
     LIBC_ASSERT(!block.used() &&
                 "only free blocks can be placed on free lists");
-    LIBC_ASSERT(block.inner_size_free() >= sizeof(FreeList) &&
+    LIBC_ASSERT(block.inner_size_free() >= sizeof(Node) &&
                 "block too small to accomodate free list node");
-    push(new (block.usable_space()) Node);
+    push(new (block.usable_space()) Node, secrets);
   }
 
   /// Push an already-constructed node to the back of the list.
   /// This allows pushing derived node types with additional data.
-  void push(Node *node);
+  void push(Node *node, const FreeListSecrets &secrets);
 
   /// Pop the first node from the list.
-  LIBC_INLINE void pop() { remove(begin_); }
+  LIBC_INLINE void pop(const FreeListSecrets &secrets) {
+    remove(begin_, secrets);
+  }
 
   /// Remove an arbitrary node from the list.
-  void remove(Node *node);
+  void remove(Node *node, const FreeListSecrets &secrets);
+
+  /// Verify secret invariants for all nodes in the list.
+  void sanitize(const FreeListSecrets &secrets) const;
 
 private:
   Node *begin_;
diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index a591d63ddd4e5..46fc0083a17b7 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -21,6 +21,7 @@
 #include "src/__support/CPP/optional.h"
 #include "src/__support/CPP/span.h"
 #include "src/__support/libc_assert.h"
+#include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/math_extras.h"
 #include "src/string/memory_utils/inline_memcpy.h"
@@ -53,9 +54,15 @@ class FreeListHeap {
 
   cpp::span<cpp::byte> region() const { return {begin, end}; }
 
-private:
-  void init();
+#if LIBC_COPT_HARDEN_FREELIST
+  void init(const FreeListSecrets &secrets);
+#else
+  void init(const FreeListSecrets &secrets = {});
+#endif
+
+  void sanitize_heap() const;
 
+private:
   void *allocate_impl(size_t alignment, size_t size);
 
   span<cpp::byte> block_to_span(BlockRef block) {
@@ -70,6 +77,7 @@ class FreeListHeap {
   cpp::byte *end;
   bool is_initialized = false;
   FreeStore free_store;
+  LIBC_NO_UNIQUE_ADDRESS FreeListSecrets secrets;
 };
 
 template <size_t BUFF_SIZE> class FreeListHeapBuffer : public FreeListHeap {
@@ -80,12 +88,14 @@ template <size_t BUFF_SIZE> class FreeListHeapBuffer : public FreeListHeap {
   cpp::byte buffer[BUFF_SIZE];
 };
 
-LIBC_INLINE void FreeListHeap::init() {
+[[gnu::noinline]] LIBC_INLINE void
+FreeListHeap::init(const FreeListSecrets &secrets_arg) {
   LIBC_ASSERT(!is_initialized && "duplicate initialization");
+  secrets = secrets_arg;
   auto result = BlockRef::init(region());
   BlockRef block = *result;
   free_store.set_range({0, cpp::bit_ceil(block.inner_size())});
-  free_store.insert(block);
+  free_store.insert(block, secrets);
   is_initialized = true;
 }
 
@@ -93,22 +103,29 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
   if (size == 0)
     return nullptr;
 
-  if (!is_initialized)
+  if (!is_initialized) {
+#if LIBC_COPT_HARDEN_FREELIST
+    LIBC_HARDENING_ASSERT(
+        false &&
+        "Hardened heap must be explicitly initialized via init(secrets)");
+#else
     init();
+#endif
+  }
 
   size_t request_size = BlockRef::min_size_for_allocation(alignment, size);
   if (!request_size)
     return nullptr;
 
-  BlockRef block = free_store.remove_best_fit(request_size);
+  BlockRef block = free_store.remove_best_fit(request_size, secrets);
   if (!block)
     return nullptr;
 
   auto block_info = BlockRef::allocate(block, alignment, size);
   if (block_info.next)
-    free_store.insert(block_info.next);
+    free_store.insert(block_info.next, secrets);
   if (block_info.prev)
-    free_store.insert(block_info.prev);
+    free_store.insert(block_info.prev, secrets);
 
   block_info.block.mark_used();
   return block_info.block.usable_space();
@@ -153,16 +170,16 @@ LIBC_INLINE void FreeListHeap::free(void *ptr) {
 
   if (prev_free) {
     // Remove from free store and merge.
-    free_store.remove(prev_free);
+    free_store.remove(prev_free, secrets);
     block = prev_free;
     block.merge_next();
   }
   if (!next.used()) {
-    free_store.remove(next);
+    free_store.remove(next, secrets);
     block.merge_next();
   }
   // Add back to the freelist
-  free_store.insert(block);
+  free_store.insert(block, secrets);
 }
 
 LIBC_INLINE bool FreeListHeap::shrink_in_place(BlockRef block, size_t size) {
@@ -182,10 +199,10 @@ LIBC_INLINE bool FreeListHeap::shrink_in_place(BlockRef block, size_t size) {
       // to be non-null.
       LIBC_ASSERT(right && "right block must be non-null");
       if (!right.used()) {
-        free_store.remove(right);
+        free_store.remove(right, secrets);
         next_block.merge_next();
       }
-      free_store.insert(next_block);
+      free_store.insert(next_block, secrets);
     }
     return true;
   }
@@ -239,6 +256,12 @@ LIBC_INLINE void *FreeListHeap::calloc(size_t num, size_t size) {
   return ptr;
 }
 
+LIBC_INLINE void FreeListHeap::sanitize_heap() const {
+  if (!is_initialized)
+    return;
+  free_store.sanitize(secrets);
+}
+
 extern FreeListHeap *freelist_heap;
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/freestore.h b/libc/src/__support/freestore.h
index adc0e061ace93..808fcc48b0617 100644
--- a/libc/src/__support/freestore.h
+++ b/libc/src/__support/freestore.h
@@ -36,15 +36,18 @@ class FreeStore {
 
   /// Insert a free block. If the block is too small to be tracked, nothing
   /// happens.
-  void insert(BlockRef block);
+  void insert(BlockRef block, const FreeListSecrets &secrets);
 
   /// Remove a free block. If the block is too small to be tracked, nothing
   /// happens.
-  void remove(BlockRef block);
+  void remove(BlockRef block, const FreeListSecrets &secrets);
 
   /// Remove a best-fit free block that can contain the given size when
   /// allocated. Returns nullptr if there is no such block.
-  BlockRef remove_best_fit(size_t size);
+  BlockRef remove_best_fit(size_t size, const FreeListSecrets &secrets);
+
+  /// Verify secret invariants for all free lists in the store.
+  void sanitize(const FreeListSecrets &secrets) const;
 
 private:
   static constexpr size_t MIN_OUTER_SIZE = align_up(
@@ -68,35 +71,39 @@ class FreeStore {
   FreeTrie large_trie;
 };
 
-LIBC_INLINE void FreeStore::insert(BlockRef block) {
+LIBC_INLINE void FreeStore::insert(BlockRef block,
+                                   const FreeListSecrets &secrets) {
   if (too_small(block))
     return;
   if (is_small(block))
-    small_list(block).push(block);
+    small_list(block).push(block, secrets);
   else
-    large_trie.push(block);
+    large_trie.push(block, secrets);
 }
 
-LIBC_INLINE void FreeStore::remove(BlockRef block) {
+LIBC_INLINE void FreeStore::remove(BlockRef block,
+                                   const FreeListSecrets &secrets) {
   if (too_small(block))
     return;
   if (is_small(block)) {
     small_list(block).remove(
-        reinterpret_cast<FreeList::Node *>(block.usable_space()));
+        reinterpret_cast<FreeList::Node *>(block.usable_space()), secrets);
   } else {
-    large_trie.remove(reinterpret_cast<FreeTrie::Node *>(block.usable_space()));
+    large_trie.remove(reinterpret_cast<FreeTrie::Node *>(block.usable_space()),
+                      secrets);
   }
 }
 
-LIBC_INLINE BlockRef FreeStore::remove_best_fit(size_t size) {
+LIBC_INLINE BlockRef
+FreeStore::remove_best_fit(size_t size, const FreeListSecrets &secrets) {
   if (FreeList *list = find_best_small_fit(size)) {
     BlockRef block = list->front();
-    list->pop();
+    list->pop(secrets);
     return block;
   }
   if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) {
     BlockRef block = best_fit->block();
-    large_trie.remove(best_fit);
+    large_trie.remove(best_fit, secrets);
     return block;
   }
   return BlockRef();
@@ -115,6 +122,12 @@ LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) {
   return nullptr;
 }
 
+LIBC_INLINE void FreeStore::sanitize(const FreeListSecrets &secrets) const {
+  for (const FreeList &list : small_lists)
+    list.sanitize(secrets);
+  large_trie.sanitize(secrets);
+}
+
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
diff --git a/libc/src/__support/freetrie.cpp b/libc/src/__support/freetrie.cpp
index e76efe717f215..056a79b0b4f8a 100644
--- a/libc/src/__support/freetrie.cpp
+++ b/libc/src/__support/freetrie.cpp
@@ -10,10 +10,10 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-void FreeTrie::remove(Node *node) {
+void FreeTrie::remove(Node *node, const FreeListSecrets &secrets) {
   LIBC_ASSERT(!empty() && "cannot remove from empty trie");
   FreeList list = node;
-  list.pop();
+  list.pop(secrets);
   Node *new_node = static_cast<Node *>(list.begin());
   if (!new_node) {
     // The freelist is empty. Replace the subtrie root with an arbitrary leaf.
@@ -61,4 +61,16 @@ void FreeTrie::replace_node(Node *node, Node *new_node) {
     node->upper->parent = new_node;
 }
 
+void FreeTrie::sanitize(const FreeListSecrets &secrets) const {
+  auto sanitize_trie_node = [&](auto &self, const Node *node) -> void {
+    if (!node)
+      return;
+    FreeList list = const_cast<Node *>(node);
+    list.sanitize(secrets);
+    self(self, node->lower);
+    self(self, node->upper);
+  };
+  sanitize_trie_node(sanitize_trie_node, root);
+}
+
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/freetrie.h b/libc/src/__support/freetrie.h
index 9e35463462b38..31c62543b04a5 100644
--- a/libc/src/__support/freetrie.h
+++ b/libc/src/__support/freetrie.h
@@ -101,15 +101,18 @@ class FreeTrie {
   LIBC_INLINE bool empty() const { return !root; }
 
   /// Push a block to the trie.
-  void push(BlockRef block);
+  void push(BlockRef block, const FreeListSecrets &secrets);
 
   /// Remove a node from this trie node's free list.
-  void remove(Node *node);
+  void remove(Node *node, const FreeListSecrets &secrets);
 
   /// @returns A smallest node that can allocate the given size; otherwise
   /// nullptr.
   Node *find_best_fit(size_t size);
 
+  /// Verify secret invariants for all free lists in the trie.
+  void sanitize(const FreeListSecrets &secrets) const;
+
 private:
   /// @returns Whether a node is the head of its containing freelist.
   bool is_head(Node *node) const { return node->parent || node == root; }
@@ -122,7 +125,8 @@ class FreeTrie {
   SizeRange range;
 };
 
-LIBC_INLINE void FreeTrie::push(BlockRef block) {
+LIBC_INLINE void FreeTrie::push(BlockRef block,
+                                const FreeListSecrets &secrets) {
   LIBC_ASSERT(block.inner_size_free() >= sizeof(Node) &&
               "block too small to accomodate free trie node");
   size_t size = block.inner_size();
@@ -152,7 +156,7 @@ LIBC_INLINE void FreeTrie::push(BlockRef block) {
   } else {
     node->parent = nullptr;
   }
-  list.push(node);
+  list.push(node, secrets);
   *cur = static_cast<Node *>(list.begin());
 }
 
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index cffcabcd29bd3..64012b415f235 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -165,4 +165,16 @@ LIBC_THREAD_MODE_EXTERNAL.
 #define LIBC_NO_SANITIZE_OOB_ACCESS
 #endif
 
+#if defined(__has_cpp_attribute)
+#if __has_cpp_attribute(msvc::no_unique_address)
+#define LIBC_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
+#elif __has_cpp_attribute(no_unique_address)
+#define LIBC_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#endif
+#endif
+
+#ifndef LIBC_NO_UNIQUE_ADDRESS
+#define LIBC_NO_UNIQUE_ADDRESS
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H
diff --git a/libc/test/src/__support/freelist_heap_test.cpp b/libc/test/src/__support/freelist_heap_test.cpp
index 68cc30152cd6d..57085275de7bb 100644
--- a/libc/test/src/__support/freelist_heap_test.cpp
+++ b/libc/test/src/__support/freelist_heap_test.cpp
@@ -32,9 +32,17 @@ using LIBC_NAMESPACE::BlockRef;
 using LIBC_NAMESPACE::freelist_heap;
 using LIBC_NAMESPACE::FreeListHeap;
 using LIBC_NAMESPACE::FreeListHeapBuffer;
+using LIBC_NAMESPACE::FreeListSecrets;
 using LIBC_NAMESPACE::cpp::byte;
 using LIBC_NAMESPACE::cpp::span;
 
+#if LIBC_COPT_HARDEN_FREELIST
+#define TEST_SECRETS_INIT FreeListSecrets{0x123, 0x456, 0x789}
+#else
+#define TEST_SECRETS_INIT                                                      \
+  FreeListSecrets {}
+#endif
+
 // Similar to `LlvmLibcBlockTest` in block_test.cpp, we'd like to run the same
 // tests independently for different parameters. In this case, we'd like to test
 // functionality for a `FreeListHeap` and the global `freelist_heap` which was
@@ -53,12 +61,14 @@ using LIBC_NAMESPACE::cpp::span;
     void SetUp() override {                                                    \
       freelist_heap =                                                          \
           new (&fake_global_buffer) FreeListHeapBuffer<BufferSize>;            \
+  ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/205382


More information about the libc-commits mailing list