Skip to content

Commit 8753917

Browse files
brenoguimvhscampos
authored andcommitted
Avoid strict aliasing violation on type punning inside llvm::PointerIntPair
llvm::PointerIntPair has methods that when used together can invoke undefined behavior by violating strict aliasing. `getPointer()` uses the underlying storage as it's declared: `intptr_t` `getAddrOfPointer()` casts the underlying storage as if it was a `PointerTy` This violates strict aliasing, so depending on how they are used, it's possible to have the compiler to optimize the code in unwanted ways. See the unit test in the patch. We declare a `PointerIntPair` and use the `getAddrOfPointer` method to fill in the a pointer value. Then, when we use `getPointer` the compiler is thrown off, thinking that `intptr_t` storage could not have possibly be changed, and the check fails. Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D124571
1 parent af694df commit 8753917

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

llvm/include/llvm/ADT/PointerIntPair.h

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,44 @@
1919
#include "llvm/Support/type_traits.h"
2020
#include <cassert>
2121
#include <cstdint>
22+
#include <cstring>
2223
#include <limits>
2324

2425
namespace llvm {
2526

27+
namespace detail {
28+
template <typename Ptr> struct PunnedPointer {
29+
static_assert(sizeof(Ptr) == sizeof(intptr_t), "");
30+
31+
// Asserts that allow us to let the compiler implement the destructor and
32+
// copy/move constructors
33+
static_assert(std::is_trivially_destructible<Ptr>::value, "");
34+
static_assert(std::is_trivially_copy_constructible<Ptr>::value, "");
35+
static_assert(std::is_trivially_move_constructible<Ptr>::value, "");
36+
37+
explicit constexpr PunnedPointer(intptr_t i = 0) { *this = i; }
38+
39+
constexpr intptr_t asInt() const {
40+
intptr_t R = 0;
41+
std::memcpy(&R, Data, sizeof(R));
42+
return R;
43+
}
44+
45+
constexpr operator intptr_t() const { return asInt(); }
46+
47+
constexpr PunnedPointer &operator=(intptr_t V) {
48+
std::memcpy(Data, &V, sizeof(Data));
49+
return *this;
50+
}
51+
52+
Ptr *getPointerAddress() { return reinterpret_cast<Ptr *>(Data); }
53+
const Ptr *getPointerAddress() const { return reinterpret_cast<Ptr *>(Data); }
54+
55+
private:
56+
alignas(Ptr) unsigned char Data[sizeof(Ptr)];
57+
};
58+
} // namespace detail
59+
2660
template <typename T, typename Enable> struct DenseMapInfo;
2761
template <typename PointerT, unsigned IntBits, typename PtrTraits>
2862
struct PointerIntPairInfo;
@@ -46,7 +80,7 @@ template <typename PointerTy, unsigned IntBits, typename IntType = unsigned,
4680
class PointerIntPair {
4781
// Used by MSVC visualizer and generally helpful for debugging/visualizing.
4882
using InfoTy = Info;
49-
intptr_t Value = 0;
83+
detail::PunnedPointer<PointerTy> Value;
5084

5185
public:
5286
constexpr PointerIntPair() = default;
@@ -86,10 +120,12 @@ class PointerIntPair {
86120
assert(Value == reinterpret_cast<intptr_t>(getPointer()) &&
87121
"Can only return the address if IntBits is cleared and "
88122
"PtrTraits doesn't change the pointer");
89-
return reinterpret_cast<PointerTy *>(&Value);
123+
return Value.getPointerAddress();
90124
}
91125

92-
void *getOpaqueValue() const { return reinterpret_cast<void *>(Value); }
126+
void *getOpaqueValue() const {
127+
return reinterpret_cast<void *>(Value.asInt());
128+
}
93129

94130
void setFromOpaqueValue(void *Val) & {
95131
Value = reinterpret_cast<intptr_t>(Val);

llvm/unittests/ADT/PointerIntPairTest.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,22 @@ TEST(PointerIntPairTest, ManyUnusedBits) {
109109
"trivially copyable");
110110
}
111111

112+
TEST(PointerIntPairTest, TypePunning) {
113+
int I = 0;
114+
int *IntPtr = &I;
115+
116+
int **IntPtrBegin = &IntPtr;
117+
int **IntPtrEnd = IntPtrBegin + 1;
118+
119+
PointerIntPair<int *, 1> Pair;
120+
int **PairAddr = Pair.getAddrOfPointer();
121+
122+
while (IntPtrBegin != IntPtrEnd) {
123+
*PairAddr = *IntPtrBegin;
124+
++PairAddr;
125+
++IntPtrBegin;
126+
}
127+
EXPECT_EQ(Pair.getPointer(), IntPtr);
128+
}
129+
112130
} // end anonymous namespace

0 commit comments

Comments
 (0)