1 #pragma once
2
3 #if DXX_HAVE_POISON
4 #include <algorithm>
5 #endif
6
7 #include <memory>
8
9 #if DXX_HAVE_POISON_VALGRIND
10 #include <valgrind/memcheck.h>
11 #endif
12
13 /* Inform Valgrind that the memory range contains writable but
14 * unreadable bytes.
15 *
16 * On non-Valgrind runs, this is a no-op.
17 */
18 template <typename T>
DXX_MAKE_MEM_UNDEFINED(T * b,unsigned long l)19 static inline void DXX_MAKE_MEM_UNDEFINED(T *b, unsigned long l)
20 {
21 (void)b;(void)l;
22 #if DXX_HAVE_POISON_VALGRIND
23 VALGRIND_MAKE_MEM_UNDEFINED(b, l);
24 #define DXX_HAVE_POISON_UNDEFINED 1
25 #endif
26 }
27
28 template <typename T>
DXX_CHECK_MEM_IS_DEFINED(const T * const b,unsigned long l)29 static inline void DXX_CHECK_MEM_IS_DEFINED(const T *const b, unsigned long l)
30 {
31 (void)b;(void)l;
32 #if DXX_HAVE_POISON_VALGRIND
33 VALGRIND_CHECK_MEM_IS_DEFINED(b, l);
34 #endif
35 }
36
37 /* Convenience function to invoke
38 * `DXX_MAKE_MEM_UNDEFINED(T *, unsigned long)`
39 */
40 template <typename T>
DXX_MAKE_MEM_UNDEFINED(T * b,T * e)41 static inline void DXX_MAKE_MEM_UNDEFINED(T *b, T *e)
42 {
43 unsigned char *bc = reinterpret_cast<unsigned char *>(b);
44 DXX_MAKE_MEM_UNDEFINED(bc, reinterpret_cast<unsigned char *>(e) - bc);
45 }
46
47 /* Convenience function to invoke
48 * `DXX_CHECK_MEM_IS_DEFINED(T *, unsigned long)` for exactly one instance
49 * of `T`.
50 */
51 template <typename T>
DXX_CHECK_VAR_IS_DEFINED(const T & b)52 static inline void DXX_CHECK_VAR_IS_DEFINED(const T &b)
53 {
54 const unsigned char *const bc = reinterpret_cast<const unsigned char *>(std::addressof(b));
55 DXX_CHECK_MEM_IS_DEFINED(bc, sizeof(T));
56 #if DXX_HAVE_POISON
57 #ifdef __SANITIZE_ADDRESS__
58 #define DXX_READ_BUILTIN_TYPE_IF_SIZE(TYPE) \
59 if constexpr (sizeof(T) == sizeof(TYPE)) \
60 { \
61 asm("" :: "rm" (*reinterpret_cast<const TYPE *>(bc))); \
62 } \
63 else \
64
65 DXX_READ_BUILTIN_TYPE_IF_SIZE(uintptr_t)
66 DXX_READ_BUILTIN_TYPE_IF_SIZE(uint64_t)
67 DXX_READ_BUILTIN_TYPE_IF_SIZE(uint32_t)
68 DXX_READ_BUILTIN_TYPE_IF_SIZE(uint16_t)
69 DXX_READ_BUILTIN_TYPE_IF_SIZE(uint8_t)
70 #undef DXX_READ_BUILTIN_TYPE_IF_SIZE
71 {
72 auto i = bc;
73 auto e = reinterpret_cast<const unsigned char *>(bc + sizeof(T));
74 for (; i != e; ++i)
75 asm("" :: "rm" (*(i)));
76 }
77 #endif
78 #endif
79 }
80
81 /* Convenience function to invoke
82 * `DXX_MAKE_MEM_UNDEFINED(T *, unsigned long)` for exactly one instance
83 * of `T`.
84 */
85 template <typename T>
DXX_MAKE_VAR_UNDEFINED(T & b)86 static inline void DXX_MAKE_VAR_UNDEFINED(T &b)
87 {
88 unsigned char *const bc = reinterpret_cast<unsigned char *>(std::addressof(b));
89 DXX_MAKE_MEM_UNDEFINED(bc, sizeof(T));
90 }
91
92 /* If enabled, overwrite the specified memory range with copies of value `v`.
93 * In poison=overwrite builds, this is always enabled.
94 * In poison=valgrind builds, this is enabled if running under Valgrind.
95 *
96 * If disabled, this is a no-op.
97 */
98 template <typename T, typename V>
DXX_POISON_MEMORY_RANGE(T b,T e,const V & v)99 static inline void DXX_POISON_MEMORY_RANGE(T b, T e, const V &v)
100 {
101 #if DXX_HAVE_POISON
102 int store = DXX_HAVE_POISON_OVERWRITE;
103 #if DXX_HAVE_POISON_VALGRIND
104 if (!store)
105 store |= RUNNING_ON_VALGRIND;
106 #endif
107 if (!store)
108 return;
109 std::fill(b, e, v);
110 #else
111 (void)b;(void)e;(void)v;
112 #endif
113 }
114
115 /* Poison a memory range, but do not mark it as unreadable. This is
116 * necessary when poisoning a buffer that will be written to the file or
117 * to a network peer and some of the poisoned fields are expected not to
118 * be updated (but cannot be removed from the source because the layout
119 * is mandatory).
120 */
121 template <typename T, typename V>
DXX_POISON_DEFINED_MEMORY(T b,unsigned long l,const V & v)122 static inline void DXX_POISON_DEFINED_MEMORY(T b, unsigned long l, const V &v)
123 {
124 DXX_POISON_MEMORY_RANGE(b, b + l, v);
125 }
126
127 /* Poison a memory range, then mark it unreadable.
128 */
129 template <typename T>
DXX_POISON_MEMORY(T b,unsigned long l,const uint8_t v)130 static inline void DXX_POISON_MEMORY(T b, unsigned long l, const uint8_t v)
131 {
132 DXX_POISON_DEFINED_MEMORY(b, l, v);
133 DXX_MAKE_MEM_UNDEFINED(b, l);
134 }
135
136 /* Convenience function to invoke
137 * `DXX_POISON_MEMORY(T &, unsigned long, V)`
138 */
139 template <typename T>
DXX_POISON_MEMORY(T b,T e,const uint8_t && v)140 static inline void DXX_POISON_MEMORY(T b, T e, const uint8_t &&v)
141 {
142 DXX_POISON_MEMORY_RANGE(b, e, v);
143 DXX_MAKE_MEM_UNDEFINED(b, e);
144 }
145
146 /* Convenience function to invoke
147 * `DXX_POISON_MEMORY(T &, unsigned long, V)` for exactly one instance
148 * of `T`.
149 */
150 template <typename T>
DXX_POISON_VAR(T & b,const uint8_t v)151 static inline void DXX_POISON_VAR(T &b, const uint8_t v)
152 {
153 DXX_POISON_MEMORY(reinterpret_cast<unsigned char *>(std::addressof(b)), sizeof(T), v);
154 }
155
156 /* Convenience function to invoke
157 * `DXX_POISON_DEFINED_MEMORY(T &, unsigned long, V)` for exactly one
158 * instance of `T`.
159 */
160 template <typename T>
DXX_POISON_DEFINED_VAR(T & b,const unsigned char v)161 static inline void DXX_POISON_DEFINED_VAR(T &b, const unsigned char v)
162 {
163 DXX_POISON_DEFINED_MEMORY(reinterpret_cast<unsigned char *>(std::addressof(b)), sizeof(T), v);
164 }
165
166 #ifndef DXX_HAVE_POISON_UNDEFINED
167 #define DXX_HAVE_POISON_UNDEFINED 0
168 #endif
169