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 55#ifdef __i386__ 56 57/* i386 directory size is 4MB */ 58#define NUM_L1_BITS 20 59 60#define REG_IP_IDX REG_EIP 61#define REX_PREFIX 62 63#define XSAVE_OFFSET_IN_FPMEM sizeof (struct _libc_fpstate) 64 65#else /* __i386__ */ 66 67/* x86_64 directory size is 2GB */ 68#define NUM_L1_BITS 28 69 70#define REG_IP_IDX REG_RIP 71#define REX_PREFIX "0x48, " 72 73#define XSAVE_OFFSET_IN_FPMEM 0 74 75#endif /* !__i386__ */ 76 77#define MPX_ENABLE_BIT_NO 0 78#define BNDPRESERVE_BIT_NO 1 79 80const size_t MPX_L1_SIZE = (1UL << NUM_L1_BITS) * sizeof (void *); 81 82struct xsave_hdr_struct 83{ 84 uint64_t xstate_bv; 85 uint64_t reserved1[2]; 86 uint64_t reserved2[5]; 87} __attribute__ ((packed)); 88 89struct bndregs_struct 90{ 91 uint64_t bndregs[8]; 92} __attribute__ ((packed)); 93 94struct bndcsr_struct { 95 uint64_t cfg_reg_u; 96 uint64_t status_reg; 97} __attribute__((packed)); 98 99struct xsave_struct 100{ 101 uint8_t fpu_sse[512]; 102 struct xsave_hdr_struct xsave_hdr; 103 uint8_t ymm[256]; 104 uint8_t lwp[128]; 105 struct bndregs_struct bndregs; 106 struct bndcsr_struct bndcsr; 107} __attribute__ ((packed)); 108 109/* Following vars are initialized at process startup only 110 and thus are considered to be thread safe. */ 111static void *l1base = NULL; 112static int bndpreserve; 113static int enable = 1; 114 115/* Var holding number of occured BRs. It is modified from 116 signal handler only and thus it should be thread safe. */ 117static uint64_t num_bnd_chk = 0; 118 119static inline void 120xrstor_state (struct xsave_struct *fx, uint64_t mask) 121{ 122 uint32_t lmask = mask; 123 uint32_t hmask = mask >> 32; 124 125 asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t" 126 : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) 127 : "memory"); 128} 129 130static inline void 131xsave_state (struct xsave_struct *fx, uint64_t mask) 132{ 133 uint32_t lmask = mask; 134 uint32_t hmask = mask >> 32; 135 136 asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t" 137 : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) 138 : "memory"); 139} 140 141static inline uint64_t 142xgetbv (uint32_t index) 143{ 144 uint32_t eax, edx; 145 146 asm volatile (".byte 0x0f,0x01,0xd0" /* xgetbv */ 147 : "=a" (eax), "=d" (edx) 148 : "c" (index)); 149 return eax + ((uint64_t)edx << 32); 150} 151 152static uint64_t 153read_mpx_status_sig (ucontext_t *uctxt) 154{ 155 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096]; 156 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer; 157 158 memset (buffer, 0, sizeof (buffer)); 159 memcpy (buffer, 160 (uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM, 161 sizeof (struct xsave_struct)); 162 return xsave_buf->bndcsr.status_reg; 163} 164 165static uint8_t * 166get_next_inst_ip (uint8_t *addr) 167{ 168 uint8_t *ip = addr; 169 uint8_t sib; 170 171 /* Determine the prefix. */ 172 switch (*ip) 173 { 174 case 0xf2: 175 case 0xf3: 176 case 0x66: 177 ip++; 178 break; 179 } 180 181 /* Look for rex prefix. */ 182 if ((*ip & 0x40) == 0x40) 183 ip++; 184 185 /* Make sure we have a MPX instruction. */ 186 if (*ip++ != 0x0f) 187 return addr; 188 189 /* Skip the op code byte. */ 190 ip++; 191 192 /* Get the moderm byte. */ 193 uint8_t modrm = *ip++; 194 195 /* Break it down into parts. */ 196 uint8_t rm = modrm & 7; 197 uint8_t mod = (modrm >> 6); 198 199 /* Init the parts of the address mode. */ 200 uint8_t base = 8; 201 202 /* Is it a mem mode? */ 203 if (mod != 3) 204 { 205 /* Look for scaled indexed addressing. */ 206 if (rm == 4) 207 { 208 /* SIB addressing. */ 209 sib = *ip++; 210 base = sib & 7; 211 switch (mod) 212 { 213 case 0: 214 if (base == 5) 215 ip += 4; 216 break; 217 218 case 1: 219 ip++; 220 break; 221 222 case 2: 223 ip += 4; 224 break; 225 } 226 } 227 else 228 { 229 /* MODRM addressing. */ 230 switch (mod) 231 { 232 case 0: 233 if (rm == 5) 234 /* DISP32 addressing, no base. */ 235 ip += 4; 236 break; 237 238 case 1: 239 ip++; 240 break; 241 242 case 2: 243 ip += 4; 244 break; 245 } 246 } 247 } 248 return ip; 249} 250 251static void 252handler (int sig __attribute__ ((unused)), 253 siginfo_t *info __attribute__ ((unused)), 254 void *vucontext, 255 struct xsave_struct *buf __attribute__ ((unused))) 256{ 257 ucontext_t* uctxt; 258 greg_t trapno; 259 greg_t ip; 260 261 uctxt = vucontext; 262 trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO]; 263 ip = uctxt->uc_mcontext.gregs[REG_IP_IDX]; 264 265 if (trapno == 5) 266 { 267 uint64_t status = read_mpx_status_sig (uctxt); 268 uint64_t br_reason = status & 0x3; 269 270 __mpxrt_write (VERB_BR, "Saw a #BR! status "); 271 __mpxrt_write_uint (VERB_BR, status, 10); 272 __mpxrt_write (VERB_BR, " at 0x"); 273 __mpxrt_write_uint (VERB_BR, ip, 16); 274 __mpxrt_write (VERB_BR, "\n"); 275 276 switch (br_reason) 277 { 278 case 1: /* traditional BR */ 279 num_bnd_chk++; 280 uctxt->uc_mcontext.gregs[REG_IP_IDX] = 281 (greg_t)get_next_inst_ip ((uint8_t *)ip); 282 if (__mpxrt_mode () == MPX_RT_STOP) 283 exit (255); 284 return; 285 286 default: 287 __mpxrt_write (VERB_BR, "Unexpected status with bound exception: "); 288 __mpxrt_write_uint (VERB_BR, status, 10); 289 __mpxrt_write (VERB_BR, "\n"); 290 break; 291 } 292 } 293 else if (trapno == 14) 294 { 295 __mpxrt_write (VERB_ERROR, "In signal handler, trapno = "); 296 __mpxrt_write_uint (VERB_ERROR, trapno, 10); 297 __mpxrt_write (VERB_ERROR, ", ip = 0x"); 298 __mpxrt_write_uint (VERB_ERROR, ip, 16); 299 __mpxrt_write (VERB_ERROR, "\n"); 300 exit (255); 301 } 302 else 303 { 304 __mpxrt_write (VERB_ERROR, "Unexpected trap "); 305 __mpxrt_write_uint (VERB_ERROR, trapno, 10); 306 __mpxrt_write (VERB_ERROR, "! at 0x"); 307 __mpxrt_write_uint (VERB_ERROR, ip, 16); 308 __mpxrt_write (VERB_ERROR, "\n"); 309 exit (255); 310 } 311} 312 313/* Using wrapper to the real handler in order to save the bnd regs 314 using xsave before any unprefixed call. an unprefixed call to 315 __i686.get_pc_thunk.bx is added by the linker in 32bit at the 316 beginning of handler function since there are references to 317 global variables. */ 318static void 319handler_wrap (int signum, siginfo_t* si, void* vucontext) 320{ 321 /* Since the OS currently not handling chkptr regs. 322 We need to store them for later use. They might be 323 init due to unprefixed call,Jcc,ret. avoiding calling 324 function since the function will be unprefixed as well. */ 325 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096]; 326 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer; 327 uint64_t mask = 0x18; 328 uint32_t lmask = mask; 329 uint32_t hmask = mask >> 32; 330 331 asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t" 332 : : "D" (xsave_buf), "m" (*xsave_buf), 333 "a" (lmask), "d" (hmask) 334 : "memory"); 335 336 handler (signum, si, vucontext, xsave_buf); 337} 338 339static bool 340check_mpx_support (void) 341{ 342 unsigned int eax, ebx, ecx, edx; 343 unsigned int max_level = __get_cpuid_max (0, NULL); 344 345 if (max_level < 13) 346 { 347 __mpxrt_print (VERB_DEBUG, "No required CPUID level support.\n"); 348 return false; 349 } 350 351 __cpuid_count (0, 0, eax, ebx, ecx, edx); 352 if (!(ecx & bit_XSAVE)) 353 { 354 __mpxrt_print (VERB_DEBUG, "No XSAVE support.\n"); 355 return false; 356 } 357 358 if (!(ecx & bit_OSXSAVE)) 359 { 360 __mpxrt_print (VERB_DEBUG, "No OSXSAVE support.\n"); 361 return false; 362 } 363 364 __cpuid_count (7, 0, eax, ebx, ecx, edx); 365 if (!(ebx & bit_MPX)) 366 { 367 __mpxrt_print (VERB_DEBUG, "No MPX support.\n"); 368 return false; 369 } 370 371 __cpuid_count (13, 0, eax, ebx, ecx, edx); 372 if (!(eax & bit_BNDREGS)) 373 { 374 __mpxrt_print (VERB_DEBUG, "No BNDREGS support.\n"); 375 return false; 376 } 377 378 if (!(eax & bit_BNDCSR)) 379 { 380 __mpxrt_print (VERB_DEBUG, "No BNDCSR support.\n"); 381 return false; 382 } 383 384 return true; 385} 386 387static void 388enable_mpx (void) 389{ 390 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096]; 391 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer; 392 393 memset (buffer, 0, sizeof (buffer)); 394 xrstor_state (xsave_buf, 0x18); 395 396 __mpxrt_print (VERB_DEBUG, "Initalizing MPX...\n"); 397 __mpxrt_print (VERB_DEBUG, " Enable bit: %d\n", enable); 398 __mpxrt_print (VERB_DEBUG, " BNDPRESERVE bit: %d\n", bndpreserve); 399 400 /* Enable MPX. */ 401 xsave_buf->xsave_hdr.xstate_bv = 0x10; 402 xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base; 403 xsave_buf->bndcsr.cfg_reg_u |= enable << MPX_ENABLE_BIT_NO; 404 xsave_buf->bndcsr.cfg_reg_u |= bndpreserve << BNDPRESERVE_BIT_NO; 405 xsave_buf->bndcsr.status_reg = 0; 406 407 xrstor_state (xsave_buf, 0x10); 408} 409 410static void 411disable_mpx (void) 412{ 413 uint8_t __attribute__ ((__aligned__ (64))) buffer[4096]; 414 struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer; 415 416 memset(buffer, 0, sizeof(buffer)); 417 xrstor_state(xsave_buf, 0x18); 418 419 /* Disable MPX. */ 420 xsave_buf->xsave_hdr.xstate_bv = 0x10; 421 xsave_buf->bndcsr.cfg_reg_u = 0; 422 xsave_buf->bndcsr.status_reg = 0; 423 424 xrstor_state(xsave_buf, 0x10); 425} 426 427static bool 428process_specific_init (void) 429{ 430 if (!check_mpx_support ()) 431 return false; 432 433 l1base = mmap (NULL, MPX_L1_SIZE, PROT_READ | PROT_WRITE, 434 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 435 if (l1base == MAP_FAILED) 436 { 437 perror ("mmap"); 438 exit (EXIT_FAILURE); 439 } 440 441 enable_mpx (); 442 443 if (prctl (43, 0, 0, 0, 0)) 444 { 445 __mpxrt_print (VERB_ERROR, "No MPX support\n"); 446 disable_mpx (); 447 return false; 448 } 449 450 return true; 451} 452 453static bool 454process_specific_finish (void) 455{ 456 if (!check_mpx_support ()) 457 return false; 458 459 if (prctl (44, 0, 0, 0, 0)) 460 { 461 __mpxrt_print (VERB_ERROR, "No MPX support\n"); 462 return false; 463 } 464 465 munmap (l1base, MPX_L1_SIZE); 466 467 return true; 468} 469 470static void 471setup_handler (void) 472{ 473 int r,rs; 474 struct sigaction newact; 475 476 /* #BR is mapped to sigsegv */ 477 int signum = SIGSEGV; 478 479 newact.sa_handler = 0; 480 newact.sa_sigaction = handler_wrap; 481 482 /* sigset_t - signals to block while in the handler 483 get the old signal mask. */ 484 rs = sigprocmask (SIG_SETMASK, 0, &newact.sa_mask); 485 assert (rs == 0); 486 487 /* Call sa_sigaction, not sa_handler. */ 488 newact.sa_flags = SA_SIGINFO; 489 /* In case we call user's handler on SIGSEGV (not bound 490 violation exception) we want to allow bound checking 491 inside the user handler -> nested exception. */ 492 newact.sa_flags |= SA_NODEFER; 493 494 newact.sa_restorer = 0; 495 r = sigaction (signum, &newact, 0); 496 assert (r == 0); 497} 498 499/* Set constructor priority to two to make it run after the 500 constructor in sigaction.c. */ 501static void __attribute__ ((constructor (1005))) 502mpxrt_prepare (void) 503{ 504 __mpxrt_init_env_vars (&bndpreserve); 505 setup_handler (); 506 process_specific_init (); 507} 508 509static void __attribute__ ((destructor)) 510mpxrt_cleanup (void) 511{ 512 __mpxrt_print_summary (num_bnd_chk, MPX_L1_SIZE); 513 __mpxrt_utils_free (); 514 process_specific_finish (); 515} 516