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