-
Notifications
You must be signed in to change notification settings - Fork 124
Pointer-Authentication Based Forward Edge Protection #838
Copy link
Copy link
Open
Description
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<Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels