Commit | Line | Data |
---|---|---|
e754aedc DH |
1 | /* |
2 | * mpx-mini-test.c: routines to test Intel MPX (Memory Protection eXtentions) | |
3 | * | |
4 | * Written by: | |
5 | * "Ren, Qiaowei" <qiaowei.ren@intel.com> | |
6 | * "Wei, Gang" <gang.wei@intel.com> | |
7 | * "Hansen, Dave" <dave.hansen@intel.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms and conditions of the GNU General Public License, | |
11 | * version 2. | |
12 | */ | |
13 | ||
14 | /* | |
15 | * 2014-12-05: Dave Hansen: fixed all of the compiler warnings, and made sure | |
16 | * it works on 32-bit. | |
17 | */ | |
18 | ||
19 | int inspect_every_this_many_mallocs = 100; | |
20 | int zap_all_every_this_many_mallocs = 1000; | |
21 | ||
22 | #define _GNU_SOURCE | |
23 | #define _LARGEFILE64_SOURCE | |
24 | ||
25 | #include <string.h> | |
26 | #include <stdio.h> | |
27 | #include <stdint.h> | |
28 | #include <stdbool.h> | |
29 | #include <signal.h> | |
30 | #include <assert.h> | |
31 | #include <stdlib.h> | |
32 | #include <ucontext.h> | |
33 | #include <sys/mman.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/stat.h> | |
36 | #include <fcntl.h> | |
37 | #include <unistd.h> | |
38 | ||
39 | #include "mpx-hw.h" | |
40 | #include "mpx-debug.h" | |
41 | #include "mpx-mm.h" | |
42 | ||
43 | #ifndef __always_inline | |
44 | #define __always_inline inline __attribute__((always_inline) | |
45 | #endif | |
46 | ||
47 | #ifndef TEST_DURATION_SECS | |
48 | #define TEST_DURATION_SECS 3 | |
49 | #endif | |
50 | ||
51 | void write_int_to(char *prefix, char *file, int int_to_write) | |
52 | { | |
53 | char buf[100]; | |
54 | int fd = open(file, O_RDWR); | |
55 | int len; | |
56 | int ret; | |
57 | ||
58 | assert(fd >= 0); | |
59 | len = snprintf(buf, sizeof(buf), "%s%d", prefix, int_to_write); | |
60 | assert(len >= 0); | |
61 | assert(len < sizeof(buf)); | |
62 | ret = write(fd, buf, len); | |
63 | assert(ret == len); | |
64 | ret = close(fd); | |
65 | assert(!ret); | |
66 | } | |
67 | ||
68 | void write_pid_to(char *prefix, char *file) | |
69 | { | |
70 | write_int_to(prefix, file, getpid()); | |
71 | } | |
72 | ||
73 | void trace_me(void) | |
74 | { | |
75 | /* tracing events dir */ | |
76 | #define TED "/sys/kernel/debug/tracing/events/" | |
77 | /* | |
78 | write_pid_to("common_pid=", TED "signal/filter"); | |
79 | write_pid_to("common_pid=", TED "exceptions/filter"); | |
80 | write_int_to("", TED "signal/enable", 1); | |
81 | write_int_to("", TED "exceptions/enable", 1); | |
82 | */ | |
83 | write_pid_to("", "/sys/kernel/debug/tracing/set_ftrace_pid"); | |
84 | write_int_to("", "/sys/kernel/debug/tracing/trace", 0); | |
85 | } | |
86 | ||
87 | #define test_failed() __test_failed(__FILE__, __LINE__) | |
88 | static void __test_failed(char *f, int l) | |
89 | { | |
90 | fprintf(stderr, "abort @ %s::%d\n", f, l); | |
91 | abort(); | |
92 | } | |
93 | ||
94 | /* Error Printf */ | |
95 | #define eprintf(args...) fprintf(stderr, args) | |
96 | ||
97 | #ifdef __i386__ | |
98 | ||
99 | /* i386 directory size is 4MB */ | |
100 | #define REG_IP_IDX REG_EIP | |
101 | #define REX_PREFIX | |
102 | ||
103 | #define XSAVE_OFFSET_IN_FPMEM sizeof(struct _libc_fpstate) | |
104 | ||
105 | /* | |
106 | * __cpuid() is from the Linux Kernel: | |
107 | */ | |
108 | static inline void __cpuid(unsigned int *eax, unsigned int *ebx, | |
109 | unsigned int *ecx, unsigned int *edx) | |
110 | { | |
111 | /* ecx is often an input as well as an output. */ | |
112 | asm volatile( | |
113 | "push %%ebx;" | |
114 | "cpuid;" | |
115 | "mov %%ebx, %1;" | |
116 | "pop %%ebx" | |
117 | : "=a" (*eax), | |
118 | "=g" (*ebx), | |
119 | "=c" (*ecx), | |
120 | "=d" (*edx) | |
121 | : "0" (*eax), "2" (*ecx)); | |
122 | } | |
123 | ||
124 | #else /* __i386__ */ | |
125 | ||
126 | #define REG_IP_IDX REG_RIP | |
127 | #define REX_PREFIX "0x48, " | |
128 | ||
129 | #define XSAVE_OFFSET_IN_FPMEM 0 | |
130 | ||
131 | /* | |
132 | * __cpuid() is from the Linux Kernel: | |
133 | */ | |
134 | static inline void __cpuid(unsigned int *eax, unsigned int *ebx, | |
135 | unsigned int *ecx, unsigned int *edx) | |
136 | { | |
137 | /* ecx is often an input as well as an output. */ | |
138 | asm volatile( | |
139 | "cpuid;" | |
140 | : "=a" (*eax), | |
141 | "=b" (*ebx), | |
142 | "=c" (*ecx), | |
143 | "=d" (*edx) | |
144 | : "0" (*eax), "2" (*ecx)); | |
145 | } | |
146 | ||
147 | #endif /* !__i386__ */ | |
148 | ||
149 | struct xsave_hdr_struct { | |
150 | uint64_t xstate_bv; | |
151 | uint64_t reserved1[2]; | |
152 | uint64_t reserved2[5]; | |
153 | } __attribute__((packed)); | |
154 | ||
155 | struct bndregs_struct { | |
156 | uint64_t bndregs[8]; | |
157 | } __attribute__((packed)); | |
158 | ||
159 | struct bndcsr_struct { | |
160 | uint64_t cfg_reg_u; | |
161 | uint64_t status_reg; | |
162 | } __attribute__((packed)); | |
163 | ||
164 | struct xsave_struct { | |
165 | uint8_t fpu_sse[512]; | |
166 | struct xsave_hdr_struct xsave_hdr; | |
167 | uint8_t ymm[256]; | |
168 | uint8_t lwp[128]; | |
169 | struct bndregs_struct bndregs; | |
170 | struct bndcsr_struct bndcsr; | |
171 | } __attribute__((packed)); | |
172 | ||
173 | uint8_t __attribute__((__aligned__(64))) buffer[4096]; | |
174 | struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer; | |
175 | ||
176 | uint8_t __attribute__((__aligned__(64))) test_buffer[4096]; | |
177 | struct xsave_struct *xsave_test_buf = (struct xsave_struct *)test_buffer; | |
178 | ||
179 | uint64_t num_bnd_chk; | |
180 | ||
181 | static __always_inline void xrstor_state(struct xsave_struct *fx, uint64_t mask) | |
182 | { | |
183 | uint32_t lmask = mask; | |
184 | uint32_t hmask = mask >> 32; | |
185 | ||
186 | asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t" | |
187 | : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) | |
188 | : "memory"); | |
189 | } | |
190 | ||
191 | static __always_inline void xsave_state_1(void *_fx, uint64_t mask) | |
192 | { | |
193 | uint32_t lmask = mask; | |
194 | uint32_t hmask = mask >> 32; | |
195 | unsigned char *fx = _fx; | |
196 | ||
197 | asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x27\n\t" | |
198 | : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) | |
199 | : "memory"); | |
200 | } | |
201 | ||
202 | static inline uint64_t xgetbv(uint32_t index) | |
203 | { | |
204 | uint32_t eax, edx; | |
205 | ||
206 | asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */ | |
207 | : "=a" (eax), "=d" (edx) | |
208 | : "c" (index)); | |
209 | return eax + ((uint64_t)edx << 32); | |
210 | } | |
211 | ||
212 | static uint64_t read_mpx_status_sig(ucontext_t *uctxt) | |
213 | { | |
214 | memset(buffer, 0, sizeof(buffer)); | |
215 | memcpy(buffer, | |
216 | (uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM, | |
217 | sizeof(struct xsave_struct)); | |
218 | ||
219 | return xsave_buf->bndcsr.status_reg; | |
220 | } | |
221 | ||
222 | #include <pthread.h> | |
223 | ||
224 | static uint8_t *get_next_inst_ip(uint8_t *addr) | |
225 | { | |
226 | uint8_t *ip = addr; | |
227 | uint8_t sib; | |
228 | uint8_t rm; | |
229 | uint8_t mod; | |
230 | uint8_t base; | |
231 | uint8_t modrm; | |
232 | ||
233 | /* determine the prefix. */ | |
234 | switch(*ip) { | |
235 | case 0xf2: | |
236 | case 0xf3: | |
237 | case 0x66: | |
238 | ip++; | |
239 | break; | |
240 | } | |
241 | ||
242 | /* look for rex prefix */ | |
243 | if ((*ip & 0x40) == 0x40) | |
244 | ip++; | |
245 | ||
246 | /* Make sure we have a MPX instruction. */ | |
247 | if (*ip++ != 0x0f) | |
248 | return addr; | |
249 | ||
250 | /* Skip the op code byte. */ | |
251 | ip++; | |
252 | ||
253 | /* Get the modrm byte. */ | |
254 | modrm = *ip++; | |
255 | ||
256 | /* Break it down into parts. */ | |
257 | rm = modrm & 7; | |
258 | mod = (modrm >> 6); | |
259 | ||
260 | /* Init the parts of the address mode. */ | |
261 | base = 8; | |
262 | ||
263 | /* Is it a mem mode? */ | |
264 | if (mod != 3) { | |
265 | /* look for scaled indexed addressing */ | |
266 | if (rm == 4) { | |
267 | /* SIB addressing */ | |
268 | sib = *ip++; | |
269 | base = sib & 7; | |
270 | switch (mod) { | |
271 | case 0: | |
272 | if (base == 5) | |
273 | ip += 4; | |
274 | break; | |
275 | ||
276 | case 1: | |
277 | ip++; | |
278 | break; | |
279 | ||
280 | case 2: | |
281 | ip += 4; | |
282 | break; | |
283 | } | |
284 | ||
285 | } else { | |
286 | /* MODRM addressing */ | |
287 | switch (mod) { | |
288 | case 0: | |
289 | /* DISP32 addressing, no base */ | |
290 | if (rm == 5) | |
291 | ip += 4; | |
292 | break; | |
293 | ||
294 | case 1: | |
295 | ip++; | |
296 | break; | |
297 | ||
298 | case 2: | |
299 | ip += 4; | |
300 | break; | |
301 | } | |
302 | } | |
303 | } | |
304 | return ip; | |
305 | } | |
306 | ||
307 | #ifdef si_lower | |
308 | static inline void *__si_bounds_lower(siginfo_t *si) | |
309 | { | |
310 | return si->si_lower; | |
311 | } | |
312 | ||
313 | static inline void *__si_bounds_upper(siginfo_t *si) | |
314 | { | |
315 | return si->si_upper; | |
316 | } | |
317 | #else | |
961888b1 RW |
318 | |
319 | /* | |
320 | * This deals with old version of _sigfault in some distros: | |
321 | * | |
322 | ||
323 | old _sigfault: | |
324 | struct { | |
325 | void *si_addr; | |
326 | } _sigfault; | |
327 | ||
328 | new _sigfault: | |
329 | struct { | |
330 | void __user *_addr; | |
331 | int _trapno; | |
332 | short _addr_lsb; | |
333 | union { | |
334 | struct { | |
335 | void __user *_lower; | |
336 | void __user *_upper; | |
337 | } _addr_bnd; | |
338 | __u32 _pkey; | |
339 | }; | |
340 | } _sigfault; | |
341 | * | |
342 | */ | |
343 | ||
e754aedc DH |
344 | static inline void **__si_bounds_hack(siginfo_t *si) |
345 | { | |
346 | void *sigfault = &si->_sifields._sigfault; | |
347 | void *end_sigfault = sigfault + sizeof(si->_sifields._sigfault); | |
961888b1 RW |
348 | int *trapno = (int*)end_sigfault; |
349 | /* skip _trapno and _addr_lsb */ | |
350 | void **__si_lower = (void**)(trapno + 2); | |
e754aedc DH |
351 | |
352 | return __si_lower; | |
353 | } | |
354 | ||
355 | static inline void *__si_bounds_lower(siginfo_t *si) | |
356 | { | |
357 | return *__si_bounds_hack(si); | |
358 | } | |
359 | ||
360 | static inline void *__si_bounds_upper(siginfo_t *si) | |
361 | { | |
961888b1 | 362 | return *(__si_bounds_hack(si) + 1); |
e754aedc DH |
363 | } |
364 | #endif | |
365 | ||
366 | static int br_count; | |
367 | static int expected_bnd_index = -1; | |
368 | uint64_t shadow_plb[NR_MPX_BOUNDS_REGISTERS][2]; /* shadow MPX bound registers */ | |
369 | unsigned long shadow_map[NR_MPX_BOUNDS_REGISTERS]; | |
370 | ||
73bb4d6c IM |
371 | /* Failed address bound checks: */ |
372 | #ifndef SEGV_BNDERR | |
373 | # define SEGV_BNDERR 3 | |
374 | #endif | |
375 | ||
e754aedc DH |
376 | /* |
377 | * The kernel is supposed to provide some information about the bounds | |
378 | * exception in the siginfo. It should match what we have in the bounds | |
379 | * registers that we are checking against. Just check against the shadow copy | |
380 | * since it is easily available, and we also check that *it* matches the real | |
381 | * registers. | |
382 | */ | |
383 | void check_siginfo_vs_shadow(siginfo_t* si) | |
384 | { | |
385 | int siginfo_ok = 1; | |
386 | void *shadow_lower = (void *)(unsigned long)shadow_plb[expected_bnd_index][0]; | |
387 | void *shadow_upper = (void *)(unsigned long)shadow_plb[expected_bnd_index][1]; | |
388 | ||
389 | if ((expected_bnd_index < 0) || | |
390 | (expected_bnd_index >= NR_MPX_BOUNDS_REGISTERS)) { | |
391 | fprintf(stderr, "ERROR: invalid expected_bnd_index: %d\n", | |
392 | expected_bnd_index); | |
393 | exit(6); | |
394 | } | |
395 | if (__si_bounds_lower(si) != shadow_lower) | |
396 | siginfo_ok = 0; | |
397 | if (__si_bounds_upper(si) != shadow_upper) | |
398 | siginfo_ok = 0; | |
399 | ||
400 | if (!siginfo_ok) { | |
401 | fprintf(stderr, "ERROR: siginfo bounds do not match " | |
402 | "shadow bounds for register %d\n", expected_bnd_index); | |
403 | exit(7); | |
404 | } | |
405 | } | |
406 | ||
407 | void handler(int signum, siginfo_t *si, void *vucontext) | |
408 | { | |
409 | int i; | |
410 | ucontext_t *uctxt = vucontext; | |
411 | int trapno; | |
412 | unsigned long ip; | |
413 | ||
414 | dprintf1("entered signal handler\n"); | |
415 | ||
416 | trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO]; | |
417 | ip = uctxt->uc_mcontext.gregs[REG_IP_IDX]; | |
418 | ||
419 | if (trapno == 5) { | |
420 | typeof(si->si_addr) *si_addr_ptr = &si->si_addr; | |
421 | uint64_t status = read_mpx_status_sig(uctxt); | |
422 | uint64_t br_reason = status & 0x3; | |
423 | ||
424 | br_count++; | |
425 | dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count); | |
426 | ||
e754aedc DH |
427 | dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n", |
428 | status, ip, br_reason); | |
429 | dprintf2("si_signo: %d\n", si->si_signo); | |
430 | dprintf2(" signum: %d\n", signum); | |
431 | dprintf2("info->si_code == SEGV_BNDERR: %d\n", | |
432 | (si->si_code == SEGV_BNDERR)); | |
433 | dprintf2("info->si_code: %d\n", si->si_code); | |
434 | dprintf2("info->si_lower: %p\n", __si_bounds_lower(si)); | |
435 | dprintf2("info->si_upper: %p\n", __si_bounds_upper(si)); | |
436 | ||
e754aedc DH |
437 | for (i = 0; i < 8; i++) |
438 | dprintf3("[%d]: %p\n", i, si_addr_ptr[i]); | |
439 | switch (br_reason) { | |
440 | case 0: /* traditional BR */ | |
441 | fprintf(stderr, | |
442 | "Undefined status with bound exception:%jx\n", | |
443 | status); | |
444 | exit(5); | |
445 | case 1: /* #BR MPX bounds exception */ | |
446 | /* these are normal and we expect to see them */ | |
5f2173e0 JR |
447 | |
448 | check_siginfo_vs_shadow(si); | |
449 | ||
e754aedc DH |
450 | dprintf1("bounds exception (normal): status 0x%jx at %p si_addr: %p\n", |
451 | status, (void *)ip, si->si_addr); | |
452 | num_bnd_chk++; | |
453 | uctxt->uc_mcontext.gregs[REG_IP_IDX] = | |
454 | (greg_t)get_next_inst_ip((uint8_t *)ip); | |
455 | break; | |
456 | case 2: | |
457 | fprintf(stderr, "#BR status == 2, missing bounds table," | |
458 | "kernel should have handled!!\n"); | |
459 | exit(4); | |
460 | break; | |
461 | default: | |
462 | fprintf(stderr, "bound check error: status 0x%jx at %p\n", | |
463 | status, (void *)ip); | |
464 | num_bnd_chk++; | |
465 | uctxt->uc_mcontext.gregs[REG_IP_IDX] = | |
466 | (greg_t)get_next_inst_ip((uint8_t *)ip); | |
467 | fprintf(stderr, "bound check error: si_addr %p\n", si->si_addr); | |
468 | exit(3); | |
469 | } | |
470 | } else if (trapno == 14) { | |
471 | eprintf("ERROR: In signal handler, page fault, trapno = %d, ip = %016lx\n", | |
472 | trapno, ip); | |
473 | eprintf("si_addr %p\n", si->si_addr); | |
474 | eprintf("REG_ERR: %lx\n", (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]); | |
475 | test_failed(); | |
476 | } else { | |
477 | eprintf("unexpected trap %d! at 0x%lx\n", trapno, ip); | |
478 | eprintf("si_addr %p\n", si->si_addr); | |
479 | eprintf("REG_ERR: %lx\n", (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]); | |
480 | test_failed(); | |
481 | } | |
482 | } | |
483 | ||
484 | static inline void cpuid_count(unsigned int op, int count, | |
485 | unsigned int *eax, unsigned int *ebx, | |
486 | unsigned int *ecx, unsigned int *edx) | |
487 | { | |
488 | *eax = op; | |
489 | *ecx = count; | |
490 | __cpuid(eax, ebx, ecx, edx); | |
491 | } | |
492 | ||
493 | #define XSTATE_CPUID 0x0000000d | |
494 | ||
495 | /* | |
496 | * List of XSAVE features Linux knows about: | |
497 | */ | |
498 | enum xfeature_bit { | |
499 | XSTATE_BIT_FP, | |
500 | XSTATE_BIT_SSE, | |
501 | XSTATE_BIT_YMM, | |
502 | XSTATE_BIT_BNDREGS, | |
503 | XSTATE_BIT_BNDCSR, | |
504 | XSTATE_BIT_OPMASK, | |
505 | XSTATE_BIT_ZMM_Hi256, | |
506 | XSTATE_BIT_Hi16_ZMM, | |
507 | ||
508 | XFEATURES_NR_MAX, | |
509 | }; | |
510 | ||
511 | #define XSTATE_FP (1 << XSTATE_BIT_FP) | |
512 | #define XSTATE_SSE (1 << XSTATE_BIT_SSE) | |
513 | #define XSTATE_YMM (1 << XSTATE_BIT_YMM) | |
514 | #define XSTATE_BNDREGS (1 << XSTATE_BIT_BNDREGS) | |
515 | #define XSTATE_BNDCSR (1 << XSTATE_BIT_BNDCSR) | |
516 | #define XSTATE_OPMASK (1 << XSTATE_BIT_OPMASK) | |
517 | #define XSTATE_ZMM_Hi256 (1 << XSTATE_BIT_ZMM_Hi256) | |
518 | #define XSTATE_Hi16_ZMM (1 << XSTATE_BIT_Hi16_ZMM) | |
519 | ||
520 | #define MPX_XSTATES (XSTATE_BNDREGS | XSTATE_BNDCSR) /* 0x18 */ | |
521 | ||
522 | bool one_bit(unsigned int x, int bit) | |
523 | { | |
524 | return !!(x & (1<<bit)); | |
525 | } | |
526 | ||
527 | void print_state_component(int state_bit_nr, char *name) | |
528 | { | |
529 | unsigned int eax, ebx, ecx, edx; | |
530 | unsigned int state_component_size; | |
531 | unsigned int state_component_supervisor; | |
532 | unsigned int state_component_user; | |
533 | unsigned int state_component_aligned; | |
534 | ||
535 | /* See SDM Section 13.2 */ | |
536 | cpuid_count(XSTATE_CPUID, state_bit_nr, &eax, &ebx, &ecx, &edx); | |
537 | assert(eax || ebx || ecx); | |
538 | state_component_size = eax; | |
539 | state_component_supervisor = ((!ebx) && one_bit(ecx, 0)); | |
540 | state_component_user = !one_bit(ecx, 0); | |
541 | state_component_aligned = one_bit(ecx, 1); | |
542 | printf("%8s: size: %d user: %d supervisor: %d aligned: %d\n", | |
543 | name, | |
544 | state_component_size, state_component_user, | |
545 | state_component_supervisor, state_component_aligned); | |
546 | ||
547 | } | |
548 | ||
549 | /* Intel-defined CPU features, CPUID level 0x00000001 (ecx) */ | |
550 | #define XSAVE_FEATURE_BIT (26) /* XSAVE/XRSTOR/XSETBV/XGETBV */ | |
551 | #define OSXSAVE_FEATURE_BIT (27) /* XSAVE enabled in the OS */ | |
552 | ||
553 | bool check_mpx_support(void) | |
554 | { | |
555 | unsigned int eax, ebx, ecx, edx; | |
556 | ||
557 | cpuid_count(1, 0, &eax, &ebx, &ecx, &edx); | |
558 | ||
559 | /* We can't do much without XSAVE, so just make these assert()'s */ | |
560 | if (!one_bit(ecx, XSAVE_FEATURE_BIT)) { | |
561 | fprintf(stderr, "processor lacks XSAVE, can not run MPX tests\n"); | |
562 | exit(0); | |
563 | } | |
564 | ||
565 | if (!one_bit(ecx, OSXSAVE_FEATURE_BIT)) { | |
566 | fprintf(stderr, "processor lacks OSXSAVE, can not run MPX tests\n"); | |
567 | exit(0); | |
568 | } | |
569 | ||
570 | /* CPUs not supporting the XSTATE CPUID leaf do not support MPX */ | |
571 | /* Is this redundant with the feature bit checks? */ | |
572 | cpuid_count(0, 0, &eax, &ebx, &ecx, &edx); | |
573 | if (eax < XSTATE_CPUID) { | |
574 | fprintf(stderr, "processor lacks XSTATE CPUID leaf," | |
575 | " can not run MPX tests\n"); | |
576 | exit(0); | |
577 | } | |
578 | ||
579 | printf("XSAVE is supported by HW & OS\n"); | |
580 | ||
581 | cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx); | |
582 | ||
583 | printf("XSAVE processor supported state mask: 0x%x\n", eax); | |
584 | printf("XSAVE OS supported state mask: 0x%jx\n", xgetbv(0)); | |
585 | ||
586 | /* Make sure that the MPX states are enabled in in XCR0 */ | |
587 | if ((eax & MPX_XSTATES) != MPX_XSTATES) { | |
588 | fprintf(stderr, "processor lacks MPX XSTATE(s), can not run MPX tests\n"); | |
589 | exit(0); | |
590 | } | |
591 | ||
592 | /* Make sure the MPX states are supported by XSAVE* */ | |
593 | if ((xgetbv(0) & MPX_XSTATES) != MPX_XSTATES) { | |
594 | fprintf(stderr, "MPX XSTATE(s) no enabled in XCR0, " | |
595 | "can not run MPX tests\n"); | |
596 | exit(0); | |
597 | } | |
598 | ||
599 | print_state_component(XSTATE_BIT_BNDREGS, "BNDREGS"); | |
600 | print_state_component(XSTATE_BIT_BNDCSR, "BNDCSR"); | |
601 | ||
602 | return true; | |
603 | } | |
604 | ||
605 | void enable_mpx(void *l1base) | |
606 | { | |
607 | /* enable point lookup */ | |
608 | memset(buffer, 0, sizeof(buffer)); | |
609 | xrstor_state(xsave_buf, 0x18); | |
610 | ||
611 | xsave_buf->xsave_hdr.xstate_bv = 0x10; | |
612 | xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base | 1; | |
613 | xsave_buf->bndcsr.status_reg = 0; | |
614 | ||
615 | dprintf2("bf xrstor\n"); | |
616 | dprintf2("xsave cndcsr: status %jx, configu %jx\n", | |
617 | xsave_buf->bndcsr.status_reg, xsave_buf->bndcsr.cfg_reg_u); | |
618 | xrstor_state(xsave_buf, 0x18); | |
619 | dprintf2("after xrstor\n"); | |
620 | ||
621 | xsave_state_1(xsave_buf, 0x18); | |
622 | ||
623 | dprintf1("xsave bndcsr: status %jx, configu %jx\n", | |
624 | xsave_buf->bndcsr.status_reg, xsave_buf->bndcsr.cfg_reg_u); | |
625 | } | |
626 | ||
627 | #include <sys/prctl.h> | |
628 | ||
629 | struct mpx_bounds_dir *bounds_dir_ptr; | |
630 | ||
631 | unsigned long __bd_incore(const char *func, int line) | |
632 | { | |
633 | unsigned long ret = nr_incore(bounds_dir_ptr, MPX_BOUNDS_DIR_SIZE_BYTES); | |
634 | return ret; | |
635 | } | |
636 | #define bd_incore() __bd_incore(__func__, __LINE__) | |
637 | ||
638 | void check_clear(void *ptr, unsigned long sz) | |
639 | { | |
640 | unsigned long *i; | |
641 | ||
642 | for (i = ptr; (void *)i < ptr + sz; i++) { | |
643 | if (*i) { | |
644 | dprintf1("%p is NOT clear at %p\n", ptr, i); | |
645 | assert(0); | |
646 | } | |
647 | } | |
648 | dprintf1("%p is clear for %lx\n", ptr, sz); | |
649 | } | |
650 | ||
651 | void check_clear_bd(void) | |
652 | { | |
653 | check_clear(bounds_dir_ptr, 2UL << 30); | |
654 | } | |
655 | ||
656 | #define USE_MALLOC_FOR_BOUNDS_DIR 1 | |
657 | bool process_specific_init(void) | |
658 | { | |
659 | unsigned long size; | |
660 | unsigned long *dir; | |
661 | /* Guarantee we have the space to align it, add padding: */ | |
662 | unsigned long pad = getpagesize(); | |
663 | ||
664 | size = 2UL << 30; /* 2GB */ | |
665 | if (sizeof(unsigned long) == 4) | |
666 | size = 4UL << 20; /* 4MB */ | |
667 | dprintf1("trying to allocate %ld MB bounds directory\n", (size >> 20)); | |
668 | ||
669 | if (USE_MALLOC_FOR_BOUNDS_DIR) { | |
670 | unsigned long _dir; | |
671 | ||
672 | dir = malloc(size + pad); | |
673 | assert(dir); | |
674 | _dir = (unsigned long)dir; | |
675 | _dir += 0xfffUL; | |
676 | _dir &= ~0xfffUL; | |
677 | dir = (void *)_dir; | |
678 | } else { | |
679 | /* | |
680 | * This makes debugging easier because the address | |
681 | * calculations are simpler: | |
682 | */ | |
683 | dir = mmap((void *)0x200000000000, size + pad, | |
684 | PROT_READ|PROT_WRITE, | |
685 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
686 | if (dir == (void *)-1) { | |
687 | perror("unable to allocate bounds directory"); | |
688 | abort(); | |
689 | } | |
690 | check_clear(dir, size); | |
691 | } | |
692 | bounds_dir_ptr = (void *)dir; | |
693 | madvise(bounds_dir_ptr, size, MADV_NOHUGEPAGE); | |
694 | bd_incore(); | |
695 | dprintf1("bounds directory: 0x%p -> 0x%p\n", bounds_dir_ptr, | |
696 | (char *)bounds_dir_ptr + size); | |
697 | check_clear(dir, size); | |
698 | enable_mpx(dir); | |
699 | check_clear(dir, size); | |
700 | if (prctl(43, 0, 0, 0, 0)) { | |
701 | printf("no MPX support\n"); | |
702 | abort(); | |
703 | return false; | |
704 | } | |
705 | return true; | |
706 | } | |
707 | ||
708 | bool process_specific_finish(void) | |
709 | { | |
710 | if (prctl(44)) { | |
711 | printf("no MPX support\n"); | |
712 | return false; | |
713 | } | |
714 | return true; | |
715 | } | |
716 | ||
717 | void setup_handler() | |
718 | { | |
719 | int r, rs; | |
720 | struct sigaction newact; | |
721 | struct sigaction oldact; | |
722 | ||
723 | /* #BR is mapped to sigsegv */ | |
724 | int signum = SIGSEGV; | |
725 | ||
726 | newact.sa_handler = 0; /* void(*)(int)*/ | |
727 | newact.sa_sigaction = handler; /* void (*)(int, siginfo_t*, void *) */ | |
728 | ||
729 | /*sigset_t - signals to block while in the handler */ | |
730 | /* get the old signal mask. */ | |
731 | rs = sigprocmask(SIG_SETMASK, 0, &newact.sa_mask); | |
732 | assert(rs == 0); | |
733 | ||
734 | /* call sa_sigaction, not sa_handler*/ | |
735 | newact.sa_flags = SA_SIGINFO; | |
736 | ||
737 | newact.sa_restorer = 0; /* void(*)(), obsolete */ | |
738 | r = sigaction(signum, &newact, &oldact); | |
739 | assert(r == 0); | |
740 | } | |
741 | ||
742 | void mpx_prepare(void) | |
743 | { | |
744 | dprintf2("%s()\n", __func__); | |
745 | setup_handler(); | |
746 | process_specific_init(); | |
747 | } | |
748 | ||
749 | void mpx_cleanup(void) | |
750 | { | |
751 | printf("%s(): %jd BRs. bye...\n", __func__, num_bnd_chk); | |
752 | process_specific_finish(); | |
753 | } | |
754 | ||
755 | /*-------------- the following is test case ---------------*/ | |
756 | #include <stdint.h> | |
757 | #include <stdbool.h> | |
758 | #include <stdlib.h> | |
759 | #include <stdio.h> | |
760 | #include <time.h> | |
761 | ||
762 | uint64_t num_lower_brs; | |
763 | uint64_t num_upper_brs; | |
764 | ||
765 | #define MPX_CONFIG_OFFSET 1024 | |
766 | #define MPX_BOUNDS_OFFSET 960 | |
767 | #define MPX_HEADER_OFFSET 512 | |
768 | #define MAX_ADDR_TESTED (1<<28) | |
769 | #define TEST_ROUNDS 100 | |
770 | ||
771 | /* | |
772 | 0F 1A /r BNDLDX-Load | |
773 | 0F 1B /r BNDSTX-Store Extended Bounds Using Address Translation | |
774 | 66 0F 1A /r BNDMOV bnd1, bnd2/m128 | |
775 | 66 0F 1B /r BNDMOV bnd1/m128, bnd2 | |
776 | F2 0F 1A /r BNDCU bnd, r/m64 | |
777 | F2 0F 1B /r BNDCN bnd, r/m64 | |
778 | F3 0F 1A /r BNDCL bnd, r/m64 | |
779 | F3 0F 1B /r BNDMK bnd, m64 | |
780 | */ | |
781 | ||
782 | static __always_inline void xsave_state(void *_fx, uint64_t mask) | |
783 | { | |
784 | uint32_t lmask = mask; | |
785 | uint32_t hmask = mask >> 32; | |
786 | unsigned char *fx = _fx; | |
787 | ||
788 | asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x27\n\t" | |
789 | : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) | |
790 | : "memory"); | |
791 | } | |
792 | ||
793 | static __always_inline void mpx_clear_bnd0(void) | |
794 | { | |
795 | long size = 0; | |
796 | void *ptr = NULL; | |
797 | /* F3 0F 1B /r BNDMK bnd, m64 */ | |
798 | /* f3 0f 1b 04 11 bndmk (%rcx,%rdx,1),%bnd0 */ | |
799 | asm volatile(".byte 0xf3,0x0f,0x1b,0x04,0x11\n\t" | |
800 | : : "c" (ptr), "d" (size-1) | |
801 | : "memory"); | |
802 | } | |
803 | ||
804 | static __always_inline void mpx_make_bound_helper(unsigned long ptr, | |
805 | unsigned long size) | |
806 | { | |
807 | /* F3 0F 1B /r BNDMK bnd, m64 */ | |
808 | /* f3 0f 1b 04 11 bndmk (%rcx,%rdx,1),%bnd0 */ | |
809 | asm volatile(".byte 0xf3,0x0f,0x1b,0x04,0x11\n\t" | |
810 | : : "c" (ptr), "d" (size-1) | |
811 | : "memory"); | |
812 | } | |
813 | ||
814 | static __always_inline void mpx_check_lowerbound_helper(unsigned long ptr) | |
815 | { | |
816 | /* F3 0F 1A /r NDCL bnd, r/m64 */ | |
817 | /* f3 0f 1a 01 bndcl (%rcx),%bnd0 */ | |
818 | asm volatile(".byte 0xf3,0x0f,0x1a,0x01\n\t" | |
819 | : : "c" (ptr) | |
820 | : "memory"); | |
821 | } | |
822 | ||
823 | static __always_inline void mpx_check_upperbound_helper(unsigned long ptr) | |
824 | { | |
825 | /* F2 0F 1A /r BNDCU bnd, r/m64 */ | |
826 | /* f2 0f 1a 01 bndcu (%rcx),%bnd0 */ | |
827 | asm volatile(".byte 0xf2,0x0f,0x1a,0x01\n\t" | |
828 | : : "c" (ptr) | |
829 | : "memory"); | |
830 | } | |
831 | ||
832 | static __always_inline void mpx_movbndreg_helper() | |
833 | { | |
834 | /* 66 0F 1B /r BNDMOV bnd1/m128, bnd2 */ | |
835 | /* 66 0f 1b c2 bndmov %bnd0,%bnd2 */ | |
836 | ||
837 | asm volatile(".byte 0x66,0x0f,0x1b,0xc2\n\t"); | |
838 | } | |
839 | ||
840 | static __always_inline void mpx_movbnd2mem_helper(uint8_t *mem) | |
841 | { | |
842 | /* 66 0F 1B /r BNDMOV bnd1/m128, bnd2 */ | |
843 | /* 66 0f 1b 01 bndmov %bnd0,(%rcx) */ | |
844 | asm volatile(".byte 0x66,0x0f,0x1b,0x01\n\t" | |
845 | : : "c" (mem) | |
846 | : "memory"); | |
847 | } | |
848 | ||
849 | static __always_inline void mpx_movbnd_from_mem_helper(uint8_t *mem) | |
850 | { | |
851 | /* 66 0F 1A /r BNDMOV bnd1, bnd2/m128 */ | |
852 | /* 66 0f 1a 01 bndmov (%rcx),%bnd0 */ | |
853 | asm volatile(".byte 0x66,0x0f,0x1a,0x01\n\t" | |
854 | : : "c" (mem) | |
855 | : "memory"); | |
856 | } | |
857 | ||
858 | static __always_inline void mpx_store_dsc_helper(unsigned long ptr_addr, | |
859 | unsigned long ptr_val) | |
860 | { | |
861 | /* 0F 1B /r BNDSTX-Store Extended Bounds Using Address Translation */ | |
862 | /* 0f 1b 04 11 bndstx %bnd0,(%rcx,%rdx,1) */ | |
863 | asm volatile(".byte 0x0f,0x1b,0x04,0x11\n\t" | |
864 | : : "c" (ptr_addr), "d" (ptr_val) | |
865 | : "memory"); | |
866 | } | |
867 | ||
868 | static __always_inline void mpx_load_dsc_helper(unsigned long ptr_addr, | |
869 | unsigned long ptr_val) | |
870 | { | |
871 | /* 0F 1A /r BNDLDX-Load */ | |
872 | /*/ 0f 1a 04 11 bndldx (%rcx,%rdx,1),%bnd0 */ | |
873 | asm volatile(".byte 0x0f,0x1a,0x04,0x11\n\t" | |
874 | : : "c" (ptr_addr), "d" (ptr_val) | |
875 | : "memory"); | |
876 | } | |
877 | ||
878 | void __print_context(void *__print_xsave_buffer, int line) | |
879 | { | |
880 | uint64_t *bounds = (uint64_t *)(__print_xsave_buffer + MPX_BOUNDS_OFFSET); | |
881 | uint64_t *cfg = (uint64_t *)(__print_xsave_buffer + MPX_CONFIG_OFFSET); | |
882 | ||
883 | int i; | |
884 | eprintf("%s()::%d\n", "print_context", line); | |
885 | for (i = 0; i < 4; i++) { | |
886 | eprintf("bound[%d]: 0x%016lx 0x%016lx(0x%016lx)\n", i, | |
887 | (unsigned long)bounds[i*2], | |
888 | ~(unsigned long)bounds[i*2+1], | |
889 | (unsigned long)bounds[i*2+1]); | |
890 | } | |
891 | ||
892 | eprintf("cpcfg: %jx cpstatus: %jx\n", cfg[0], cfg[1]); | |
893 | } | |
894 | #define print_context(x) __print_context(x, __LINE__) | |
895 | #ifdef DEBUG | |
896 | #define dprint_context(x) print_context(x) | |
897 | #else | |
898 | #define dprint_context(x) do{}while(0) | |
899 | #endif | |
900 | ||
901 | void init() | |
902 | { | |
903 | int i; | |
904 | ||
905 | srand((unsigned int)time(NULL)); | |
906 | ||
907 | for (i = 0; i < 4; i++) { | |
908 | shadow_plb[i][0] = 0; | |
909 | shadow_plb[i][1] = ~(unsigned long)0; | |
910 | } | |
911 | } | |
912 | ||
913 | long int __mpx_random(int line) | |
914 | { | |
915 | #ifdef NOT_SO_RANDOM | |
916 | static long fake = 722122311; | |
917 | fake += 563792075; | |
918 | return fakse; | |
919 | #else | |
920 | return random(); | |
921 | #endif | |
922 | } | |
923 | #define mpx_random() __mpx_random(__LINE__) | |
924 | ||
925 | uint8_t *get_random_addr() | |
926 | { | |
927 | uint8_t*addr = (uint8_t *)(unsigned long)(rand() % MAX_ADDR_TESTED); | |
928 | return (addr - (unsigned long)addr % sizeof(uint8_t *)); | |
929 | } | |
930 | ||
931 | static inline bool compare_context(void *__xsave_buffer) | |
932 | { | |
933 | uint64_t *bounds = (uint64_t *)(__xsave_buffer + MPX_BOUNDS_OFFSET); | |
934 | ||
935 | int i; | |
936 | for (i = 0; i < 4; i++) { | |
937 | dprintf3("shadow[%d]{%016lx/%016lx}\nbounds[%d]{%016lx/%016lx}\n", | |
938 | i, (unsigned long)shadow_plb[i][0], (unsigned long)shadow_plb[i][1], | |
939 | i, (unsigned long)bounds[i*2], ~(unsigned long)bounds[i*2+1]); | |
940 | if ((shadow_plb[i][0] != bounds[i*2]) || | |
941 | (shadow_plb[i][1] != ~(unsigned long)bounds[i*2+1])) { | |
942 | eprintf("ERROR comparing shadow to real bound register %d\n", i); | |
943 | eprintf("shadow{0x%016lx/0x%016lx}\nbounds{0x%016lx/0x%016lx}\n", | |
944 | (unsigned long)shadow_plb[i][0], (unsigned long)shadow_plb[i][1], | |
945 | (unsigned long)bounds[i*2], (unsigned long)bounds[i*2+1]); | |
946 | return false; | |
947 | } | |
948 | } | |
949 | ||
950 | return true; | |
951 | } | |
952 | ||
953 | void mkbnd_shadow(uint8_t *ptr, int index, long offset) | |
954 | { | |
955 | uint64_t *lower = (uint64_t *)&(shadow_plb[index][0]); | |
956 | uint64_t *upper = (uint64_t *)&(shadow_plb[index][1]); | |
957 | *lower = (unsigned long)ptr; | |
958 | *upper = (unsigned long)ptr + offset - 1; | |
959 | } | |
960 | ||
961 | void check_lowerbound_shadow(uint8_t *ptr, int index) | |
962 | { | |
963 | uint64_t *lower = (uint64_t *)&(shadow_plb[index][0]); | |
964 | if (*lower > (uint64_t)(unsigned long)ptr) | |
965 | num_lower_brs++; | |
966 | else | |
967 | dprintf1("LowerBoundChk passed:%p\n", ptr); | |
968 | } | |
969 | ||
970 | void check_upperbound_shadow(uint8_t *ptr, int index) | |
971 | { | |
972 | uint64_t upper = *(uint64_t *)&(shadow_plb[index][1]); | |
973 | if (upper < (uint64_t)(unsigned long)ptr) | |
974 | num_upper_brs++; | |
975 | else | |
976 | dprintf1("UpperBoundChk passed:%p\n", ptr); | |
977 | } | |
978 | ||
979 | __always_inline void movbndreg_shadow(int src, int dest) | |
980 | { | |
981 | shadow_plb[dest][0] = shadow_plb[src][0]; | |
982 | shadow_plb[dest][1] = shadow_plb[src][1]; | |
983 | } | |
984 | ||
985 | __always_inline void movbnd2mem_shadow(int src, unsigned long *dest) | |
986 | { | |
987 | unsigned long *lower = (unsigned long *)&(shadow_plb[src][0]); | |
988 | unsigned long *upper = (unsigned long *)&(shadow_plb[src][1]); | |
989 | *dest = *lower; | |
990 | *(dest+1) = *upper; | |
991 | } | |
992 | ||
993 | __always_inline void movbnd_from_mem_shadow(unsigned long *src, int dest) | |
994 | { | |
995 | unsigned long *lower = (unsigned long *)&(shadow_plb[dest][0]); | |
996 | unsigned long *upper = (unsigned long *)&(shadow_plb[dest][1]); | |
997 | *lower = *src; | |
998 | *upper = *(src+1); | |
999 | } | |
1000 | ||
1001 | __always_inline void stdsc_shadow(int index, uint8_t *ptr, uint8_t *ptr_val) | |
1002 | { | |
1003 | shadow_map[0] = (unsigned long)shadow_plb[index][0]; | |
1004 | shadow_map[1] = (unsigned long)shadow_plb[index][1]; | |
1005 | shadow_map[2] = (unsigned long)ptr_val; | |
1006 | dprintf3("%s(%d, %p, %p) set shadow map[2]: %p\n", __func__, | |
1007 | index, ptr, ptr_val, ptr_val); | |
1008 | /*ptr ignored */ | |
1009 | } | |
1010 | ||
1011 | void lddsc_shadow(int index, uint8_t *ptr, uint8_t *ptr_val) | |
1012 | { | |
1013 | uint64_t lower = shadow_map[0]; | |
1014 | uint64_t upper = shadow_map[1]; | |
1015 | uint8_t *value = (uint8_t *)shadow_map[2]; | |
1016 | ||
1017 | if (value != ptr_val) { | |
1018 | dprintf2("%s(%d, %p, %p) init shadow bounds[%d] " | |
1019 | "because %p != %p\n", __func__, index, ptr, | |
1020 | ptr_val, index, value, ptr_val); | |
1021 | shadow_plb[index][0] = 0; | |
1022 | shadow_plb[index][1] = ~(unsigned long)0; | |
1023 | } else { | |
1024 | shadow_plb[index][0] = lower; | |
1025 | shadow_plb[index][1] = upper; | |
1026 | } | |
1027 | /* ptr ignored */ | |
1028 | } | |
1029 | ||
1030 | static __always_inline void mpx_test_helper0(uint8_t *buf, uint8_t *ptr) | |
1031 | { | |
1032 | mpx_make_bound_helper((unsigned long)ptr, 0x1800); | |
1033 | } | |
1034 | ||
1035 | static __always_inline void mpx_test_helper0_shadow(uint8_t *buf, uint8_t *ptr) | |
1036 | { | |
1037 | mkbnd_shadow(ptr, 0, 0x1800); | |
1038 | } | |
1039 | ||
1040 | static __always_inline void mpx_test_helper1(uint8_t *buf, uint8_t *ptr) | |
1041 | { | |
1042 | /* these are hard-coded to check bnd0 */ | |
1043 | expected_bnd_index = 0; | |
1044 | mpx_check_lowerbound_helper((unsigned long)(ptr-1)); | |
1045 | mpx_check_upperbound_helper((unsigned long)(ptr+0x1800)); | |
1046 | /* reset this since we do not expect any more bounds exceptions */ | |
1047 | expected_bnd_index = -1; | |
1048 | } | |
1049 | ||
1050 | static __always_inline void mpx_test_helper1_shadow(uint8_t *buf, uint8_t *ptr) | |
1051 | { | |
1052 | check_lowerbound_shadow(ptr-1, 0); | |
1053 | check_upperbound_shadow(ptr+0x1800, 0); | |
1054 | } | |
1055 | ||
1056 | static __always_inline void mpx_test_helper2(uint8_t *buf, uint8_t *ptr) | |
1057 | { | |
1058 | mpx_make_bound_helper((unsigned long)ptr, 0x1800); | |
1059 | mpx_movbndreg_helper(); | |
1060 | mpx_movbnd2mem_helper(buf); | |
1061 | mpx_make_bound_helper((unsigned long)(ptr+0x12), 0x1800); | |
1062 | } | |
1063 | ||
1064 | static __always_inline void mpx_test_helper2_shadow(uint8_t *buf, uint8_t *ptr) | |
1065 | { | |
1066 | mkbnd_shadow(ptr, 0, 0x1800); | |
1067 | movbndreg_shadow(0, 2); | |
1068 | movbnd2mem_shadow(0, (unsigned long *)buf); | |
1069 | mkbnd_shadow(ptr+0x12, 0, 0x1800); | |
1070 | } | |
1071 | ||
1072 | static __always_inline void mpx_test_helper3(uint8_t *buf, uint8_t *ptr) | |
1073 | { | |
1074 | mpx_movbnd_from_mem_helper(buf); | |
1075 | } | |
1076 | ||
1077 | static __always_inline void mpx_test_helper3_shadow(uint8_t *buf, uint8_t *ptr) | |
1078 | { | |
1079 | movbnd_from_mem_shadow((unsigned long *)buf, 0); | |
1080 | } | |
1081 | ||
1082 | static __always_inline void mpx_test_helper4(uint8_t *buf, uint8_t *ptr) | |
1083 | { | |
1084 | mpx_store_dsc_helper((unsigned long)buf, (unsigned long)ptr); | |
1085 | mpx_make_bound_helper((unsigned long)(ptr+0x12), 0x1800); | |
1086 | } | |
1087 | ||
1088 | static __always_inline void mpx_test_helper4_shadow(uint8_t *buf, uint8_t *ptr) | |
1089 | { | |
1090 | stdsc_shadow(0, buf, ptr); | |
1091 | mkbnd_shadow(ptr+0x12, 0, 0x1800); | |
1092 | } | |
1093 | ||
1094 | static __always_inline void mpx_test_helper5(uint8_t *buf, uint8_t *ptr) | |
1095 | { | |
1096 | mpx_load_dsc_helper((unsigned long)buf, (unsigned long)ptr); | |
1097 | } | |
1098 | ||
1099 | static __always_inline void mpx_test_helper5_shadow(uint8_t *buf, uint8_t *ptr) | |
1100 | { | |
1101 | lddsc_shadow(0, buf, ptr); | |
1102 | } | |
1103 | ||
1104 | #define NR_MPX_TEST_FUNCTIONS 6 | |
1105 | ||
1106 | /* | |
1107 | * For compatibility reasons, MPX will clear the bounds registers | |
1108 | * when you make function calls (among other things). We have to | |
1109 | * preserve the registers in between calls to the "helpers" since | |
1110 | * they build on each other. | |
1111 | * | |
1112 | * Be very careful not to make any function calls inside the | |
1113 | * helpers, or anywhere else beween the xrstor and xsave. | |
1114 | */ | |
1115 | #define run_helper(helper_nr, buf, buf_shadow, ptr) do { \ | |
1116 | xrstor_state(xsave_test_buf, flags); \ | |
1117 | mpx_test_helper##helper_nr(buf, ptr); \ | |
1118 | xsave_state(xsave_test_buf, flags); \ | |
1119 | mpx_test_helper##helper_nr##_shadow(buf_shadow, ptr); \ | |
1120 | } while (0) | |
1121 | ||
1122 | static void run_helpers(int nr, uint8_t *buf, uint8_t *buf_shadow, uint8_t *ptr) | |
1123 | { | |
1124 | uint64_t flags = 0x18; | |
1125 | ||
1126 | dprint_context(xsave_test_buf); | |
1127 | switch (nr) { | |
1128 | case 0: | |
1129 | run_helper(0, buf, buf_shadow, ptr); | |
1130 | break; | |
1131 | case 1: | |
1132 | run_helper(1, buf, buf_shadow, ptr); | |
1133 | break; | |
1134 | case 2: | |
1135 | run_helper(2, buf, buf_shadow, ptr); | |
1136 | break; | |
1137 | case 3: | |
1138 | run_helper(3, buf, buf_shadow, ptr); | |
1139 | break; | |
1140 | case 4: | |
1141 | run_helper(4, buf, buf_shadow, ptr); | |
1142 | break; | |
1143 | case 5: | |
1144 | run_helper(5, buf, buf_shadow, ptr); | |
1145 | break; | |
1146 | default: | |
1147 | test_failed(); | |
1148 | break; | |
1149 | } | |
1150 | dprint_context(xsave_test_buf); | |
1151 | } | |
1152 | ||
1153 | unsigned long buf_shadow[1024]; /* used to check load / store descriptors */ | |
1154 | extern long inspect_me(struct mpx_bounds_dir *bounds_dir); | |
1155 | ||
1156 | long cover_buf_with_bt_entries(void *buf, long buf_len) | |
1157 | { | |
1158 | int i; | |
1159 | long nr_to_fill; | |
1160 | int ratio = 1000; | |
1161 | unsigned long buf_len_in_ptrs; | |
1162 | ||
1163 | /* Fill about 1/100 of the space with bt entries */ | |
1164 | nr_to_fill = buf_len / (sizeof(unsigned long) * ratio); | |
1165 | ||
1166 | if (!nr_to_fill) | |
1167 | dprintf3("%s() nr_to_fill: %ld\n", __func__, nr_to_fill); | |
1168 | ||
1169 | /* Align the buffer to pointer size */ | |
1170 | while (((unsigned long)buf) % sizeof(void *)) { | |
1171 | buf++; | |
1172 | buf_len--; | |
1173 | } | |
1174 | /* We are storing pointers, so make */ | |
1175 | buf_len_in_ptrs = buf_len / sizeof(void *); | |
1176 | ||
1177 | for (i = 0; i < nr_to_fill; i++) { | |
1178 | long index = (mpx_random() % buf_len_in_ptrs); | |
1179 | void *ptr = buf + index * sizeof(unsigned long); | |
1180 | unsigned long ptr_addr = (unsigned long)ptr; | |
1181 | ||
1182 | /* ptr and size can be anything */ | |
1183 | mpx_make_bound_helper((unsigned long)ptr, 8); | |
1184 | ||
1185 | /* | |
1186 | * take bnd0 and put it in to bounds tables "buf + index" is an | |
1187 | * address inside the buffer where we are pretending that we | |
1188 | * are going to put a pointer We do not, though because we will | |
1189 | * never load entries from the table, so it doesn't matter. | |
1190 | */ | |
1191 | mpx_store_dsc_helper(ptr_addr, (unsigned long)ptr); | |
1192 | dprintf4("storing bound table entry for %lx (buf start @ %p)\n", | |
1193 | ptr_addr, buf); | |
1194 | } | |
1195 | return nr_to_fill; | |
1196 | } | |
1197 | ||
1198 | unsigned long align_down(unsigned long alignme, unsigned long align_to) | |
1199 | { | |
1200 | return alignme & ~(align_to-1); | |
1201 | } | |
1202 | ||
1203 | unsigned long align_up(unsigned long alignme, unsigned long align_to) | |
1204 | { | |
1205 | return (alignme + align_to - 1) & ~(align_to-1); | |
1206 | } | |
1207 | ||
1208 | /* | |
1209 | * Using 1MB alignment guarantees that each no allocation | |
1210 | * will overlap with another's bounds tables. | |
1211 | * | |
1212 | * We have to cook our own allocator here. malloc() can | |
1213 | * mix other allocation with ours which means that even | |
1214 | * if we free all of our allocations, there might still | |
1215 | * be bounds tables for the *areas* since there is other | |
1216 | * valid memory there. | |
1217 | * | |
1218 | * We also can't use malloc() because a free() of an area | |
1219 | * might not free it back to the kernel. We want it | |
1220 | * completely unmapped an malloc() does not guarantee | |
1221 | * that. | |
1222 | */ | |
1223 | #ifdef __i386__ | |
1224 | long alignment = 4096; | |
1225 | long sz_alignment = 4096; | |
1226 | #else | |
1227 | long alignment = 1 * MB; | |
1228 | long sz_alignment = 1 * MB; | |
1229 | #endif | |
1230 | void *mpx_mini_alloc(unsigned long sz) | |
1231 | { | |
1232 | unsigned long long tries = 0; | |
1233 | static void *last; | |
1234 | void *ptr; | |
1235 | void *try_at; | |
1236 | ||
1237 | sz = align_up(sz, sz_alignment); | |
1238 | ||
1239 | try_at = last + alignment; | |
1240 | while (1) { | |
1241 | ptr = mmap(try_at, sz, PROT_READ|PROT_WRITE, | |
1242 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
1243 | if (ptr == (void *)-1) | |
1244 | return NULL; | |
1245 | if (ptr == try_at) | |
1246 | break; | |
1247 | ||
1248 | munmap(ptr, sz); | |
1249 | try_at += alignment; | |
1250 | #ifdef __i386__ | |
1251 | /* | |
1252 | * This isn't quite correct for 32-bit binaries | |
1253 | * on 64-bit kernels since they can use the | |
1254 | * entire 32-bit address space, but it's close | |
1255 | * enough. | |
1256 | */ | |
1257 | if (try_at > (void *)0xC0000000) | |
1258 | #else | |
1259 | if (try_at > (void *)0x0000800000000000) | |
1260 | #endif | |
1261 | try_at = (void *)0x0; | |
1262 | if (!(++tries % 10000)) | |
1263 | dprintf1("stuck in %s(), tries: %lld\n", __func__, tries); | |
1264 | continue; | |
1265 | } | |
1266 | last = ptr; | |
1267 | dprintf3("mpx_mini_alloc(0x%lx) returning: %p\n", sz, ptr); | |
1268 | return ptr; | |
1269 | } | |
1270 | void mpx_mini_free(void *ptr, long sz) | |
1271 | { | |
1272 | dprintf2("%s() ptr: %p\n", __func__, ptr); | |
1273 | if ((unsigned long)ptr > 0x100000000000) { | |
1274 | dprintf1("uh oh !!!!!!!!!!!!!!! pointer too high: %p\n", ptr); | |
1275 | test_failed(); | |
1276 | } | |
1277 | sz = align_up(sz, sz_alignment); | |
1278 | dprintf3("%s() ptr: %p before munmap\n", __func__, ptr); | |
1279 | munmap(ptr, sz); | |
1280 | dprintf3("%s() ptr: %p DONE\n", __func__, ptr); | |
1281 | } | |
1282 | ||
1283 | #define NR_MALLOCS 100 | |
1284 | struct one_malloc { | |
1285 | char *ptr; | |
1286 | int nr_filled_btes; | |
1287 | unsigned long size; | |
1288 | }; | |
1289 | struct one_malloc mallocs[NR_MALLOCS]; | |
1290 | ||
1291 | void free_one_malloc(int index) | |
1292 | { | |
1293 | unsigned long free_ptr; | |
1294 | unsigned long mask; | |
1295 | ||
1296 | if (!mallocs[index].ptr) | |
1297 | return; | |
1298 | ||
1299 | mpx_mini_free(mallocs[index].ptr, mallocs[index].size); | |
1300 | dprintf4("freed[%d]: %p\n", index, mallocs[index].ptr); | |
1301 | ||
1302 | free_ptr = (unsigned long)mallocs[index].ptr; | |
1303 | mask = alignment-1; | |
1304 | dprintf4("lowerbits: %lx / %lx mask: %lx\n", free_ptr, | |
1305 | (free_ptr & mask), mask); | |
1306 | assert((free_ptr & mask) == 0); | |
1307 | ||
1308 | mallocs[index].ptr = NULL; | |
1309 | } | |
1310 | ||
1311 | #ifdef __i386__ | |
1312 | #define MPX_BOUNDS_TABLE_COVERS 4096 | |
1313 | #else | |
1314 | #define MPX_BOUNDS_TABLE_COVERS (1 * MB) | |
1315 | #endif | |
1316 | void zap_everything(void) | |
1317 | { | |
1318 | long after_zap; | |
1319 | long before_zap; | |
1320 | int i; | |
1321 | ||
1322 | before_zap = inspect_me(bounds_dir_ptr); | |
1323 | dprintf1("zapping everything start: %ld\n", before_zap); | |
1324 | for (i = 0; i < NR_MALLOCS; i++) | |
1325 | free_one_malloc(i); | |
1326 | ||
1327 | after_zap = inspect_me(bounds_dir_ptr); | |
1328 | dprintf1("zapping everything done: %ld\n", after_zap); | |
1329 | /* | |
1330 | * We only guarantee to empty the thing out if our allocations are | |
1331 | * exactly aligned on the boundaries of a boudns table. | |
1332 | */ | |
1333 | if ((alignment >= MPX_BOUNDS_TABLE_COVERS) && | |
1334 | (sz_alignment >= MPX_BOUNDS_TABLE_COVERS)) { | |
1335 | if (after_zap != 0) | |
1336 | test_failed(); | |
1337 | ||
1338 | assert(after_zap == 0); | |
1339 | } | |
1340 | } | |
1341 | ||
1342 | void do_one_malloc(void) | |
1343 | { | |
1344 | static int malloc_counter; | |
1345 | long sz; | |
1346 | int rand_index = (mpx_random() % NR_MALLOCS); | |
1347 | void *ptr = mallocs[rand_index].ptr; | |
1348 | ||
1349 | dprintf3("%s() enter\n", __func__); | |
1350 | ||
1351 | if (ptr) { | |
1352 | dprintf3("freeing one malloc at index: %d\n", rand_index); | |
1353 | free_one_malloc(rand_index); | |
1354 | if (mpx_random() % (NR_MALLOCS*3) == 3) { | |
1355 | int i; | |
1356 | dprintf3("zapping some more\n"); | |
1357 | for (i = rand_index; i < NR_MALLOCS; i++) | |
1358 | free_one_malloc(i); | |
1359 | } | |
1360 | if ((mpx_random() % zap_all_every_this_many_mallocs) == 4) | |
1361 | zap_everything(); | |
1362 | } | |
1363 | ||
1364 | /* 1->~1M */ | |
1365 | sz = (1 + mpx_random() % 1000) * 1000; | |
1366 | ptr = mpx_mini_alloc(sz); | |
1367 | if (!ptr) { | |
1368 | /* | |
1369 | * If we are failing allocations, just assume we | |
1370 | * are out of memory and zap everything. | |
1371 | */ | |
1372 | dprintf3("zapping everything because out of memory\n"); | |
1373 | zap_everything(); | |
1374 | goto out; | |
1375 | } | |
1376 | ||
1377 | dprintf3("malloc: %p size: 0x%lx\n", ptr, sz); | |
1378 | mallocs[rand_index].nr_filled_btes = cover_buf_with_bt_entries(ptr, sz); | |
1379 | mallocs[rand_index].ptr = ptr; | |
1380 | mallocs[rand_index].size = sz; | |
1381 | out: | |
1382 | if ((++malloc_counter) % inspect_every_this_many_mallocs == 0) | |
1383 | inspect_me(bounds_dir_ptr); | |
1384 | } | |
1385 | ||
1386 | void run_timed_test(void (*test_func)(void)) | |
1387 | { | |
1388 | int done = 0; | |
1389 | long iteration = 0; | |
1390 | static time_t last_print; | |
1391 | time_t now; | |
1392 | time_t start; | |
1393 | ||
1394 | time(&start); | |
1395 | while (!done) { | |
1396 | time(&now); | |
1397 | if ((now - start) > TEST_DURATION_SECS) | |
1398 | done = 1; | |
1399 | ||
1400 | test_func(); | |
1401 | iteration++; | |
1402 | ||
1403 | if ((now - last_print > 1) || done) { | |
1404 | printf("iteration %ld complete, OK so far\n", iteration); | |
1405 | last_print = now; | |
1406 | } | |
1407 | } | |
1408 | } | |
1409 | ||
1410 | void check_bounds_table_frees(void) | |
1411 | { | |
1412 | printf("executing unmaptest\n"); | |
1413 | inspect_me(bounds_dir_ptr); | |
1414 | run_timed_test(&do_one_malloc); | |
1415 | printf("done with malloc() fun\n"); | |
1416 | } | |
1417 | ||
1418 | void insn_test_failed(int test_nr, int test_round, void *buf, | |
1419 | void *buf_shadow, void *ptr) | |
1420 | { | |
1421 | print_context(xsave_test_buf); | |
1422 | eprintf("ERROR: test %d round %d failed\n", test_nr, test_round); | |
1423 | while (test_nr == 5) { | |
1424 | struct mpx_bt_entry *bte; | |
1425 | struct mpx_bounds_dir *bd = (void *)bounds_dir_ptr; | |
1426 | struct mpx_bd_entry *bde = mpx_vaddr_to_bd_entry(buf, bd); | |
1427 | ||
1428 | printf(" bd: %p\n", bd); | |
1429 | printf("&bde: %p\n", bde); | |
1430 | printf("*bde: %lx\n", *(unsigned long *)bde); | |
1431 | if (!bd_entry_valid(bde)) | |
1432 | break; | |
1433 | ||
1434 | bte = mpx_vaddr_to_bt_entry(buf, bd); | |
1435 | printf(" te: %p\n", bte); | |
1436 | printf("bte[0]: %lx\n", bte->contents[0]); | |
1437 | printf("bte[1]: %lx\n", bte->contents[1]); | |
1438 | printf("bte[2]: %lx\n", bte->contents[2]); | |
1439 | printf("bte[3]: %lx\n", bte->contents[3]); | |
1440 | break; | |
1441 | } | |
1442 | test_failed(); | |
1443 | } | |
1444 | ||
1445 | void check_mpx_insns_and_tables(void) | |
1446 | { | |
1447 | int successes = 0; | |
1448 | int failures = 0; | |
1449 | int buf_size = (1024*1024); | |
1450 | unsigned long *buf = malloc(buf_size); | |
1451 | const int total_nr_tests = NR_MPX_TEST_FUNCTIONS * TEST_ROUNDS; | |
1452 | int i, j; | |
1453 | ||
1454 | memset(buf, 0, buf_size); | |
1455 | memset(buf_shadow, 0, sizeof(buf_shadow)); | |
1456 | ||
1457 | for (i = 0; i < TEST_ROUNDS; i++) { | |
1458 | uint8_t *ptr = get_random_addr() + 8; | |
1459 | ||
1460 | for (j = 0; j < NR_MPX_TEST_FUNCTIONS; j++) { | |
1461 | if (0 && j != 5) { | |
1462 | successes++; | |
1463 | continue; | |
1464 | } | |
1465 | dprintf2("starting test %d round %d\n", j, i); | |
1466 | dprint_context(xsave_test_buf); | |
1467 | /* | |
1468 | * test5 loads an address from the bounds tables. | |
1469 | * The load will only complete if 'ptr' matches | |
1470 | * the load and the store, so with random addrs, | |
1471 | * the odds of this are very small. Make it | |
1472 | * higher by only moving 'ptr' 1/10 times. | |
1473 | */ | |
1474 | if (random() % 10 <= 0) | |
1475 | ptr = get_random_addr() + 8; | |
1476 | dprintf3("random ptr{%p}\n", ptr); | |
1477 | dprint_context(xsave_test_buf); | |
1478 | run_helpers(j, (void *)buf, (void *)buf_shadow, ptr); | |
1479 | dprint_context(xsave_test_buf); | |
1480 | if (!compare_context(xsave_test_buf)) { | |
1481 | insn_test_failed(j, i, buf, buf_shadow, ptr); | |
1482 | failures++; | |
1483 | goto exit; | |
1484 | } | |
1485 | successes++; | |
1486 | dprint_context(xsave_test_buf); | |
1487 | dprintf2("finished test %d round %d\n", j, i); | |
1488 | dprintf3("\n"); | |
1489 | dprint_context(xsave_test_buf); | |
1490 | } | |
1491 | } | |
1492 | ||
1493 | exit: | |
1494 | dprintf2("\nabout to free:\n"); | |
1495 | free(buf); | |
1496 | dprintf1("successes: %d\n", successes); | |
1497 | dprintf1(" failures: %d\n", failures); | |
1498 | dprintf1(" tests: %d\n", total_nr_tests); | |
1499 | dprintf1(" expected: %jd #BRs\n", num_upper_brs + num_lower_brs); | |
1500 | dprintf1(" saw: %d #BRs\n", br_count); | |
1501 | if (failures) { | |
1502 | eprintf("ERROR: non-zero number of failures\n"); | |
1503 | exit(20); | |
1504 | } | |
1505 | if (successes != total_nr_tests) { | |
1506 | eprintf("ERROR: succeded fewer than number of tries (%d != %d)\n", | |
1507 | successes, total_nr_tests); | |
1508 | exit(21); | |
1509 | } | |
1510 | if (num_upper_brs + num_lower_brs != br_count) { | |
1511 | eprintf("ERROR: unexpected number of #BRs: %jd %jd %d\n", | |
1512 | num_upper_brs, num_lower_brs, br_count); | |
1513 | eprintf("successes: %d\n", successes); | |
1514 | eprintf(" failures: %d\n", failures); | |
1515 | eprintf(" tests: %d\n", total_nr_tests); | |
1516 | eprintf(" expected: %jd #BRs\n", num_upper_brs + num_lower_brs); | |
1517 | eprintf(" saw: %d #BRs\n", br_count); | |
1518 | exit(22); | |
1519 | } | |
1520 | } | |
1521 | ||
1522 | /* | |
1523 | * This is supposed to SIGSEGV nicely once the kernel | |
1524 | * can no longer allocate vaddr space. | |
1525 | */ | |
1526 | void exhaust_vaddr_space(void) | |
1527 | { | |
1528 | unsigned long ptr; | |
1529 | /* Try to make sure there is no room for a bounds table anywhere */ | |
1530 | unsigned long skip = MPX_BOUNDS_TABLE_SIZE_BYTES - PAGE_SIZE; | |
1531 | #ifdef __i386__ | |
1532 | unsigned long max_vaddr = 0xf7788000UL; | |
1533 | #else | |
1534 | unsigned long max_vaddr = 0x800000000000UL; | |
1535 | #endif | |
1536 | ||
1537 | dprintf1("%s() start\n", __func__); | |
1538 | /* do not start at 0, we aren't allowed to map there */ | |
1539 | for (ptr = PAGE_SIZE; ptr < max_vaddr; ptr += skip) { | |
1540 | void *ptr_ret; | |
1541 | int ret = madvise((void *)ptr, PAGE_SIZE, MADV_NORMAL); | |
1542 | ||
1543 | if (!ret) { | |
1544 | dprintf1("madvise() %lx ret: %d\n", ptr, ret); | |
1545 | continue; | |
1546 | } | |
1547 | ptr_ret = mmap((void *)ptr, PAGE_SIZE, PROT_READ|PROT_WRITE, | |
1548 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
1549 | if (ptr_ret != (void *)ptr) { | |
1550 | perror("mmap"); | |
1551 | dprintf1("mmap(%lx) ret: %p\n", ptr, ptr_ret); | |
1552 | break; | |
1553 | } | |
1554 | if (!(ptr & 0xffffff)) | |
1555 | dprintf1("mmap(%lx) ret: %p\n", ptr, ptr_ret); | |
1556 | } | |
1557 | for (ptr = PAGE_SIZE; ptr < max_vaddr; ptr += skip) { | |
1558 | dprintf2("covering 0x%lx with bounds table entries\n", ptr); | |
1559 | cover_buf_with_bt_entries((void *)ptr, PAGE_SIZE); | |
1560 | } | |
1561 | dprintf1("%s() end\n", __func__); | |
1562 | printf("done with vaddr space fun\n"); | |
1563 | } | |
1564 | ||
1565 | void mpx_table_test(void) | |
1566 | { | |
1567 | printf("starting mpx bounds table test\n"); | |
1568 | run_timed_test(check_mpx_insns_and_tables); | |
1569 | printf("done with mpx bounds table test\n"); | |
1570 | } | |
1571 | ||
1572 | int main(int argc, char **argv) | |
1573 | { | |
1574 | int unmaptest = 0; | |
1575 | int vaddrexhaust = 0; | |
1576 | int tabletest = 0; | |
1577 | int i; | |
1578 | ||
1579 | check_mpx_support(); | |
1580 | mpx_prepare(); | |
1581 | srandom(11179); | |
1582 | ||
1583 | bd_incore(); | |
1584 | init(); | |
1585 | bd_incore(); | |
1586 | ||
1587 | trace_me(); | |
1588 | ||
1589 | xsave_state((void *)xsave_test_buf, 0x1f); | |
1590 | if (!compare_context(xsave_test_buf)) | |
1591 | printf("Init failed\n"); | |
1592 | ||
1593 | for (i = 1; i < argc; i++) { | |
1594 | if (!strcmp(argv[i], "unmaptest")) | |
1595 | unmaptest = 1; | |
1596 | if (!strcmp(argv[i], "vaddrexhaust")) | |
1597 | vaddrexhaust = 1; | |
1598 | if (!strcmp(argv[i], "tabletest")) | |
1599 | tabletest = 1; | |
1600 | } | |
1601 | if (!(unmaptest || vaddrexhaust || tabletest)) { | |
1602 | unmaptest = 1; | |
1603 | /* vaddrexhaust = 1; */ | |
1604 | tabletest = 1; | |
1605 | } | |
1606 | if (unmaptest) | |
1607 | check_bounds_table_frees(); | |
1608 | if (tabletest) | |
1609 | mpx_table_test(); | |
1610 | if (vaddrexhaust) | |
1611 | exhaust_vaddr_space(); | |
1612 | printf("%s completed successfully\n", argv[0]); | |
1613 | exit(0); | |
1614 | } | |
1615 | ||
1616 | #include "mpx-dig.c" |