1 //
2 // Tests for
3 //  bounded_ptr& operator-=(std::ptrdiff_t n);
4 //
5 
6 #include <libkern/c++/bounded_ptr.h>
7 #include <array>
8 #include <darwintest.h>
9 #include <darwintest_utils.h>
10 #include "test_utils.h"
11 
12 #define _assert(...) T_ASSERT_TRUE((__VA_ARGS__), # __VA_ARGS__)
13 
14 struct T { int i; };
15 
16 namespace {
17 struct tracking_policy {
18 	static bool did_trap;
19 	static void
trap__anon2fd011fd0111::tracking_policy20 	trap(char const* msg)
21 	{
22 		did_trap = true;
23 	}
24 };
25 bool tracking_policy::did_trap = false;
26 }
27 
28 template <typename T, typename QualT>
29 static void
tests()30 tests()
31 {
32 	std::array<T, 5> array = {T{0}, T{1}, T{2}, T{3}, T{4}};
33 
34 	// Subtract-assign positive offsets
35 	// T{0}     T{1}     T{2}     T{3}     T{4}     <one-past-last>
36 	//   ^                                                ^
37 	//   |                                                |
38 	// begin                                           end,ptr
39 	{
40 		test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end());
41 		auto& ref = ptr -= 0;
42 		_assert(&ref == &ptr);
43 		_assert(ptr == array.end());
44 	}
45 	{
46 		test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end());
47 		auto& ref = ptr -= 1;
48 		_assert(&ref == &ptr);
49 		_assert(&*ptr == &array[4]);
50 	}
51 	{
52 		test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end());
53 		auto& ref = ptr -= 2;
54 		_assert(&ref == &ptr);
55 		_assert(&*ptr == &array[3]);
56 	}
57 	{
58 		test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end());
59 		auto& ref = ptr -= 3;
60 		_assert(&ref == &ptr);
61 		_assert(&*ptr == &array[2]);
62 	}
63 	{
64 		test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end());
65 		auto& ref = ptr -= 4;
66 		_assert(&ref == &ptr);
67 		_assert(&*ptr == &array[1]);
68 	}
69 	{
70 		test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end());
71 		auto& ref = ptr -= 5;
72 		_assert(&ref == &ptr);
73 		_assert(&*ptr == &array[0]);
74 	}
75 
76 	// Subtract-assign negative offsets
77 	// T{0}     T{1}     T{2}     T{3}     T{4}     <one-past-last>
78 	//   ^                                                ^
79 	//   |                                                |
80 	// begin,ptr                                         end
81 	{
82 		test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end());
83 		auto& ref = ptr -= 0;
84 		_assert(&ref == &ptr);
85 		_assert(&*ptr == &array[0]);
86 	}
87 	{
88 		test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end());
89 		auto& ref = ptr -= -1;
90 		_assert(&ref == &ptr);
91 		_assert(&*ptr == &array[1]);
92 	}
93 	{
94 		test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end());
95 		auto& ref = ptr -= -2;
96 		_assert(&ref == &ptr);
97 		_assert(&*ptr == &array[2]);
98 	}
99 	{
100 		test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end());
101 		auto& ref = ptr -= -3;
102 		_assert(&ref == &ptr);
103 		_assert(&*ptr == &array[3]);
104 	}
105 	{
106 		test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end());
107 		auto& ref = ptr -= -4;
108 		_assert(&ref == &ptr);
109 		_assert(&*ptr == &array[4]);
110 	}
111 	{
112 		test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end());
113 		auto& ref = ptr -= -5;
114 		_assert(&ref == &ptr);
115 		_assert(ptr == array.end());
116 	}
117 
118 	// Make sure we trap on arithmetic overflow in the number of bytes calculation
119 	{
120 		std::ptrdiff_t sizeof_T = sizeof(T); // avoid promotion to unsigned in calculations
121 
122 		// largest (most positive) n for the number of bytes `n * sizeof(T)` not to overflow ptrdiff_t
123 		std::ptrdiff_t max_n = std::numeric_limits<std::ptrdiff_t>::max() / sizeof_T;
124 
125 		// smallest (most negative) n for the number of bytes `n * sizeof(T)` not to overflow ptrdiff_t
126 		std::ptrdiff_t min_n = std::numeric_limits<std::ptrdiff_t>::min() / sizeof_T;
127 
128 		// Overflow with a positive offset
129 		{
130 			libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end());
131 			tracking_policy::did_trap = false;
132 			ptr -= max_n + 1;
133 			_assert(tracking_policy::did_trap);
134 		}
135 
136 		// Overflow with a negative offset
137 		{
138 			libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end());
139 			tracking_policy::did_trap = false;
140 			ptr -= min_n - 1;
141 			_assert(tracking_policy::did_trap);
142 		}
143 	}
144 
145 	// Make sure we trap on arithmetic overflow in the offset calculation
146 	//
147 	// To avoid running into the overflow of `n * sizeof(T)` when ptrdiff_t
148 	// is the same size as int32_t, we test the offset overflow check by
149 	// successive subtraction of smaller offsets.
150 	//
151 	// We basically push the offset right to its limit, and then push it
152 	// past its limit to watch it overflow.
153 	{
154 		std::int64_t sizeof_T = sizeof(T); // avoid promotion to unsigned in calculations
155 
156 		// largest (most positive) n for the number of bytes `n * sizeof(T)` not to overflow the int32_t offset
157 		std::int64_t max_n = std::numeric_limits<std::int32_t>::max() / sizeof_T;
158 
159 		// smallest (most negative) n for the number of bytes `n * sizeof(T)` not to overflow the int32_t offset
160 		std::int64_t min_n = std::numeric_limits<std::int32_t>::min() / sizeof_T;
161 
162 		// Subtract positive offsets
163 		{
164 			libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end());
165 			tracking_policy::did_trap = false;
166 			ptr -= static_cast<ptrdiff_t>(-min_n / 2);
167 			_assert(!tracking_policy::did_trap);
168 			ptr -= static_cast<ptrdiff_t>(-min_n / 2);
169 			_assert(!tracking_policy::did_trap);
170 			ptr -= (-min_n % 2);
171 			_assert(!tracking_policy::did_trap); // offset is now right at its negative limit
172 			ptr -= 1;
173 			_assert(tracking_policy::did_trap);
174 		}
175 
176 		// Subtract negative offsets
177 		{
178 			libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end());
179 			tracking_policy::did_trap = false;
180 			ptr -= static_cast<ptrdiff_t>(-max_n / 2);
181 			_assert(!tracking_policy::did_trap);
182 			ptr -= static_cast<ptrdiff_t>(-max_n / 2);
183 			_assert(!tracking_policy::did_trap);
184 			ptr -= (-max_n % 2);
185 			_assert(!tracking_policy::did_trap); // offset is now right at its positive limit
186 			ptr -= -1;
187 			_assert(tracking_policy::did_trap);
188 		}
189 	}
190 }
191 
192 T_DECL(arith_subtract_assign, "bounded_ptr.arith.subtract_assign", T_META_TAG_VM_PREFERRED) {
193 	tests<T, T>();
194 	tests<T, T const>();
195 	tests<T, T volatile>();
196 	tests<T, T const volatile>();
197 }
198