Add and plumb unsafe_{to,from}_uintptr<T> casts

These encapsulate the wildly powerful reinterpret_cast<> operator where one side
is a uintptr_t and the other is a native pointer.  In both cases we require the
pointer type to be explicitly given.
This commit is contained in:
Nathaniel Wesley Filardo
2022-03-17 17:34:26 +00:00
committed by Nathaniel Wesley Filardo
parent 4ad99d7392
commit 26324e8bfc
6 changed files with 45 additions and 21 deletions

View File

@@ -28,8 +28,8 @@ namespace snmalloc
inline U* pointer_offset(T* base, size_t diff)
{
SNMALLOC_ASSERT(base != nullptr); /* Avoid UB */
return reinterpret_cast<U*>(
reinterpret_cast<uintptr_t>(base) + static_cast<uintptr_t>(diff));
return unsafe_from_uintptr<U>(
unsafe_to_uintptr<T>(base) + static_cast<uintptr_t>(diff));
}
template<SNMALLOC_CONCEPT(capptr::ConceptBound) bounds, typename T>
@@ -129,8 +129,8 @@ namespace snmalloc
template<size_t alignment, typename T = void>
inline T* pointer_align_down(void* p)
{
return reinterpret_cast<T*>(
pointer_align_down<alignment>(reinterpret_cast<uintptr_t>(p)));
return unsafe_from_uintptr<T>(
pointer_align_down<alignment>(unsafe_to_uintptr<void>(p)));
}
template<
@@ -164,8 +164,8 @@ namespace snmalloc
#if __has_builtin(__builtin_align_up)
return static_cast<T*>(__builtin_align_up(p, alignment));
#else
return reinterpret_cast<T*>(
bits::align_up(reinterpret_cast<uintptr_t>(p), alignment));
return unsafe_from_uintptr<T>(
bits::align_up(unsafe_to_uintptr<void>(p), alignment));
#endif
}
}
@@ -197,8 +197,8 @@ namespace snmalloc
#if __has_builtin(__builtin_align_down)
return static_cast<T*>(__builtin_align_down(p, alignment));
#else
return reinterpret_cast<T*>(
bits::align_down(reinterpret_cast<uintptr_t>(p), alignment));
return unsafe_from_uintptr<T>(
bits::align_down(unsafe_to_uintptr<void>(p), alignment));
#endif
}
@@ -221,8 +221,8 @@ namespace snmalloc
#if __has_builtin(__builtin_align_up)
return static_cast<T*>(__builtin_align_up(p, alignment));
#else
return reinterpret_cast<T*>(
bits::align_up(reinterpret_cast<uintptr_t>(p), alignment));
return unsafe_from_uintptr<T>(
bits::align_up(unsafe_to_uintptr<void>(p), alignment));
#endif
}

View File

@@ -7,6 +7,30 @@
namespace snmalloc
{
/*
* reinterpret_cast<> is a powerful primitive that, excitingly, does not
* require the programmer to annotate the expected *source* type. We
* therefore wrap its use to interconvert between uintptr_t and pointer types.
*/
/**
* Convert a pointer to a uintptr_t. Template argument inference is
* prohibited.
*/
template<typename T>
SNMALLOC_FAST_PATH_INLINE uintptr_t
unsafe_to_uintptr(std::enable_if_t<true, T>* p)
{
return reinterpret_cast<uintptr_t>(p);
}
/** Convert a uintptr_t to a T*. */
template<typename T>
SNMALLOC_FAST_PATH_INLINE T* unsafe_from_uintptr(uintptr_t p)
{
return reinterpret_cast<T*>(p);
}
/**
* To assist in providing a uniform interface regardless of pointer wrapper,
* we also export intrinsic pointer and atomic pointer aliases, as the postfix
@@ -321,7 +345,7 @@ namespace snmalloc
[[nodiscard]] SNMALLOC_FAST_PATH uintptr_t unsafe_uintptr() const
{
return reinterpret_cast<uintptr_t>(this->unsafe_capptr);
return unsafe_to_uintptr<T>(this->unsafe_capptr);
}
};

View File

@@ -239,8 +239,8 @@ namespace snmalloc
if constexpr (CHECK_CLIENT && !aal_supports<StrictProvenance>)
{
return reinterpret_cast<Object::T<BQueue>*>(
reinterpret_cast<uintptr_t>(next) ^ key.key_next);
return unsafe_from_uintptr<Object::T<BQueue>>(
unsafe_to_uintptr<Object::T<BQueue>>(next) ^ key.key_next);
}
else
{

View File

@@ -272,7 +272,7 @@ namespace snmalloc
*/
SNMALLOC_FAST_PATH
MetaEntry(Metaslab* meta, uintptr_t remote_and_sizeclass)
: meta(reinterpret_cast<uintptr_t>(meta)),
: meta(unsafe_to_uintptr<Metaslab>(meta)),
remote_and_sizeclass(remote_and_sizeclass)
{}
@@ -281,11 +281,11 @@ namespace snmalloc
Metaslab* meta,
RemoteAllocator* remote,
sizeclass_t sizeclass = sizeclass_t())
: meta(reinterpret_cast<uintptr_t>(meta))
: meta(unsafe_to_uintptr<Metaslab>(meta))
{
/* remote might be nullptr; cast to uintptr_t before offsetting */
remote_and_sizeclass =
pointer_offset(reinterpret_cast<uintptr_t>(remote), sizeclass.raw());
remote_and_sizeclass = pointer_offset(
unsafe_to_uintptr<RemoteAllocator>(remote), sizeclass.raw());
}
/**
@@ -296,7 +296,7 @@ namespace snmalloc
[[nodiscard]] SNMALLOC_FAST_PATH Metaslab* get_metaslab() const
{
SNMALLOC_ASSERT(get_remote() != nullptr);
return reinterpret_cast<Metaslab*>(meta & ~BOUNDARY_BIT);
return unsafe_from_uintptr<Metaslab>(meta & ~BOUNDARY_BIT);
}
/**
@@ -312,7 +312,7 @@ namespace snmalloc
[[nodiscard]] SNMALLOC_FAST_PATH RemoteAllocator* get_remote() const
{
return reinterpret_cast<RemoteAllocator*>(
return unsafe_from_uintptr<RemoteAllocator>(
pointer_align_down<REMOTE_WITH_BACKEND_MARKER_ALIGN>(
remote_and_sizeclass));
}

View File

@@ -20,7 +20,7 @@ int main()
auto& global = GlobalChunkmap::pagemap();
SNMALLOC_CHECK(&p == &global);
// Get a valid heap address
uintptr_t addr = reinterpret_cast<uintptr_t>(malloc(42));
uintptr_t addr = unsafe_to_uintptr<void>(malloc(42));
// Make this very strongly aligned
addr &= ~0xfffffULL;
void* page = p.page_for_address(addr);

View File

@@ -212,7 +212,7 @@ int main(int argc, char** argv)
//
// Note: We cannot use the check or assert macros here because they depend on
// `MessageBuilder` working. They are safe to use in any other test.
void* fakeptr = reinterpret_cast<void*>(static_cast<uintptr_t>(0x42));
void* fakeptr = unsafe_from_uintptr<void>(static_cast<uintptr_t>(0x42));
MessageBuilder<1024> b{
"testing pointer {} size_t {} message, {} world, null is {}, -123456 is "
"{}, 1234567 is {}",