[libcxx-commits] [libcxx] [libc++] Add __pointer_int_pair (PR #94324)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 4 08:19:23 PDT 2024


================
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_POINTER_INT_PAIR_H
+#define _LIBCPP___UTILITY_POINTER_INT_PAIR_H
+
+#include <__assert>
+#include <__bit/bit_log2.h>
+#include <__concepts/derived_from.h>
+#include <__config>
+#include <__tuple/tuple_element.h>
+#include <__tuple/tuple_size.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_unsigned.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/swap.h>
+#include <cstddef>
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 23
+
+// A __pointer_int_pair is a pair of a pointer and an integral type. The lower bits of the pointer that are free
+// due to the alignment requirement of the pointee are used to store the integral type.
+//
+// This imposes a constraint on the number of bits available for the integral type -- the integral type can use
+// at most log2(alignof(T)) bits. This technique allows storing the integral type without additional storage
+// beyond that of the pointer itself, at the cost of some bit twiddling.
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class>
+struct _PointerLikeTraits;
+
+template <class _Tp>
+  requires(!is_void_v<_Tp>)
+struct _PointerLikeTraits<_Tp*> {
+  static constexpr size_t __low_bits_available = std::__bit_log2(alignof(_Tp));
+
+  static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
+  static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
+};
+
+template <class _Tp>
+  requires is_void_v<_Tp>
+struct _PointerLikeTraits<_Tp*> {
+  static constexpr size_t __low_bits_available = 0;
+
+  static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
+  static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
+};
+
+enum class __integer_width : size_t {};
+
+template <class _Pointer, class _IntType, __integer_width __int_bit_count>
+class __pointer_int_pair {
+  using _PointerTraits = _PointerLikeTraits<_Pointer>;
+
+  static constexpr auto __int_width = static_cast<size_t>(__int_bit_count);
+
+  static_assert(__int_width <= _PointerTraits::__low_bits_available,
+                "Not enough bits available for requested bit count");
+  static_assert(is_integral_v<_IntType>, "_IntType has to be an integral type");
+  static_assert(is_unsigned_v<_IntType>, "__pointer_int_pair doesn't work for signed types");
+
+  static constexpr size_t __extra_bits  = _PointerTraits::__low_bits_available - __int_width;
+  static constexpr uintptr_t __int_mask = static_cast<uintptr_t>(1 << _PointerTraits::__low_bits_available) - 1;
+  static constexpr uintptr_t __ptr_mask = ~__int_mask;
+
+  uintptr_t __value_ = 0;
+
+public:
+  __pointer_int_pair()                                     = default;
+  __pointer_int_pair(const __pointer_int_pair&)            = default;
+  __pointer_int_pair(__pointer_int_pair&&)                 = default;
+  __pointer_int_pair& operator=(const __pointer_int_pair&) = default;
+  __pointer_int_pair& operator=(__pointer_int_pair&&)      = default;
+  ~__pointer_int_pair()                                    = default;
+
+  _LIBCPP_HIDE_FROM_ABI __pointer_int_pair(_Pointer __ptr_value, _IntType __int_value)
+      : __value_(_PointerTraits::__to_uintptr(__ptr_value) | (__int_value << __extra_bits)) {
+    _LIBCPP_ASSERT_INTERNAL((__int_value & (__int_mask >> __extra_bits)) == __int_value, "integer is too large!");
----------------
EricWF wrote:

These runtime checks scare me.

If they fail, could an attacker not change the address of the pointer we're calling by controlling the value of the integer?

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


More information about the libcxx-commits mailing list