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