4.1.1.5 Code Execution in RAM
Security Objectives:
- Integrity
- Authenticity
- Availability
For systems that do not intend to allow execution from RAM at all, disabling the RAM execution of the device reduces the risk of inauthentic code execution. For these systems, RAM execution should be disabled at the start of the boot/IRT code as described in Disabling RAM Execution Until Reset. Similarly, each layer of code should lock this feature, if it is decided that the feature is no longer needed.
For systems that intend to load and execute code in RAM any time during the device’s operation, all mutable code needs to be verified and authenticated before being allowed to run. This is true for any code regardless of the source or how it is executed, including code loaded into RAM for execution. Execution in RAM is sometimes used in bootloaders to load the Flash routines or additional boot capabilities. Execution in RAM can also be used in an application for performance enhancements or run-time configurability. The firmware that is loading code into RAM is responsible for verifying the integrity and authenticity of the code loaded into RAM to maintain the chain of trust.
The use of mutable code in RAM introduces unique security challenges. Unlike Flash memory, RAM is volatile and more susceptible to accidental or malicious modification. Ensuring the security of code executed from RAM requires careful attention to integrity, authenticity and availability. The device must verify that the code has not been corrupted, confirm that it originates from a trusted source and protect the code from unauthorized access or overwrites. dsPIC33A devices provide hardware features, such as the Bus Matrix Execution Window (BMXIRAML and BMXIRAMH registers), ECC RAM and MBIST, as well as software mechanisms to help address these challenges. These features work together to enforce boundaries, detect errors and support secure execution of mutable code in RAM.
It is worth considering when the integrity and authenticity of the code being loaded into RAM is checked. As described in the RAM Execution section, the RAM contents are modifiable until the BMXIRAML/H registers are set. Once these registers are set, the code inside that range is now executable but is no longer modifiable. The mutability and the executability of the code are coupled. This creates three possible times to consider when to check the integrity and authenticity of the code to be executed in RAM:
- Before loading the code into RAM
- After loading the code into RAM but before setting the BMXIRAML/H registers (still modifiable but not executable)
- After setting the BMXIRAML/H registers but before jumping into the code (not modifiable but is now executable)
Selecting when to verify the integrity and authenticity the code will depend on the system and a combination of these might be desired/required to achieve a layered defense against different possible attacks.
If the code to be run in RAM is already part of an authenticated block in a Flash, write-protected section, some systems might consider doing a memory comparison over hash of the code since the authenticity and integrity of the code source has already been checked through the Flash region’s previous verification/authentication.
If the code to be run in RAM is coming through an external communication port and is not stored in local, nonvolatile storage, then the first point might not be possible.
If the code to be run in RAM is coming from an unauthenticated local storage, then allowing the data to be copied into RAM at all without checking the authenticity and integrity of the incoming data could allow an attacker to progress in a possible attack, even if that image could be spoofed as valid and then switched for an invalid image on the copy.
When using the CAM for cryptographic acceleration, a hash operation is much faster than a signature verification. Because of this time difference, it might make sense to authenticate a signature of the code digest once and then evaluate the integrity of the code against that authenticated code digest at the various stages of the code loading operations as required.
