Security and the Cortex-M MPU
As embedded systems are drawn more into the IoT, security in the form of protection of critical resources is becoming increasingly important. Effective protection can only be achieved via hardware means.
The Cortex-M Memory Protection Unit (MPU) is difficult to use, but it is the main means of hardware memory protection available for most Cortex-M processors. These processors are in widespread use in small to medium size embedded systems. Hence, it behooves us to learn to use this MPU effectively in order to achieve the reliability, security, and safety that modern embedded systems require.
Developing a good protection strategy for an embedded system is difficult enough without having to deal with excessive low-level complexity. The latter is not only frustrating, but may result in insufficient system protection. MPU software is needed that overcomes hardware complexity and provides a solid foundation for creating protected, secure systems and for detecting and dealing with security violations. This is the first in a series of blogs presenting a software approach to satisfy this need.
Cortex-M processors have three modes of operation:
- handler mode – privileged mode for ISRs, fault handlers, the SVC Handler, and the PendSV Handler. This mode can be entered only via an exception.
- privileged thread mode – privileged tasks (ptasks) run in this mode. It can be entered only from handler mode.
- unprivileged thread mode – unprivileged tasks (utasks) run in this mode. It can be entered from either of the above two modes.
In the discussions that follow, the first two modes are collectively called pmode and the third mode is called umode. Similarly, this and subsequent blogs refer to pcode, ucode, pSSRs, uSSRs, etc. These are not industry-standard terms, but rather are introduced here to simplify discussions.
The basic goal of protection is to run trusted software in pmode and to run all less-trusted software in umode. Examples of trusted code are RTOSs, ISRs, handlers, and low-level drivers. Examples of less-trusted code are untested code, third party software or software of an unknown pedigree (SOUP), and code that is vulnerable to malware such as protocol stacks and high-level drivers. This goal can be broken into subgoals for umode:
- Prevent direct access to RTOS services and data
- Prevent access to restricted RTOS services by utasks
- Protect processor core resources (e.g. SysTick timer)
- Protect peripherals from unintended modification
- Prevent code execution from RAM
- Prevent direct access to critical system code and data
- Restrict utasks and utask groups to access only designated code and data
- Permit isolating utasks and utask groups from each other
- Detect task stack and buffer overflows immediately
- Detect intrusions and bugs and shut them down so critical operations are not imperiled
Obviously, the degree of protection needed depends upon the security and safety requirements of the specific system. Note that protection for a system can be increased in future releases as it becomes more widely distributed and therefore more vulnerable. This MPU software approach fosters progressive protection improvement in this manner.
Cortex-M0/1/3/4 MPUs have 8 slots and Cortex-M7 MPUs have 16 slots. Each active slot defines a memory region with its own attributes such as size, alignment, read/write (RW), read only (RO), execute never (XN), etc. Slots in which the EN bit is zero are inactive and have no effect upon memory accesses. Hence a user is not forced to use all slots. Unused slots are usually filled with zeros to disable them.
Two unfortunate aspects of the Cortex-M MPU are that memory region sizes must be powers of 2, ranging from 32 bytes to 4 GB and memory regions must start on multiples of their sizes. These requirements undermine the utility of the MPU by making it difficult to use without wasting substantial memory. For example, if a protected stack increases from 256 bytes to 260 bytes, the region containing it must be increased from 256 bytes to 512 bytes, and if it is not already on a 512-byte boundary, it must be moved up 256 bytes to the next 512-byte boundary – almost a 200% waste of memory. This is a problem because systems using Cortex-M processors usually have limited memory.
An advantage of the MPU is that it allows definition of very small regions (as little as 32 bytes). This compares to a minimum of 4096 bytes for most MMUs. Hence an MPU is more appropriate for RTOS-based multitasking systems than is an MMU.
Following initialization, the MPU is in background mode and all of its slots are disabled (i.e. all are filled with zeros). In this mode, operation is the same as with the MPU off or no MPU at all. System initialization is performed in this mode. Then MPU slots are loaded with regions.
Converting legacy code
Hacks and malware attacks are becoming increasingly prevalent. As a consequence, many managers probably wish that products their companies are shipping today had better protection. Retrofitting legacy code with MPU protection is possible and is a primary objective of the MPU software that we will be discussing.
Legacy code will run normally in privileged mode (pmode) with the MPU enabled in background mode. This is the starting point. From here, less-trusted tasks and code are gradually moved to unprivileged mode (umode). This step-by-step process allows dealing with the least-trusted and most-vulnerable code first, while making sure that the system continues to run correctly after each step. If it does not, the step can be reversed and the problem(s) found and fixed. This permits a strategy of security updates to make installed systems more secure as their numbers increase.
Another good time to add MPU protection is when new features are added to legacy systems that have been in use for some time. In this case, the new features are probably ancillary to the main function of the equipment and thus can and should be isolated. This would be particularly true if networking were added to what had been a stand-alone system, for example.
Trusted code and trusted tasks are best left running in pmode because it is simpler and faster. pcode and pdata are in the background region and thus accessible by all ptasks and handlers. This assures that carefully-crafted, mission-critical code need not be rewritten – it stays the same and runs the same. Furthermore, communication stays the same whether between ptasks or between ptasks and utasks. However, utasks are isolated and may not perform restricted system services, such as power down or deleting other tasks.
Developing new code
Security adds a new dimension to product development. While theoretically sound to “build security in from the start”, it may not be an overly welcome added dimension to projects that already have too many dimensions and too little time to achieve them. Thus, there may be a need to postpone security measures until late in the project, or even after the project, when they become more beneficial and less of a distraction. Drawing protection boundaries before tasks and code have stabilized can waste significant time.
Background mode may be the best starting point for developing new code for new projects since debugging is simpler. However, once code is operating reasonably well, moving it into umode enables the power of the MPU and SVC (Supervisor Call) to assist in debugging problems such as stack and buffer overflows, wild pointers, restricted operations, etc. This is most helpful during the system-integration phase. Tasks can be moved from pmode to umode and back in order to track down and fix problems caused by umode.
The benefits of MPU error detection during the final project phase can easily outweigh the time required to modify code and tasks to run in umode. However, some projects may prefer to suffer. If so, post-release system security upgrades are possible. Of course this breaks all the rules, but it makes practical sense for projects that are behind schedule and overwhelmed by just getting required features to work.
Once the dust settles, it is possible to step back, look at the system security requirements, and start making the system more secure. During this time, manufacturing and installation problems are being slowly solved by other people, shipments are gradually increasing, and likewise, security can be gradually improving.
The upcoming articles are as follows:
- Multitasking and the MPU
- Defining MPU Regions
- Software Interrupt API for MPU Systems
- Structuring MPU Applications
More information can also be found at www.smxrtos.com/mpu.