xref: /xnu-11417.121.6/tests/ecc_test_helper.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1 #include <errno.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <math.h>
5 #include <ptrauth.h>
6 #include <string.h>
7 #include <sys/mman.h>
8 #include <sys/sysctl.h>
9 #include <unistd.h>
10 
11 #include <mach/mach_vm.h>
12 
13 /*
14  * ecc_test_helper is a convenience binary to induce various ECC errors
15  * it's used by ECC-related tests: XNU unit tests and end-2-end coreos-tests
16  */
17 
18 
19 int verbose = 0;
20 #define PRINTF(...) \
21 	if (verbose) { \
22 	        printf(__VA_ARGS__); \
23 	}
24 
25 __attribute__((noinline))
26 static void
foo(void)27 foo(void)
28 {
29 	PRINTF("In foo()\n");
30 	fflush(stdout);
31 }
32 
33 volatile struct data {
34 	char buffer1[16 * 1024];
35 	int big_data[16 * 1024];
36 	char buffer2[16 * 1024];
37 } x = {
38 	.big_data = {
39 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
40 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
41 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
42 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
43 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
44 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
45 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
46 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
47 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
48 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
49 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
50 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
51 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
52 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
53 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
54 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
55 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
56 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
57 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
58 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
59 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
60 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
61 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
62 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
63 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
64 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
65 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
66 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
67 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
68 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
69 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
70 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
71 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
72 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
73 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
74 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
75 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
76 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
77 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
78 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
79 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
80 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
81 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
82 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
83 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
84 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
85 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
86 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
87 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
88 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
89 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
90 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
91 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
92 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
93 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
94 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
95 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
96 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
97 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
98 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
99 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
100 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
101 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
102 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
103 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
104 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
105 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
106 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
107 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
108 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
109 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
110 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
111 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
112 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
113 		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
114 	}
115 };
116 
117 /*
118  * volatile to stop the compiler from optimizing away calls to atan()
119  */
120 volatile double zero = 0.0;
121 
122 
123 typedef enum TestCase {
124 	Yfoo,
125 	Xfoo,
126 	Yatan,
127 	Xatan,
128 	Xclean,
129 	Xdirty,
130 	Xcopyout,
131 	Xmmap_clean,
132 	Xmmap_dirty,
133 	Xwired,
134 	kernel,
135 
136 	BAD_TEST_CASE
137 } TestCase;
138 
139 typedef struct{
140 	char *key;
141 	enum TestCase val;
142 } testcase_t;
143 
144 #define testCase(name) {#name, name}
145 
146 static testcase_t testcases[] = {
147 	testCase(Yfoo),
148 	testCase(Xfoo),
149 	testCase(Yatan),
150 	testCase(Xatan),
151 	testCase(Xclean),
152 	testCase(Xdirty),
153 	testCase(Xmmap_clean),
154 	testCase(Xmmap_dirty),
155 	testCase(Xcopyout),
156 	testCase(kernel),
157 	testCase(Xwired)
158 };
159 
160 TestCase
get_testcase(char * key)161 get_testcase(char *key)
162 {
163 	int i;
164 	for (i = 0; i < sizeof(testcases) / sizeof(testcase_t); i++) {
165 		testcase_t elem = testcases[i];
166 		if (strcmp(elem.key, key) == 0) {
167 			return elem.val;
168 		}
169 	}
170 	return BAD_TEST_CASE;
171 }
172 
173 int
main(int argc,char ** argv)174 main(int argc, char **argv)
175 {
176 	void *addr;
177 	int *page;
178 	size_t s = sizeof(addr);
179 	int err;
180 	static volatile int readval;
181 	static volatile double readval_d;
182 
183 	/*
184 	 * check for -v for verbose output
185 	 */
186 	if (argc > 1 && strcmp(argv[1], "-v") == 0) {
187 		verbose = 1;
188 	}
189 
190 	/*
191 	 * needs to run as root for sysctl
192 	 */
193 	if (geteuid() != 0) {
194 		printf("Test not running as root, exiting\n");
195 		exit(-1);
196 	}
197 
198 	/*
199 	 * The argument determines what test to try.
200 	 * "Y{name}" is a test, "X{name}" does the test after injecting an ECC error
201 	 *
202 	 * Tests:
203 	 * "foo" - invoke a local TEXT function.
204 	 * "atan" - invoke a shared library TEXT function.
205 	 * "clean" - read from a clean DATA page
206 	 * "dirty" - read from a dirty DATA page
207 	 * "mmap_clean" - read from a clean mmap'd page
208 	 * "mmap_dirty" - read from a dirty mmap'd page
209 	 */
210 	switch (get_testcase(argv[argc - 1])) {
211 	case Yfoo:
212 		foo();
213 		break;
214 	case Xfoo:
215 		PRINTF("Warm up call to foo()\n");
216 		foo();
217 
218 		addr = (void *)ptrauth_strip(&foo, ptrauth_key_function_pointer);
219 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &addr, s);
220 
221 		PRINTF("Calling foo() after injection\n");
222 		foo();
223 
224 		break;
225 	case Yatan:
226 		readval_d = atan(zero);
227 		PRINTF("atan(0) is %g\n", readval_d);
228 		break;
229 	case Xatan:
230 		readval_d = atan(zero);
231 		PRINTF("Warmup call to atan(0) is %g\n", readval_d);
232 
233 		addr = (void *)ptrauth_strip(&atan, ptrauth_key_function_pointer);
234 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &addr, s);
235 
236 		readval_d = atan(zero);
237 		PRINTF("After injection, atan(0) is %g\n", readval_d);
238 		break;
239 	case Xclean:
240 		readval = x.big_data[35];
241 		PRINTF("initial read of clean x.big_data[35] is %d\n", readval);
242 
243 		addr = (void *)&x.big_data[35];
244 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &addr, s);
245 
246 		readval = x.big_data[35];
247 		PRINTF("After injection, read of x.big_data[35] is %d\n", readval);
248 		break;
249 	case Xdirty:
250 		x.big_data[36] = (int)random();
251 		PRINTF("initial read of dirty x.big_data[36] is %d\n", x.big_data[36]);
252 
253 		addr = (void *)&x.big_data[36];
254 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &addr, s);
255 
256 		readval = x.big_data[36];
257 		PRINTF("After injection, read of x.big_data[36] is %d\n", readval);
258 		break;
259 	case Xmmap_clean:
260 		page = (int *)mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
261 		page = (int *)((char *)page + PAGE_SIZE);
262 
263 		readval = *page;
264 		PRINTF("initial read of clean page %p is %d\n", page, readval);
265 
266 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &page, s);
267 
268 		readval = *page;
269 		PRINTF("second read of page is %d\n", readval);
270 		break;
271 	case Xmmap_dirty:
272 		page = (int *) mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
273 		page = (int *)((char *)page + PAGE_SIZE);
274 
275 		*page = 0xFFFF;
276 		PRINTF("initial read of dirty page %p is %d (after write)\n", page, *page);
277 
278 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &page, s);
279 
280 		readval = *page;
281 		PRINTF("second read of page is %d\n", readval);
282 		break;
283 	case Xcopyout:
284 		x.big_data[37] = (int)random();
285 		PRINTF("initial read of dirty x.big_data[37] is %d\n", x.big_data[37]);
286 
287 		addr = (void *)&x.big_data[37];
288 		err = sysctlbyname("vm.inject_ecc_copyout", NULL, NULL, &addr, s);
289 		if (err) {
290 			PRINTF("copyout return %d\n", err);
291 			exit(err);
292 		}
293 
294 		readval = x.big_data[37];
295 		PRINTF("After injection, read of dirty x.big_data[37] is %d\n", readval);
296 		break;
297 	case Xwired:
298 		page = (int *) mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
299 		page = (int *)((char *)page + PAGE_SIZE);
300 		PRINTF("page addr %p\n", page);
301 		if (mlock(page, PAGE_SIZE)) {
302 			printf("Failed to wire, errno: %d", errno);
303 			exit(0);
304 		}
305 
306 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &page, s);
307 
308 		readval = *page;
309 		PRINTF("wire trigger value: %d", readval);
310 
311 		break;
312 	case kernel:
313 		PRINTF("Inducing ECC on kernel page\n");
314 
315 		addr = (void *)1;         /* used to flag some kernel page */
316 		err = sysctlbyname("vm.inject_ecc", NULL, NULL, &addr, s);
317 		exit(0);
318 
319 		break;
320 	case BAD_TEST_CASE:
321 		printf("Unknown test case\n\n");
322 		printf("Valid tests:\n");
323 		for (int i = 0; i < sizeof(testcases) / sizeof(testcase_t); i++) {
324 			testcase_t elem = testcases[i];
325 			printf("%d. %s\n", i + 1, elem.key);
326 		}
327 		printf("\nY{name} is a test, X{name} does the test after injecting an ECC error\n");
328 
329 		exit(1);
330 
331 		break;
332 	}
333 
334 	exit(0);
335 }
336