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
xrstor_state(struct xsave_struct * fx,uint64_t mask)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
xsave_state(struct xsave_struct * fx,uint64_t mask)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
xgetbv(uint32_t index)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
read_mpx_status_sig(ucontext_t * uctxt)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 *
get_next_inst_ip(uint8_t * addr)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
handler(int sig,siginfo_t * info,void * vucontext,struct xsave_struct * buf)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
handler_wrap(int signum,siginfo_t * si,void * vucontext)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
check_mpx_support(void)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
enable_mpx(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
disable_mpx(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
process_specific_init(void)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
process_specific_finish(void)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
setup_handler(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)))
mpxrt_prepare(void)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))
mpxrt_cleanup(void)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 *
get_bd()491 get_bd ()
492 {
493 return l1base;
494 }
495