Invalid memory access problem is commonly found in many C/C++ programs and leads to time consuming debugging, program instability and vulnerability. Many attacks exploit software bugs related to inappropriate memory accesses caused by buffer overflow (or buffer overruns). Existing set of techniques/tools to find such memory bugs in the programs and defend them from the attacks are software only solutions which result in poor performance of the protected code.
Intel® is introducing a new ISA extension called Intel® Memory Protection Extensions (Intel® MPX) (see latest Programming Reference manual posted here under Related Specifications) to be used for memory protection in applications with low performance overhead. To get the advantage of new extension, changes are required in the OS kernel, binutils, compiler, system libraries support.
This paper describes changes in GNU Binutils, GCC and Glibc to support Intel® MPX.
Intel® MPX introduces new registers called bound registers to hold bounds for a pointer and instructions to manipulate those registers (for details see the Programming Reference). Therefore the first step is to implement support for new hardware features in binutils and the GCC.
The second step in Intel® MPX support is implementation of Intel® MPX instrumentation pass in the GCC compiler which is responsible for instrumenting all memory accesses with pointer checks. Compiler changes for runtime bound checks include:
Bounds creation for statically allocated objects, objects allocated on the stack and statically initialized pointers
Intel® MPX support in ABI: ABI extension allows passing bounds for the pointers passed as function arguments and provide returned bounds with the pointers (see the posted Linux ABI document under Intel® MPX Technical Content at http://software.intel.com/en-us/intel-isa-extensions)
Bounds table content management: each pointer is stored into the memory should have its bounds stored in the corresponding row of the bounds table; compiler generates appropriate code to have the bounds table in the consistent state
Memory accesses instrumentation: compiler analyzes data flow to compute bounds corresponding to each memory access and inserts code to check used address against computed bounds
Dynamically created objects in heap using memory allocators need to set bounds for objects (buffers) at allocation time. So the next step is to add Intel® MPX support into standard memory allocators in Glibc.
To have the full protection, an application has to use libraries compiled with Intel® MPX instrumentation. It means we had to compile Glibc with the Intel® MPX enabled GCC compiler because it is used in most applications. Also we had to add Intel® MPX instrumentation to all the necessary Glibc routines (e.g. memcpy) written on assembler.
We introduce new option –fmpx in the GCC to utilize Intel® MPX instructions. Also binutils with Intel® MPX enabled should be used to get binaries with memory protection.
Consider following simple test for MPX compiled program:
int main(int argc, char* argv)
Snippet of the original assembler output (compiled with -O2):
movslq %edi, %rdi
movl -120(%rsp,%rdi,4), %eax // memory access buf[argc]
Compile test as follows: mpx-gcc/gcc test.c -fmpx –O2.
Resulted assembler snippet:
movl $399, %edx // load array length to edx
movslq %edi, %rdi // rdi contains value of argc
leaq -104(%rsp), %rax // load start address of buf to rax
bndmk (%rax,%rdx), %bnd0 // create bounds for buf
bndcl (%rax,%rdi,4), %bnd0 // check that memory access doesn’t violate buf’s low bound
bndcu 3(%rax,%rdi,4), %bnd0 // check that memory access doesn’t violate buf’s upper bound
movl -104(%rsp,%rdi,4), %eax // original memory access
Code looks pretty clear. Note only that we added displacement 3 for upper bound checking since we have 4 byte (integer) access here.
Another simple example demonstrates using of bndldx and bndstx instructions.
extern int * p;
extern int * q;
int foo( int c)
q = p;
After compilation with –O2 –fmpx we have such piece of assembler code:
movq p(%rip), %rax
movslq %edi, %rdi
bndldx p(,%rax), %bnd0 // load bounds for pointer p into bnd0
movq %rax, q(%rip) // q=p
bndstx %bnd0, q(,%rax) // we need now to update bound information for q
leaq (%rax,%rdi,4), %rax
bndcl (%rax), %bnd0
bndcu 3(%rax), %bnd0
movl (%rax), %eax
Several Intel® MPX specific compiler options besides –fmpx were introduced in the compiler. Most of them, like -fmpx-check-read and -fmpx-check-write, control number of inserted runtime bound checks. Also developers always can use intrinsics to insert Intel® MPX instructions manually. To see full set of Intel® MPX specific options and intrinsics added to the compiler check GCC Wiki page
Currently GCC compiler sources with Intel® MPX support is available in a separate branch in common GCC SVN repository. See GCC SVN page for details.
Currently no hardware with Intel® MPX ISA is available but it is always possible to use Intel® SDE instead, which can be downloaded from http://software.intel.com/en-us/articles/intel-software-development-emulator