From df3664c4c7e21a82c4bed0a6cb7bd2139237aa36 Mon Sep 17 00:00:00 2001 From: Andrew Drake Date: Sun, 22 Jun 2025 14:58:27 -0700 Subject: [PATCH 1/3] Enforce minimum alignment on entries to fix tagged pointer unsoundness --- src/raw/mod.rs | 5 +++-- src/raw/utils/tagged.rs | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 79eb825..4fc5ac7 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -118,8 +118,9 @@ pub enum RawInsertResult<'g, K, V> { }, } -// An entry in the hash-table. -#[repr(C)] +// An entry in the hash-table. We force a minimum of 8-byte alignment because +// we store entry flags in the low 3 bits of pointers to this type. +#[repr(C, align(8))] pub struct Entry { /// The key for this entry. pub key: K, diff --git a/src/raw/utils/tagged.rs b/src/raw/utils/tagged.rs index 793ebfb..4ab0120 100644 --- a/src/raw/utils/tagged.rs +++ b/src/raw/utils/tagged.rs @@ -33,6 +33,9 @@ unsafe impl StrictProvenance for *mut T { where T: Unpack, { + // This assert will fail at compile time if T doesn't have an alignment that + // guarantees all valid pointers have zero in the bits excluded by T::MASK. + const { assert!(align_of::() > !T::MASK) }; Tagged { raw: self, ptr: self.map_addr(|addr| addr & T::MASK), From fdb0aad9a510b79ad3b1869f70371484921a095f Mon Sep 17 00:00:00 2001 From: Andrew Drake Date: Sun, 22 Jun 2025 21:08:32 -0700 Subject: [PATCH 2/3] Fix Rust 1.72 compatibility --- src/raw/utils/tagged.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/raw/utils/tagged.rs b/src/raw/utils/tagged.rs index 4ab0120..962a6c5 100644 --- a/src/raw/utils/tagged.rs +++ b/src/raw/utils/tagged.rs @@ -1,3 +1,4 @@ +use std::mem::align_of; use std::sync::atomic::{AtomicPtr, Ordering}; // Polyfill for the unstable strict-provenance APIs. @@ -17,6 +18,16 @@ pub trait Unpack { const MASK: usize; } +// This function does nothing, but will fail to compile if T doesn't have an alignment +// that guarantees all valid pointers have zero in the bits excluded by T::MASK. +const fn static_assert_align_of() { + struct Dummy(T); + impl Dummy { + const ASSERT: () = assert!(align_of::() > !T::MASK); + } + Dummy::::ASSERT +} + unsafe impl StrictProvenance for *mut T { #[inline(always)] fn addr(self) -> usize { @@ -33,9 +44,7 @@ unsafe impl StrictProvenance for *mut T { where T: Unpack, { - // This assert will fail at compile time if T doesn't have an alignment that - // guarantees all valid pointers have zero in the bits excluded by T::MASK. - const { assert!(align_of::() > !T::MASK) }; + static_assert_align_of::(); Tagged { raw: self, ptr: self.map_addr(|addr| addr & T::MASK), From 2851f093ed30e613feaf655d14f6eb50f73de73f Mon Sep 17 00:00:00 2001 From: Andrew Drake Date: Tue, 24 Jun 2025 02:59:09 -0700 Subject: [PATCH 3/3] Move assertion const to Unpack trait --- src/raw/utils/tagged.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/raw/utils/tagged.rs b/src/raw/utils/tagged.rs index 962a6c5..c92de4b 100644 --- a/src/raw/utils/tagged.rs +++ b/src/raw/utils/tagged.rs @@ -13,19 +13,13 @@ pub unsafe trait StrictProvenance: Sized { } // Unpack a tagged pointer. -pub trait Unpack { +pub trait Unpack: Sized { // A mask for the pointer tag bits. const MASK: usize; -} -// This function does nothing, but will fail to compile if T doesn't have an alignment -// that guarantees all valid pointers have zero in the bits excluded by T::MASK. -const fn static_assert_align_of() { - struct Dummy(T); - impl Dummy { - const ASSERT: () = assert!(align_of::() > !T::MASK); - } - Dummy::::ASSERT + // This constant, if used, will fail to compile if T doesn't have an alignment + // that guarantees all valid pointers have zero in the bits excluded by T::MASK. + const ASSERT_ALIGNMENT: () = assert!(align_of::() > !Self::MASK); } unsafe impl StrictProvenance for *mut T { @@ -44,7 +38,7 @@ unsafe impl StrictProvenance for *mut T { where T: Unpack, { - static_assert_align_of::(); + let () = T::ASSERT_ALIGNMENT; Tagged { raw: self, ptr: self.map_addr(|addr| addr & T::MASK),