Skip to content

Pointer-Authentication Based Forward Edge Protection #838

@SchrodingerZhu

Description

@SchrodingerZhu

We can use LLVM intrinsics to support ptr authentication based forward edge protection in architectures without full strict provenance.

https://llvm.org/docs/PointerAuth.html

The following PoC is done by codex but I don't yet have time to fully check it yet. CHERI researchers probably have a better background than me to design the PAC-based hardening.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e21d6bd..da432e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -374,6 +374,41 @@ if(SNMALLOC_COMPILER_SUPPORT_MCX16)
   target_compile_options(snmalloc INTERFACE $<$<COMPILE_LANGUAGE:CXX>:-mcx16>)
 endif()
 
+set(SNMALLOC_COMPILER_SUPPORT_PACA_PACG FALSE)
+check_cxx_compiler_flag(
+  "-Werror -march=armv8.3-a+pauth"
+  SNMALLOC_COMPILER_SUPPORT_MARCH_V83A_PAUTH)
+if(SNMALLOC_COMPILER_SUPPORT_MARCH_V83A_PAUTH)
+  set(SNMALLOC_SAVED_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+  set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=armv8.3-a+pauth")
+  check_cxx_source_compiles("
+#if !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC)
+#  error PAC instructions require AArch64
+#endif
+#if !__has_include(<ptrauth.h>)
+#  error Missing ptrauth.h
+#endif
+#include <ptrauth.h>
+#ifndef __ARM_FEATURE_PAUTH
+#  error Compiler target does not expose pointer authentication
+#endif
+static inline void* sign_and_auth(void* ptr, void* storage)
+{
+  auto discriminator = ptrauth_blend_discriminator(storage, 0x5678U);
+  auto signed_ptr = ptrauth_sign_unauthenticated(
+    ptr, ptrauth_key_process_dependent_data, discriminator);
+  return ptrauth_auth_data(
+    signed_ptr, ptrauth_key_process_dependent_data, discriminator);
+}
+int main()
+{
+  return sign_and_auth(nullptr, reinterpret_cast<void*>(0x1234UL)) != nullptr;
+}
+" SNMALLOC_COMPILER_SUPPORT_PACA_PACG)
+  set(CMAKE_REQUIRED_FLAGS "${SNMALLOC_SAVED_REQUIRED_FLAGS}")
+  unset(SNMALLOC_SAVED_REQUIRED_FLAGS)
+endif()
+
 if (NOT SNMALLOC_HEADER_ONLY_LIBRARY AND SNMALLOC_IPO)
   check_ipo_supported(RESULT HAS_IPO)
   if (HAS_IPO)
@@ -400,6 +435,7 @@ add_as_define(SNMALLOC_PLATFORM_HAS_GETENTROPY)
 add_as_define(SNMALLOC_PTHREAD_ATFORK_WORKS)
 add_as_define(SNMALLOC_HAS_LINUX_RANDOM_H)
 add_as_define(SNMALLOC_HAS_LINUX_FUTEX_H)
+add_as_define(SNMALLOC_COMPILER_SUPPORT_PACA_PACG)
 if (SNMALLOC_NO_REALLOCARRAY)
   add_as_define(SNMALLOC_NO_REALLOCARRAY)
 endif()
diff --git a/src/snmalloc/aal/aal.h b/src/snmalloc/aal/aal.h
index 6f9ab81..32a661e 100644
--- a/src/snmalloc/aal/aal.h
+++ b/src/snmalloc/aal/aal.h
@@ -180,6 +180,38 @@ namespace snmalloc
 #endif
       }
     }
+
+    static SNMALLOC_FAST_PATH uintptr_t
+    pointer_auth_sign_data(
+      uintptr_t value, address_t storage_addr, uintptr_t tweak) noexcept
+    {
+      if constexpr ((Arch::aal_features & PtrAuthentication) != 0)
+      {
+        return Arch::pointer_auth_sign_data(
+          value, static_cast<uintptr_t>(storage_addr), tweak);
+      }
+      else
+      {
+        UNUSED(storage_addr, tweak);
+        return value;
+      }
+    }
+
+    static SNMALLOC_FAST_PATH uintptr_t
+    pointer_auth_auth_data(
+      uintptr_t value, address_t storage_addr, uintptr_t tweak) noexcept
+    {
+      if constexpr ((Arch::aal_features & PtrAuthentication) != 0)
+      {
+        return Arch::pointer_auth_auth_data(
+          value, static_cast<uintptr_t>(storage_addr), tweak);
+      }
+      else
+      {
+        UNUSED(storage_addr, tweak);
+        return value;
+      }
+    }
   };
 
   template<class Arch>
diff --git a/src/snmalloc/aal/aal_arm.h b/src/snmalloc/aal/aal_arm.h
index c076ce7..47792ba 100644
--- a/src/snmalloc/aal/aal_arm.h
+++ b/src/snmalloc/aal/aal_arm.h
@@ -13,6 +13,12 @@
 #endif
 
 #include <stddef.h>
+#include <stdint.h>
+
+#if defined(SNMALLOC_COMPILER_SUPPORT_PACA_PACG) && \
+  defined(__ARM_FEATURE_PAUTH) && __has_include(<ptrauth.h>)
+#  include <ptrauth.h>
+#endif
 
 namespace snmalloc
 {
@@ -28,6 +34,10 @@ namespace snmalloc
     static constexpr uint64_t aal_features = IntegerPointers
 #if defined(SNMALLOC_VA_BITS_32) || !defined(__APPLE__)
       | NoCpuCycleCounters
+#endif
+#if defined(SNMALLOC_COMPILER_SUPPORT_PACA_PACG) && \
+  defined(__ARM_FEATURE_PAUTH)
+      | PtrAuthentication
 #endif
       ;
 
@@ -69,6 +79,31 @@ namespace snmalloc
       return t;
     }
 #endif
+
+#if defined(SNMALLOC_COMPILER_SUPPORT_PACA_PACG) && \
+  defined(__ARM_FEATURE_PAUTH) && __has_include(<ptrauth.h>)
+    static SNMALLOC_FAST_PATH uintptr_t
+    pointer_auth_sign_data(
+      uintptr_t value, uintptr_t storage_addr, uintptr_t tweak) noexcept
+    {
+      return unsafe_to_uintptr<void>(ptrauth_sign_unauthenticated(
+        unsafe_from_uintptr<void>(value),
+        ptrauth_key_process_dependent_data,
+        ptrauth_blend_discriminator(
+          unsafe_from_uintptr<void>(storage_addr), tweak)));
+    }
+
+    static SNMALLOC_FAST_PATH uintptr_t
+    pointer_auth_auth_data(
+      uintptr_t value, uintptr_t storage_addr, uintptr_t tweak) noexcept
+    {
+      return unsafe_to_uintptr<void>(ptrauth_auth_data(
+        unsafe_from_uintptr<void>(value),
+        ptrauth_key_process_dependent_data,
+        ptrauth_blend_discriminator(
+          unsafe_from_uintptr<void>(storage_addr), tweak)));
+    }
+#endif
   };
 
   using AAL_Arch = AAL_arm;
diff --git a/src/snmalloc/aal/aal_consts.h b/src/snmalloc/aal/aal_consts.h
index 7b3d22a..a680237 100644
--- a/src/snmalloc/aal/aal_consts.h
+++ b/src/snmalloc/aal/aal_consts.h
@@ -24,6 +24,12 @@ namespace snmalloc
      * internal high-privilege pointers for recycling memory on free().
      */
     StrictProvenance = (1 << 2),
+    /**
+     * This architecture can authenticate stored internal pointers, allowing
+     * allocator metadata edges to be signed before storage and authenticated on
+     * reload.
+     */
+    PtrAuthentication = (1 << 3),
   };
 
   enum AalName : int
diff --git a/src/snmalloc/mem/freelist.h b/src/snmalloc/mem/freelist.h
index fd91f6d..f2213f2 100644
--- a/src/snmalloc/mem/freelist.h
+++ b/src/snmalloc/mem/freelist.h
@@ -290,16 +290,23 @@ namespace snmalloc
         const FreeListKey& key,
         address_t key_tweak)
       {
-        // Note we can consider other encoding schemes here.
-        //   * XORing curr and next.  This doesn't require any key material
-        //   * XORing (curr * key). This makes it harder to guess the underlying
-        //     key, as each location effectively has its own key.
-        // Curr is not used in the current encoding scheme.
-        UNUSED(curr);
-
         if constexpr (
+          mitigations(freelist_forward_edge) && aal_supports<PtrAuthentication>)
+        {
+          return unsafe_from_uintptr<Object::T<BQueue>>(Aal::pointer_auth_sign_data(
+            unsafe_to_uintptr<Object::T<BQueue>>(next),
+            curr,
+            key.key_next ^ key_tweak));
+        }
+        else if constexpr (
           mitigations(freelist_forward_edge) && !aal_supports<StrictProvenance>)
         {
+          // Note we can consider other encoding schemes here.
+          //   * XORing curr and next.  This doesn't require any key material
+          //   * XORing (curr * key). This makes it harder to guess the
+          //     underlying key, as each location effectively has its own key.
+          // Curr is not used in the current encoding scheme.
+          UNUSED(curr);
           return unsafe_from_uintptr<Object::T<BQueue>>(
             unsafe_to_uintptr<Object::T<BQueue>>(next) ^ key.key_next ^
             key_tweak);
@@ -364,8 +371,20 @@ namespace snmalloc
         const FreeListKey& key,
         address_t key_tweak)
       {
-        return BHeadPtr<BView, BQueue>::unsafe_from(
-          code_next(curr, next.unsafe_ptr(), key, key_tweak));
+        if constexpr (
+          mitigations(freelist_forward_edge) && aal_supports<PtrAuthentication>)
+        {
+          return BHeadPtr<BView, BQueue>::unsafe_from(
+            unsafe_from_uintptr<Object::T<BQueue>>(Aal::pointer_auth_auth_data(
+            unsafe_to_uintptr<Object::T<BQueue>>(next.unsafe_ptr()),
+            curr,
+            key.key_next ^ key_tweak)));
+        }
+        else
+        {
+          return BHeadPtr<BView, BQueue>::unsafe_from(
+            code_next(curr, next.unsafe_ptr(), key, key_tweak));
+        }
       }
 
       template<

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions