Intel Memory Protection Extensions
Introduction
Intel MPX is designed to allow a system to run both Intel MPX enabled software and legacy software. MPX enabled application can link with, call into, or be called from legacy software. MPX associates bounds with pointers in a novel manner, and uses bounds to check that pointer based accesses are suitably constrained. Futhermore, programmers can selectively use MPX to protect a subset of pointers.
The initial goal of MPX is twofold: 1) provide means to defend a system against attacks, 2) provide means to pinpoint accidental logic defects in pointer usage.
Programming Environment
Intel MPX can be enabled for user mode and supervisor mode. And, it is designed to allow software to associate bounds with pointers, and allows software to check memory references against the bounds to prevent out of bound access. The bounds registers hold lower bound and upper bound. An OOB access can causes a #BR (Bound Range Exceeded) exception.
The BNDLDX
and BNDSTX
instructions each take an operand whose bits are used to travese data structure in memory. In 64-bit mode, these instructions operate only on the lower bits in the supplied 64-bit addresses. The number of bits used is 48 plus a value called the MPX address-width adjust(MAWA) which depends on CPL (if CPL < 3 then MAWA = 0 else MAWA = CPUID.(EAX=07H,ECX=0H):ECX.MAWAU[bits 21:17]
).
Bounds Registers
Intel MPX architectrure defines four registers BND0-3
, each of which stores a pair of 64-bit lower bound (LB) and upper bound (UB). The bounds are inclusive. The upper bounds are architecturally represented in 1’s complement form. Thus, lower bound = 0, upper bound = 0 will allow access entire address space.
Configuration and Status Registers
Intel MPX defines two configuration registers for user mode (BNDCFGU
) and supervisor mode (BDNCFGS
), respectively. Also, Intel MPX defines a status register (BNDSTATUS
) primarily used to communicates states information for #BR exception.
Intel MPX Instruction Summary
Usage and Examples
BNDMK
is typically used after memory is allocated for a buffer. However, many other usages are possible such as when accessing an array member of a structure.
1 | // assume the array A is allocated on the stack at 'offset' from `RBP` |
BNDMOV
is typically used to copy bounds from one bound register to another when a pointer is copied from one GP register to another, or to spill/fill bounds into memory corresponding to a spill/fill of a pointer.
BNDCL
/BNDCU
/BNDCN
are typically used before writing to a buffer but can be used in other instances as well. The pointer used to write to memory will be compared against lower bound. However, for upper bound check, the software must add the (operand size - 1) to the pointer before upper bound checking.
BNDSTX
is used to store the bounds associated with a buffer and the “pointer value” of the pointer to that buffer onto a bound table entry via address translation using a two-level structure.
1 | // assume a buffer with bounds stored in `BND0`, the pointer to the buffer is in ESI, the following sequence will store the "pointer value" and the bounds into a a configured bound table entry. |
Loading and Storing Bounds in Memory
Intel MPX defines two instructions to load and store of the linear address of a pointer to a buffer, along with the bounds of the buffer into a data structure of extended bounds. When storing these extended bounds, the processor parses the address of the pointer (where it is stored) to locate an entry in a bound table in which to store the extended bounds.
An extended bound is a 4-tuple consisting of lower bounds, upper bound, pointer value and a reserved field. On 64-bit paging mode, a bound table entry is 4*64 bits (32 bytes). The linear address of a bound table is stored in a bound directory entry.The linear address of the bound directory is derived from either BNDCFU
or BNDCFGS
.
The bound directory and bound table are stored in application memory and are allocated by the application. In 64-bit mode, the size of a bound table is 4 MBytes (2 17 * 32 bytes), and the size of a bound directory is 2 1 + MAWA GBytes (2 28+MAWA).
Bounds in memory are associated with the memory address where the pointer is stored.
BNDLDX
and BNDSTX
in 64-Bit Mode
The linear address of the bound directory is derived from BNDCFGs
. In 64-bit mode, each bound-directory entry (BDE) is 8 bytes. The number of entries in the bound directory is 228+MAWA.
In 64-bit mode, the processor uses the two-level structures to access extended bounds as follows:
In the first stage, the corresponding BD entry has to be loaded. For that, the CPU: (1) extracts the offset of BD entry from bits 20:47 of the pointer address and shifted it by 3 bits. (2) loads the base address of BD from the
BDCFGx
register, and (3) sums the base and the offset and loads the BD entry of the resulting address.In the second stage, the CPU: (4) extracts the offset of BT entry from bits 3:19 of the pointer address and shifts it by 5 bits, (5) shifts the loaded entry by 3 to remove the metadata contained in the first 3 bits, and (6) sums the base and the offset and (7) finally loads the BT entry from the resulting address.