By Kostya Serebryany, Software Engineer, Google
C/C++ memory (un)safety remains a significant threat to security and stability of user-space applications and OS kernels. More than half of all high/critical security vulnerabilities across all major ecosystems are memory safety bugs , . Software tools for detecting memory safety bugs and/or preventing their exploitability are widely available, but their deployment in production is limited due to performance impact and increased memory usage. In order to move the needle further, various hardware extensions are starting to appear.
A major class of such hardware extensions are implementations of Control Flow Integrity (CFI). The goal of CFI is to make it hard or impossible to hijack the normal control flow of the application by exploiting a memory safety bug. Backward-edge CFI protects return addresses stored on the stack. The upcoming Intel CET introduces a strong backward-edge CFI via a hardware shadow call stack. Arm PAC, which is already present in existing hardware, allows to cryptographically sign return addresses on function entry and verify the signature before returning, i.e., PAC implements another approach to backward-edge CFI. Forward-edge CFI protects function pointers and virtual function table pointers. Future x86_64 and Arm CPUs will have a coarse-grained form of forward-edge CFI: ENDBRANCH in Intel CET and BTI in Arm v8.6. Another form of forward-edge CFI can be implemented via Arm PAC.
Hardware-based implementations of CFI have relatively low CPU overhead, on the order of 1%-2%, and thus can be applied widely. But they address only a subset of attack techniques, completely ignoring others, such as data-oriented programming (see for example the two recent iOS exploits that did not involve control flow hijacking). CFI also does not address the stability and developer productivity side of memory safety.
Another major class of hardware extensions directly addresses underlying memory safety issues by detecting and preventing use-after-free and buffer overflow bugs at run-time. SPARC ADI and Arm MTE are implementations of the memory tagging approach. With memory tagging, every small granule of application memory (16 bytes for Arm and 64 bytes for SPARC) has a 4-bit metadata, or a tag. Another 4-bit tag is stored in the upper bits of every pointer. On every load and store, the hardware verifies that the two tags match. Hardware memory tagging can be used as a) a testing tool in the lab, b) a bug detection mechanism for production, and c) a strong security mitigation. Arm MTE hardware is several years away but a software implementation of the memory tagging concept is available on all existing 64-bit Arm CPUs. SPARC ADI already exists in hardware.
CHERI implements another approach to hardware-assisted memory safety: fat pointers that encode object bounds and permissions, providing precise spatial memory safety (prevention of buffer overflows) as well as fine-grained software compartmentalization. CHERI can also be used to implement temporal safety (prevention of use-after-free), although at a higher overhead.
We encourage the reader interested in memory safety to study the above extensions and contribute to their adoption in software.