5373cd33a8
* Makefile.def: Add libmpx. * configure.ac: Add libmpx. * Makefile.in: Regenerate. * configure: Regenerate. gcc/ * config/i386/linux-common.h (LIBMPX_LIBS): New. (LIBMPX_SPEC): New. (CHKP_SPEC): New. * gcc.c (CHKP_SPEC): New. (LINK_COMMAND_SPEC): Add CHKP_SPEC. * c-family/c.opt (static-libmpx): New. libmpx/ Initial commit. From-SVN: r221311
516 lines
12 KiB
C
516 lines
12 KiB
C
/* mpxrt.c -*-C++-*-
|
|
*
|
|
*************************************************************************
|
|
*
|
|
* @copyright
|
|
* Copyright (C) 2014, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* @copyright
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* @copyright
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
**************************************************************************/
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
#include "config.h"
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <signal.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/prctl.h>
|
|
#include <cpuid.h>
|
|
#include "mpxrt-utils.h"
|
|
|
|
#ifdef __i386__
|
|
|
|
/* i386 directory size is 4MB */
|
|
#define NUM_L1_BITS 20
|
|
|
|
#define REG_IP_IDX REG_EIP
|
|
#define REX_PREFIX
|
|
|
|
#define XSAVE_OFFSET_IN_FPMEM sizeof (struct _libc_fpstate)
|
|
|
|
#else /* __i386__ */
|
|
|
|
/* x86_64 directory size is 2GB */
|
|
#define NUM_L1_BITS 28
|
|
|
|
#define REG_IP_IDX REG_RIP
|
|
#define REX_PREFIX "0x48, "
|
|
|
|
#define XSAVE_OFFSET_IN_FPMEM 0
|
|
|
|
#endif /* !__i386__ */
|
|
|
|
#define MPX_ENABLE_BIT_NO 0
|
|
#define BNDPRESERVE_BIT_NO 1
|
|
|
|
const size_t MPX_L1_SIZE = (1UL << NUM_L1_BITS) * sizeof (void *);
|
|
|
|
struct xsave_hdr_struct
|
|
{
|
|
uint64_t xstate_bv;
|
|
uint64_t reserved1[2];
|
|
uint64_t reserved2[5];
|
|
} __attribute__ ((packed));
|
|
|
|
struct bndregs_struct
|
|
{
|
|
uint64_t bndregs[8];
|
|
} __attribute__ ((packed));
|
|
|
|
struct bndcsr_struct {
|
|
uint64_t cfg_reg_u;
|
|
uint64_t status_reg;
|
|
} __attribute__((packed));
|
|
|
|
struct xsave_struct
|
|
{
|
|
uint8_t fpu_sse[512];
|
|
struct xsave_hdr_struct xsave_hdr;
|
|
uint8_t ymm[256];
|
|
uint8_t lwp[128];
|
|
struct bndregs_struct bndregs;
|
|
struct bndcsr_struct bndcsr;
|
|
} __attribute__ ((packed));
|
|
|
|
/* Following vars are initialized at process startup only
|
|
and thus are considered to be thread safe. */
|
|
static void *l1base = NULL;
|
|
static int bndpreserve;
|
|
static int enable = 1;
|
|
|
|
/* Var holding number of occured BRs. It is modified from
|
|
signal handler only and thus it should be thread safe. */
|
|
static uint64_t num_bnd_chk = 0;
|
|
|
|
static inline void
|
|
xrstor_state (struct xsave_struct *fx, uint64_t mask)
|
|
{
|
|
uint32_t lmask = mask;
|
|
uint32_t hmask = mask >> 32;
|
|
|
|
asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
|
|
: : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
|
|
: "memory");
|
|
}
|
|
|
|
static inline void
|
|
xsave_state (struct xsave_struct *fx, uint64_t mask)
|
|
{
|
|
uint32_t lmask = mask;
|
|
uint32_t hmask = mask >> 32;
|
|
|
|
asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
|
|
: : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
|
|
: "memory");
|
|
}
|
|
|
|
static inline uint64_t
|
|
xgetbv (uint32_t index)
|
|
{
|
|
uint32_t eax, edx;
|
|
|
|
asm volatile (".byte 0x0f,0x01,0xd0" /* xgetbv */
|
|
: "=a" (eax), "=d" (edx)
|
|
: "c" (index));
|
|
return eax + ((uint64_t)edx << 32);
|
|
}
|
|
|
|
static uint64_t
|
|
read_mpx_status_sig (ucontext_t *uctxt)
|
|
{
|
|
uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
|
|
memset (buffer, 0, sizeof (buffer));
|
|
memcpy (buffer,
|
|
(uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM,
|
|
sizeof (struct xsave_struct));
|
|
return xsave_buf->bndcsr.status_reg;
|
|
}
|
|
|
|
static uint8_t *
|
|
get_next_inst_ip (uint8_t *addr)
|
|
{
|
|
uint8_t *ip = addr;
|
|
uint8_t sib;
|
|
|
|
/* Determine the prefix. */
|
|
switch (*ip)
|
|
{
|
|
case 0xf2:
|
|
case 0xf3:
|
|
case 0x66:
|
|
ip++;
|
|
break;
|
|
}
|
|
|
|
/* Look for rex prefix. */
|
|
if ((*ip & 0x40) == 0x40)
|
|
ip++;
|
|
|
|
/* Make sure we have a MPX instruction. */
|
|
if (*ip++ != 0x0f)
|
|
return addr;
|
|
|
|
/* Skip the op code byte. */
|
|
ip++;
|
|
|
|
/* Get the moderm byte. */
|
|
uint8_t modrm = *ip++;
|
|
|
|
/* Break it down into parts. */
|
|
uint8_t rm = modrm & 7;
|
|
uint8_t mod = (modrm >> 6);
|
|
|
|
/* Init the parts of the address mode. */
|
|
uint8_t base = 8;
|
|
|
|
/* Is it a mem mode? */
|
|
if (mod != 3)
|
|
{
|
|
/* Look for scaled indexed addressing. */
|
|
if (rm == 4)
|
|
{
|
|
/* SIB addressing. */
|
|
sib = *ip++;
|
|
base = sib & 7;
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
if (base == 5)
|
|
ip += 4;
|
|
break;
|
|
|
|
case 1:
|
|
ip++;
|
|
break;
|
|
|
|
case 2:
|
|
ip += 4;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* MODRM addressing. */
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
if (rm == 5)
|
|
/* DISP32 addressing, no base. */
|
|
ip += 4;
|
|
break;
|
|
|
|
case 1:
|
|
ip++;
|
|
break;
|
|
|
|
case 2:
|
|
ip += 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ip;
|
|
}
|
|
|
|
static void
|
|
handler (int sig __attribute__ ((unused)),
|
|
siginfo_t *info __attribute__ ((unused)),
|
|
void *vucontext,
|
|
struct xsave_struct *buf __attribute__ ((unused)))
|
|
{
|
|
ucontext_t* uctxt;
|
|
greg_t trapno;
|
|
greg_t ip;
|
|
|
|
uctxt = vucontext;
|
|
trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO];
|
|
ip = uctxt->uc_mcontext.gregs[REG_IP_IDX];
|
|
|
|
if (trapno == 5)
|
|
{
|
|
uint64_t status = read_mpx_status_sig (uctxt);
|
|
uint64_t br_reason = status & 0x3;
|
|
|
|
__mpxrt_write (VERB_BR, "Saw a #BR! status ");
|
|
__mpxrt_write_uint (VERB_BR, status, 10);
|
|
__mpxrt_write (VERB_BR, " at 0x");
|
|
__mpxrt_write_uint (VERB_BR, ip, 16);
|
|
__mpxrt_write (VERB_BR, "\n");
|
|
|
|
switch (br_reason)
|
|
{
|
|
case 1: /* traditional BR */
|
|
num_bnd_chk++;
|
|
uctxt->uc_mcontext.gregs[REG_IP_IDX] =
|
|
(greg_t)get_next_inst_ip ((uint8_t *)ip);
|
|
if (__mpxrt_mode () == MPX_RT_STOP)
|
|
exit (255);
|
|
return;
|
|
|
|
default:
|
|
__mpxrt_write (VERB_BR, "Unexpected status with bound exception: ");
|
|
__mpxrt_write_uint (VERB_BR, status, 10);
|
|
__mpxrt_write (VERB_BR, "\n");
|
|
break;
|
|
}
|
|
}
|
|
else if (trapno == 14)
|
|
{
|
|
__mpxrt_write (VERB_ERROR, "In signal handler, trapno = ");
|
|
__mpxrt_write_uint (VERB_ERROR, trapno, 10);
|
|
__mpxrt_write (VERB_ERROR, ", ip = 0x");
|
|
__mpxrt_write_uint (VERB_ERROR, ip, 16);
|
|
__mpxrt_write (VERB_BR, "\n");
|
|
exit (255);
|
|
}
|
|
else
|
|
{
|
|
__mpxrt_write (VERB_ERROR, "Unexpected trap ");
|
|
__mpxrt_write_uint (VERB_ERROR, trapno, 10);
|
|
__mpxrt_write (VERB_ERROR, "! at 0x");
|
|
__mpxrt_write_uint (VERB_ERROR, ip, 16);
|
|
__mpxrt_write (VERB_BR, "\n");
|
|
exit (255);
|
|
}
|
|
}
|
|
|
|
/* Using wrapper to the real handler in order to save the bnd regs
|
|
using xsave before any unprefixed call. an unprefixed call to
|
|
__i686.get_pc_thunk.bx is added by the linker in 32bit at the
|
|
beginning of handler function since there are references to
|
|
global variables. */
|
|
static void
|
|
handler_wrap (int signum, siginfo_t* si, void* vucontext)
|
|
{
|
|
/* Since the OS currently not handling chkptr regs.
|
|
We need to store them for later use. They might be
|
|
init due to unprefixed call,Jcc,ret. avoiding calling
|
|
function since the function will be unprefixed as well. */
|
|
uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
uint64_t mask = 0x18;
|
|
uint32_t lmask = mask;
|
|
uint32_t hmask = mask >> 32;
|
|
|
|
asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
|
|
: : "D" (xsave_buf), "m" (*xsave_buf),
|
|
"a" (lmask), "d" (hmask)
|
|
: "memory");
|
|
|
|
handler (signum, si, vucontext, xsave_buf);
|
|
}
|
|
|
|
static bool
|
|
check_mpx_support (void)
|
|
{
|
|
unsigned int eax, ebx, ecx, edx;
|
|
unsigned int max_level = __get_cpuid_max (0, NULL);
|
|
|
|
if (max_level < 13)
|
|
{
|
|
__mpxrt_print (VERB_DEBUG, "No required CPUID level support.\n");
|
|
return false;
|
|
}
|
|
|
|
__cpuid_count (0, 0, eax, ebx, ecx, edx);
|
|
if (!(ecx & bit_XSAVE))
|
|
{
|
|
__mpxrt_print (VERB_DEBUG, "No XSAVE support.\n");
|
|
return false;
|
|
}
|
|
|
|
if (!(ecx & bit_OSXSAVE))
|
|
{
|
|
__mpxrt_print (VERB_DEBUG, "No OSXSAVE support.\n");
|
|
return false;
|
|
}
|
|
|
|
__cpuid_count (7, 0, eax, ebx, ecx, edx);
|
|
if (!(ebx & bit_MPX))
|
|
{
|
|
__mpxrt_print (VERB_DEBUG, "No MPX support.\n");
|
|
return false;
|
|
}
|
|
|
|
__cpuid_count (13, 0, eax, ebx, ecx, edx);
|
|
if (!(eax & bit_BNDREGS))
|
|
{
|
|
__mpxrt_print (VERB_DEBUG, "No BNDREGS support.\n");
|
|
return false;
|
|
}
|
|
|
|
if (!(eax & bit_BNDCSR))
|
|
{
|
|
__mpxrt_print (VERB_DEBUG, "No BNDCSR support.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
enable_mpx (void)
|
|
{
|
|
uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
|
|
memset (buffer, 0, sizeof (buffer));
|
|
xrstor_state (xsave_buf, 0x18);
|
|
|
|
__mpxrt_print (VERB_DEBUG, "Initalizing MPX...\n");
|
|
__mpxrt_print (VERB_DEBUG, " Enable bit: %d\n", enable);
|
|
__mpxrt_print (VERB_DEBUG, " BNDPRESERVE bit: %d\n", bndpreserve);
|
|
|
|
/* Enable MPX. */
|
|
xsave_buf->xsave_hdr.xstate_bv = 0x10;
|
|
xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base;
|
|
xsave_buf->bndcsr.cfg_reg_u |= enable << MPX_ENABLE_BIT_NO;
|
|
xsave_buf->bndcsr.cfg_reg_u |= bndpreserve << BNDPRESERVE_BIT_NO;
|
|
xsave_buf->bndcsr.status_reg = 0;
|
|
|
|
xrstor_state (xsave_buf, 0x10);
|
|
}
|
|
|
|
static void
|
|
disable_mpx (void)
|
|
{
|
|
uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
|
|
struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
xrstor_state(xsave_buf, 0x18);
|
|
|
|
/* Disable MPX. */
|
|
xsave_buf->xsave_hdr.xstate_bv = 0x10;
|
|
xsave_buf->bndcsr.cfg_reg_u = 0;
|
|
xsave_buf->bndcsr.status_reg = 0;
|
|
|
|
xrstor_state(xsave_buf, 0x10);
|
|
}
|
|
|
|
static bool
|
|
process_specific_init (void)
|
|
{
|
|
if (!check_mpx_support ())
|
|
return false;
|
|
|
|
l1base = mmap (NULL, MPX_L1_SIZE, PROT_READ | PROT_WRITE,
|
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
if (l1base == MAP_FAILED)
|
|
{
|
|
perror ("mmap");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
enable_mpx ();
|
|
|
|
if (prctl (43, 0, 0, 0, 0))
|
|
{
|
|
__mpxrt_print (VERB_ERROR, "No MPX support\n");
|
|
disable_mpx ();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
process_specific_finish (void)
|
|
{
|
|
if (!check_mpx_support ())
|
|
return false;
|
|
|
|
if (prctl (44, 0, 0, 0, 0))
|
|
{
|
|
__mpxrt_print (VERB_ERROR, "No MPX support\n");
|
|
return false;
|
|
}
|
|
|
|
munmap (l1base, MPX_L1_SIZE);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
setup_handler (void)
|
|
{
|
|
int r,rs;
|
|
struct sigaction newact;
|
|
|
|
/* #BR is mapped to sigsegv */
|
|
int signum = SIGSEGV;
|
|
|
|
newact.sa_handler = 0;
|
|
newact.sa_sigaction = handler_wrap;
|
|
|
|
/* sigset_t - signals to block while in the handler
|
|
get the old signal mask. */
|
|
rs = sigprocmask (SIG_SETMASK, 0, &newact.sa_mask);
|
|
assert (rs == 0);
|
|
|
|
/* Call sa_sigaction, not sa_handler. */
|
|
newact.sa_flags = SA_SIGINFO;
|
|
/* In case we call user's handler on SIGSEGV (not bound
|
|
violation exception) we want to allow bound checking
|
|
inside the user handler -> nested exception. */
|
|
newact.sa_flags |= SA_NODEFER;
|
|
|
|
newact.sa_restorer = 0;
|
|
r = sigaction (signum, &newact, 0);
|
|
assert (r == 0);
|
|
}
|
|
|
|
/* Set constructor priority to two to make it run after the
|
|
constructor in sigaction.c. */
|
|
static void __attribute__ ((constructor (1005)))
|
|
mpxrt_prepare (void)
|
|
{
|
|
__mpxrt_init_env_vars (&bndpreserve);
|
|
setup_handler ();
|
|
process_specific_init ();
|
|
}
|
|
|
|
static void __attribute__ ((destructor))
|
|
mpxrt_cleanup (void)
|
|
{
|
|
__mpxrt_print_summary (num_bnd_chk, MPX_L1_SIZE);
|
|
__mpxrt_utils_free ();
|
|
process_specific_finish ();
|
|
}
|