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