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
2
3
4
5
6
7
8
// assume the array A is allocated on the stack at 'offset' from `RBP`
int A[100];

// the instruction to store starting address of array will be
LEA RAX, [RBP+offset]
// the instruction to create the bounds for array A will be
BNDMK BND0, [RAX+399]
// store RAX into BND0.LB and ~(RAX+399) into BND0.UB

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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.

// store the pointer value in the index register ECX
MOV ECX, DWORD ptr [ESI]

// store the pointer in the base register EAX
MOV EAX, ESI

// perform address translation from the linear address of the base EAX and store bounds and pointer value ECX onto a bound table entry
BNDSTX DWORD ptr [EAX+ECX], BND0

// Similarly, to retrieve a buffer and its associated bonds from a bound table entry
MOV EAX, DWORD ptr [EBX]

// perform address translation from the linear address of the base EBX, and loads bounds and pointer value from a bound table entry
BNDLDX BND0, dword ptr [EBX+EAX]

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.