111
|
1 /* mpxrt.c -*-C++-*-
|
|
2 *
|
|
3 *************************************************************************
|
|
4 *
|
|
5 * @copyright
|
|
6 * Copyright (C) 2014, Intel Corporation
|
|
7 * All rights reserved.
|
|
8 *
|
|
9 * @copyright
|
|
10 * Redistribution and use in source and binary forms, with or without
|
|
11 * modification, are permitted provided that the following conditions
|
|
12 * are met:
|
|
13 *
|
|
14 * * Redistributions of source code must retain the above copyright
|
|
15 * notice, this list of conditions and the following disclaimer.
|
|
16 * * Redistributions in binary form must reproduce the above copyright
|
|
17 * notice, this list of conditions and the following disclaimer in
|
|
18 * the documentation and/or other materials provided with the
|
|
19 * distribution.
|
|
20 * * Neither the name of Intel Corporation nor the names of its
|
|
21 * contributors may be used to endorse or promote products derived
|
|
22 * from this software without specific prior written permission.
|
|
23 *
|
|
24 * @copyright
|
|
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
29 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
32 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
35 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
36 * POSSIBILITY OF SUCH DAMAGE.
|
|
37 *
|
|
38 **************************************************************************/
|
|
39
|
|
40 #define __STDC_FORMAT_MACROS
|
|
41 #include "config.h"
|
|
42 #include <inttypes.h>
|
|
43 #include <stdio.h>
|
|
44 #include <string.h>
|
|
45 #include <stdint.h>
|
|
46 #include <stdbool.h>
|
|
47 #include <signal.h>
|
|
48 #include <assert.h>
|
|
49 #include <stdlib.h>
|
|
50 #include <sys/mman.h>
|
|
51 #include <sys/prctl.h>
|
|
52 #include <cpuid.h>
|
|
53 #include "mpxrt-utils.h"
|
|
54 #include "mpxrt.h"
|
|
55
|
|
56 #define MPX_ENABLE_BIT_NO 0
|
|
57 #define BNDPRESERVE_BIT_NO 1
|
|
58
|
|
59 struct xsave_hdr_struct
|
|
60 {
|
|
61 uint64_t xstate_bv;
|
|
62 uint64_t reserved1[2];
|
|
63 uint64_t reserved2[5];
|
|
64 } __attribute__ ((packed));
|
|
65
|
|
66 struct bndregs_struct
|
|
67 {
|
|
68 uint64_t bndregs[8];
|
|
69 } __attribute__ ((packed));
|
|
70
|
|
71 struct bndcsr_struct {
|
|
72 uint64_t cfg_reg_u;
|
|
73 uint64_t status_reg;
|
|
74 } __attribute__((packed));
|
|
75
|
|
76 struct xsave_struct
|
|
77 {
|
|
78 uint8_t fpu_sse[512];
|
|
79 struct xsave_hdr_struct xsave_hdr;
|
|
80 uint8_t ymm[256];
|
|
81 uint8_t lwp[128];
|
|
82 struct bndregs_struct bndregs;
|
|
83 struct bndcsr_struct bndcsr;
|
|
84 } __attribute__ ((packed));
|
|
85
|
|
86 /* Following vars are initialized at process startup only
|
|
87 and thus are considered to be thread safe. */
|
|
88 static void *l1base = NULL;
|
|
89 static int bndpreserve;
|
|
90 static int enable = 1;
|
|
91
|
|
92 /* Var holding number of occured BRs. It is modified from
|
|
93 signal handler only and thus it should be thread safe. */
|
|
94 static uint64_t num_bnd_chk = 0;
|
|
95
|
|
96 static inline void
|
|
97 xrstor_state (struct xsave_struct *fx, uint64_t mask)
|
|
98 {
|
|
99 uint32_t lmask = mask;
|
|
100 uint32_t hmask = mask >> 32;
|
|
101
|
|
102 asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
|
|
103 : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
|
|
104 : "memory");
|
|
105 }
|
|
106
|
|
107 static inline void
|
|
108 xsave_state (struct xsave_struct *fx, uint64_t mask)
|
|
109 {
|
|
110 uint32_t lmask = mask;
|
|
111 uint32_t hmask = mask >> 32;
|
|
112
|
|
113 asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
|
|
114 : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
|
|
115 : "memory");
|
|
116 }
|
|
117
|
|
118 static inline uint64_t
|
|
119 xgetbv (uint32_t index)
|
|
120 {
|
|
121 uint32_t eax, edx;
|
|
122
|
|
123 asm volatile (".byte 0x0f,0x01,0xd0" /* xgetbv */
|
|
124 : "=a" (eax), "=d" (edx)
|
|
125 : "c" (index));
|
|
126 return eax + ((uint64_t)edx << 32);
|
|
127 }
|
|
128
|
|
129 static uint64_t
|
|
130 read_mpx_status_sig (ucontext_t *uctxt)
|
|
131 {
|
|
132 uint8_t *regs = (uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM;
|
|
133 struct xsave_struct *xsave_buf = (struct xsave_struct *)regs;
|
|
134 return xsave_buf->bndcsr.status_reg;
|
|
135 }
|
|
136
|
|
137 static uint8_t *
|
|
138 get_next_inst_ip (uint8_t *addr)
|
|
139 {
|
|
140 uint8_t *ip = addr;
|
|
141 uint8_t sib;
|
|
142
|
|
143 /* Determine the prefix. */
|
|
144 switch (*ip)
|
|
145 {
|
|
146 case 0xf2:
|
|
147 case 0xf3:
|
|
148 case 0x66:
|
|
149 ip++;
|
|
150 break;
|
|
151 }
|
|
152
|
|
153 /* Look for rex prefix. */
|
|
154 if ((*ip & 0x40) == 0x40)
|
|
155 ip++;
|
|
156
|
|
157 /* Make sure we have a MPX instruction. */
|
|
158 if (*ip++ != 0x0f)
|
|
159 return addr;
|
|
160
|
|
161 /* Skip the op code byte. */
|
|
162 ip++;
|
|
163
|
|
164 /* Get the moderm byte. */
|
|
165 uint8_t modrm = *ip++;
|
|
166
|
|
167 /* Break it down into parts. */
|
|
168 uint8_t rm = modrm & 7;
|
|
169 uint8_t mod = (modrm >> 6);
|
|
170
|
|
171 /* Init the parts of the address mode. */
|
|
172 uint8_t base = 8;
|
|
173
|
|
174 /* Is it a mem mode? */
|
|
175 if (mod != 3)
|
|
176 {
|
|
177 /* Look for scaled indexed addressing. */
|
|
178 if (rm == 4)
|
|
179 {
|
|
180 /* SIB addressing. */
|
|
181 sib = *ip++;
|
|
182 base = sib & 7;
|
|
183 switch (mod)
|
|
184 {
|
|
185 case 0:
|
|
186 if (base == 5)
|
|
187 ip += 4;
|
|
188 break;
|
|
189
|
|
190 case 1:
|
|
191 ip++;
|
|
192 break;
|
|
193
|
|
194 case 2:
|
|
195 ip += 4;
|
|
196 break;
|
|
197 }
|
|
198 }
|
|
199 else
|
|
200 {
|
|
201 /* MODRM addressing. */
|
|
202 switch (mod)
|
|
203 {
|
|
204 case 0:
|
|
205 if (rm == 5)
|
|
206 /* DISP32 addressing, no base. */
|
|
207 ip += 4;
|
|
208 break;
|
|
209
|
|
210 case 1:
|
|
211 ip++;
|
|
212 break;
|
|
213
|
|
214 case 2:
|
|
215 ip += 4;
|
|
216 break;
|
|
217 }
|
|
218 }
|
|
219 }
|
|
220 return ip;
|
|
221 }
|
|
222
|
|
223 static void
|
|
224 handler (int sig __attribute__ ((unused)),
|
|
225 siginfo_t *info __attribute__ ((unused)),
|
|
226 void *vucontext,
|
|
227 struct xsave_struct *buf __attribute__ ((unused)))
|
|
228 {
|
|
229 ucontext_t* uctxt;
|
|
230 greg_t trapno;
|
|
231 greg_t ip;
|
|
232
|
|
233 uctxt = vucontext;
|
|
234 trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO];
|
|
235 ip = uctxt->uc_mcontext.gregs[REG_IP_IDX];
|
|
236
|
|
237 if (trapno == 5)
|
|
238 {
|
|
239 uint64_t status = read_mpx_status_sig (uctxt);
|
|
240 uint64_t br_reason = status & 0x3;
|
|
241
|
|
242 __mpxrt_write (VERB_BR, "Saw a #BR! status ");
|
|
243 __mpxrt_write_uint (VERB_BR, status, 10);
|
|
244 __mpxrt_write (VERB_BR, " at 0x");
|
|
245 __mpxrt_write_uint (VERB_BR, ip, 16);
|
|
246 __mpxrt_write (VERB_BR, "\n");
|
|
247
|
|
248 switch (br_reason)
|
|
249 {
|
|
250 case 1: /* traditional BR */
|
|
251 num_bnd_chk++;
|
|
252 uctxt->uc_mcontext.gregs[REG_IP_IDX] =
|
|
253 (greg_t)get_next_inst_ip ((uint8_t *)ip);
|
|
254 if (__mpxrt_mode () == MPX_RT_STOP)
|
|
255 __mpxrt_stop ();
|
|
256 return;
|
|
257
|
|
258 default:
|
|
259 __mpxrt_write (VERB_BR, "Unexpected status with bound exception: ");
|
|
260 __mpxrt_write_uint (VERB_BR, status, 10);
|
|
261 __mpxrt_write (VERB_BR, "\n");
|
|
262 break;
|
|
263 }
|
|
264 }
|
|
265 else if (trapno == 14)
|
|
266 {
|
|
267 __mpxrt_write (VERB_ERROR, "In signal handler, trapno = ");
|
|
268 __mpxrt_write_uint (VERB_ERROR, trapno, 10);
|
|
269 __mpxrt_write (VERB_ERROR, ", ip = 0x");
|
|
270 __mpxrt_write_uint (VERB_ERROR, ip, 16);
|
|
271 __mpxrt_write (VERB_ERROR, "\n");
|
|
272 __mpxrt_stop ();
|
|
273 }
|
|
274 else
|
|
275 {
|
|
276 __mpxrt_write (VERB_ERROR, "Unexpected trap ");
|
|
277 __mpxrt_write_uint (VERB_ERROR, trapno, 10);
|
|
278 __mpxrt_write (VERB_ERROR, "! at 0x");
|
|
279 __mpxrt_write_uint (VERB_ERROR, ip, 16);
|
|
280 __mpxrt_write (VERB_ERROR, "\n");
|
|
281 __mpxrt_stop ();
|
|
282 }
|
|
283 }
|
|
284
|
|
285 /* Using wrapper to the real handler in order to save the bnd regs
|
|
286 using xsave before any unprefixed call. an unprefixed call to
|
|
287 __i686.get_pc_thunk.bx is added by the linker in 32bit at the
|
|
288 beginning of handler function since there are references to
|
|
289 global variables. */
|
|
290 static void
|
|
291 handler_wrap (int signum, siginfo_t* si, void* vucontext)
|
|
292 {
|
|
293 /* Since the OS currently not handling chkptr regs.
|
|
294 We need to store them for later use. They might be
|
|
295 init due to unprefixed call,Jcc,ret. avoiding calling
|
|
296 function since the function will be unprefixed as well. */
|
|
297 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
298 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
299 uint64_t mask = 0x18;
|
|
300 uint32_t lmask = mask;
|
|
301 uint32_t hmask = mask >> 32;
|
|
302
|
|
303 asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
|
|
304 : : "D" (xsave_buf), "m" (*xsave_buf),
|
|
305 "a" (lmask), "d" (hmask)
|
|
306 : "memory");
|
|
307
|
|
308 handler (signum, si, vucontext, xsave_buf);
|
|
309 }
|
|
310
|
|
311 static bool
|
|
312 check_mpx_support (void)
|
|
313 {
|
|
314 unsigned int eax, ebx, ecx, edx;
|
|
315 unsigned int max_level = __get_cpuid_max (0, NULL);
|
|
316
|
|
317 if (max_level < 13)
|
|
318 {
|
|
319 __mpxrt_print (VERB_DEBUG, "No required CPUID level support.\n");
|
|
320 return false;
|
|
321 }
|
|
322
|
|
323 __cpuid_count (0, 0, eax, ebx, ecx, edx);
|
|
324 if (!(ecx & bit_XSAVE))
|
|
325 {
|
|
326 __mpxrt_print (VERB_DEBUG, "No XSAVE support.\n");
|
|
327 return false;
|
|
328 }
|
|
329
|
|
330 if (!(ecx & bit_OSXSAVE))
|
|
331 {
|
|
332 __mpxrt_print (VERB_DEBUG, "No OSXSAVE support.\n");
|
|
333 return false;
|
|
334 }
|
|
335
|
|
336 __cpuid_count (7, 0, eax, ebx, ecx, edx);
|
|
337 if (!(ebx & bit_MPX))
|
|
338 {
|
|
339 __mpxrt_print (VERB_DEBUG, "No MPX support.\n");
|
|
340 return false;
|
|
341 }
|
|
342
|
|
343 __cpuid_count (13, 0, eax, ebx, ecx, edx);
|
|
344 if (!(eax & bit_BNDREGS))
|
|
345 {
|
|
346 __mpxrt_print (VERB_DEBUG, "No BNDREGS support.\n");
|
|
347 return false;
|
|
348 }
|
|
349
|
|
350 if (!(eax & bit_BNDCSR))
|
|
351 {
|
|
352 __mpxrt_print (VERB_DEBUG, "No BNDCSR support.\n");
|
|
353 return false;
|
|
354 }
|
|
355
|
|
356 return true;
|
|
357 }
|
|
358
|
|
359 static void
|
|
360 enable_mpx (void)
|
|
361 {
|
|
362 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
363 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
364
|
|
365 memset (buffer, 0, sizeof (buffer));
|
|
366 xrstor_state (xsave_buf, 0x18);
|
|
367
|
|
368 __mpxrt_print (VERB_DEBUG, "Initalizing MPX...\n");
|
|
369 __mpxrt_print (VERB_DEBUG, " Enable bit: %d\n", enable);
|
|
370 __mpxrt_print (VERB_DEBUG, " BNDPRESERVE bit: %d\n", bndpreserve);
|
|
371
|
|
372 /* Enable MPX. */
|
|
373 xsave_buf->xsave_hdr.xstate_bv = 0x10;
|
|
374 xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base;
|
|
375 xsave_buf->bndcsr.cfg_reg_u |= enable << MPX_ENABLE_BIT_NO;
|
|
376 xsave_buf->bndcsr.cfg_reg_u |= bndpreserve << BNDPRESERVE_BIT_NO;
|
|
377 xsave_buf->bndcsr.status_reg = 0;
|
|
378
|
|
379 xrstor_state (xsave_buf, 0x10);
|
|
380 }
|
|
381
|
|
382 static void
|
|
383 disable_mpx (void)
|
|
384 {
|
|
385 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
386 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
387
|
|
388 memset(buffer, 0, sizeof(buffer));
|
|
389 xrstor_state(xsave_buf, 0x18);
|
|
390
|
|
391 /* Disable MPX. */
|
|
392 xsave_buf->xsave_hdr.xstate_bv = 0x10;
|
|
393 xsave_buf->bndcsr.cfg_reg_u = 0;
|
|
394 xsave_buf->bndcsr.status_reg = 0;
|
|
395
|
|
396 xrstor_state(xsave_buf, 0x10);
|
|
397 }
|
|
398
|
|
399 static bool
|
|
400 process_specific_init (void)
|
|
401 {
|
|
402 if (!check_mpx_support ())
|
|
403 return false;
|
|
404
|
|
405 l1base = mmap (NULL, MPX_L1_SIZE, PROT_READ | PROT_WRITE,
|
|
406 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
407 if (l1base == MAP_FAILED)
|
|
408 {
|
|
409 perror ("mmap");
|
|
410 exit (EXIT_FAILURE);
|
|
411 }
|
|
412
|
|
413 enable_mpx ();
|
|
414
|
|
415 if (prctl (43, 0, 0, 0, 0))
|
|
416 {
|
|
417 __mpxrt_print (VERB_ERROR, "No MPX support\n");
|
|
418 disable_mpx ();
|
|
419 return false;
|
|
420 }
|
|
421
|
|
422 return true;
|
|
423 }
|
|
424
|
|
425 static bool
|
|
426 process_specific_finish (void)
|
|
427 {
|
|
428 if (!check_mpx_support ())
|
|
429 return false;
|
|
430
|
|
431 if (prctl (44, 0, 0, 0, 0))
|
|
432 {
|
|
433 __mpxrt_print (VERB_ERROR, "No MPX support\n");
|
|
434 return false;
|
|
435 }
|
|
436
|
|
437 munmap (l1base, MPX_L1_SIZE);
|
|
438
|
|
439 return true;
|
|
440 }
|
|
441
|
|
442 static void
|
|
443 setup_handler (void)
|
|
444 {
|
|
445 int r,rs;
|
|
446 struct sigaction newact;
|
|
447
|
|
448 /* #BR is mapped to sigsegv */
|
|
449 int signum = SIGSEGV;
|
|
450
|
|
451 newact.sa_handler = 0;
|
|
452 newact.sa_sigaction = handler_wrap;
|
|
453
|
|
454 /* sigset_t - signals to block while in the handler
|
|
455 get the old signal mask. */
|
|
456 rs = sigprocmask (SIG_SETMASK, 0, &newact.sa_mask);
|
|
457 assert (rs == 0);
|
|
458
|
|
459 /* Call sa_sigaction, not sa_handler. */
|
|
460 newact.sa_flags = SA_SIGINFO;
|
|
461 /* In case we call user's handler on SIGSEGV (not bound
|
|
462 violation exception) we want to allow bound checking
|
|
463 inside the user handler -> nested exception. */
|
|
464 newact.sa_flags |= SA_NODEFER;
|
|
465
|
|
466 newact.sa_restorer = 0;
|
|
467 r = sigaction (signum, &newact, 0);
|
|
468 assert (r == 0);
|
|
469 }
|
|
470
|
|
471 /* Set constructor priority to two to make it run after the
|
|
472 constructor in sigaction.c. */
|
|
473 static void __attribute__ ((constructor (1005)))
|
|
474 mpxrt_prepare (void)
|
|
475 {
|
|
476 __mpxrt_init_env_vars (&bndpreserve);
|
|
477 setup_handler ();
|
|
478 process_specific_init ();
|
|
479 }
|
|
480
|
|
481 static void __attribute__ ((destructor))
|
|
482 mpxrt_cleanup (void)
|
|
483 {
|
|
484 __mpxrt_print_summary (num_bnd_chk, MPX_L1_SIZE);
|
|
485 __mpxrt_utils_free ();
|
|
486 process_specific_finish ();
|
|
487 }
|
|
488
|
|
489 /* Get address of bounds directory. */
|
|
490 void *
|
|
491 get_bd ()
|
|
492 {
|
|
493 return l1base;
|
|
494 }
|