NULL pointer dereferences are a common source of bugs and security vulnerabilities in embedded systems built on ARM Cortex-M CPUs. Enabling the Memory Protection Unit (MPU) on Cortex-M3 and above devices provides an effective mitigation by preventing access to address 0x00000000, the NULL pointer address. This article explains how to configure the MPU regions to protect against NULL pointer dereferences on Cortex-M devices.
What is a NULL Pointer?
A NULL pointer is a pointer variable that does not point to a valid memory location. It has a value of 0x00000000. Dereferencing a NULL pointer to read or write data will result in a fault or undefined behavior. This is a frequent cause of crashes and security exploits.
For example, consider the following C code snippet:
int *ptr = NULL;
*ptr = 10; // NULL pointer dereference!
Here, dereferencing ptr will try to write to address 0x00000000 and cause a fault. Attackers can exploit this to redirect control flow and execute malicious code.
NULL Pointer Protection with MPU
The Memory Protection Unit (MPU) available on ARM Cortex-M3 and above devices can be used to prevent NULL pointer dereferences. The MPU allows configuring memory access permissions for different regions of memory.
To protect against NULL pointers, we can configure an MPU region for the NULL address range 0x00000000 to 0x00001000 with no access permissions. This will cause a fault on any access to NULL.
MPU Region Configuration
Here is an example of how to configure the MPU to protect the NULL address range against reads and writes in C:
MPU->RBAR = 0x00000000; // NULL address
MPU->RASR = 0x00000001; // 1kB region size, no access
This sets up an MPU region starting at 0x00000000 that is 1kB large. The RASR value 0x00000001 means no permissions (no read, write or execute access).
We can also protect a larger NULL range by increasing the region size field in RASR. For example, to protect the 8kB NULL range:
MPU->RBAR = 0x00000000;
MPU->RASR = 0x00000801; // 8kB region size, no access
This MPU configuration will cause a HardFault whenever the CPU tries to read or write to the NULL address range. The application can handle the resulting exception and take appropriate action.
Enabling MPU and NULL Pointer Protection
After configuring the MPU regions, we need to enable the MPU globally using:
MPU->CTRL = 1;
The full steps are:
- Configure MPU regions for NULL range protection
- Enable MPU globally
- Handle HardFaults caused by NULL pointer access
This will activate the MPU and protect against NULL pointer exploits. The NULL pointer region should be configured early during application startup.
Example Code
Here is example code to set up an MPU region for NULL pointer protection on a Cortex-M device:
#include "stm32xyz.h" // Device header
// MPU configuration
#define NULL_REGION_SIZE 4096
// Fault handling
void HardFault_Handler(void) {
// Handle NULL pointer fault
while(1);
}
int main(void) {
// Configure NULL pointer MPU region
MPU->RBAR = 0x00000000;
MPU->RASR = NULL_REGION_SIZE | 0x01; // 4kB, no access
// Enable MPU
MPU->CTRL = 1;
// Application code...
return 0;
}
This configures a 4kB NULL region with no access, enables the MPU globally, and implements a HardFault handler to catch NULL pointer faults.
The application code can now safely dereference pointers without worrying about NULL pointer crashes. Any NULL pointer bugs will be caught by the MPU.
Conclusion
The Cortex-M Memory Protection Unit provides an efficient hardware mechanism to protect against common NULL pointer bugs. Configuring an MPU region covering the NULL address range with no permissions allows catching NULL pointer accesses, preventing crashes or exploits.
Combined with proper input validation and other software practices, MPU NULL pointer protection can significantly improve robustness and security of embedded applications built on ARM Cortex-M devices.