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

Daniel Thornburgh via libc-commits libc-commits at lists.llvm.org
Tue Jul 14 16:47:59 PDT 2026


================
@@ -15,9 +15,110 @@
 #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 {
 
+/// Secrets used to encrypt and verify forward and backward pointers in free
+/// lists when hardening is enabled.
+///
+/// When hardening is enabled (LIBC_COPT_HARDEN_FREELIST), we use XOR and
+/// bit-rotation encoding to protect the forward (`next`) and backward (`prev`)
+/// pointers in free list nodes. This encoding mechanism protects heap integrity
+/// against memory corruption and software bugs.
+///
+/// Threat Model and Security Guarantees:
+/// To effectively protect against intentional attackers who might attempt to
+/// infer the secret keys from memory, this hardening mechanism relies on the
+/// following assumptions:
+///
+/// - Header Separation: We assume that the allocator metadata (the heap header
+///   and secret keys) is isolated from dynamic data memory. This ensures that a
+///   linear buffer overflow read cannot easily dump the secret keys from
+///   adjacent memory.
+///
+/// - Allocation Randomness: We assume that allocation and deallocation patterns
+///   have sufficient randomness or diversity such that an attacker cannot
+///   reliably predict the exact memory layout of free list nodes. If an
+///   attacker could reliably guess node addresses and read forward/backward
+///   pointers via local overflow reads, they could XOR known addresses against
+///   encoded pointers to derive the secret keys.
+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) {}
----------------
mysterymath wrote:

It seems fraught with peril to have a "secrets" constructor that puts in zeros. Def think this shouldn't exist; if you want all zeros, you should have to say that. 

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


More information about the libc-commits mailing list