ARM Memory Tagging Extension and How It Improves C:C++ Memory Safety
ARM Memory Tagging Extension and How It Improves C:C++ Memory Safety
I
Konstantin (Kostya) Serebryany discuss memory safety bugs typical to C and C++, current tools and
is a Software Engineer at
approaches to finding such bugs or mitigating their risk, and a new
Google. His team develops
and deploys dynamic testing
hardware feature, ARM MTE, that promises to be the biggest improve-
tools, such as AddressSanitizer, ment since the introduction of page protection.
MemorySanitizer, ThreadSanitizer, and
libFuzzer. Prior to joining Google in 2007, Memory (Un)safety
Konstantin spent four years at Elbrus/MCST More than 30 years after the Internet Worm, we are still talking about memory safety bugs
working for Sun compiler lab and then three in C and C++ programs. Numerous improvements in the software development process are
years at Intel Compiler Lab. Konstantin holds dwarfed by the exponential increase in the amount of software, its exposed attack surface,
a PhD from Moscow State University of and the discovery of new attack techniques.
Economics, Statistics, and Informatics and an Memory safety bug is an umbrella term to represent program defects inherent in C and C++
MS from Moscow State University. but also present in other languages. The most common classes of bugs are buffer overflows,
[email protected] heap-use-after-free, and stack-use-after-return.
These bugs often make the code vulnerable to exploitation. Malicious actors can leverage
memory-unsafe behavior to remotely execute code, leak sensitive information, escalate
privileges, or escape VMs. A buffer overflow in OpenSSL, nicknamed Heartbleed, achieved
notoriety for its ease of exploitation and high impact. It allowed attackers to steal a server’s
private memory, including cryptographic information such as keys and passwords, without
being detected. But named bugs like Heartbleed and Stagefright, a family of remotely exploit-
able bugs in Android, are just the tip of the iceberg.
Thousands of memory safety bugs are filed as CVEs every year. Roughly two-thirds of all
CVEs in the Android platform are memory safety bugs. A similar picture is seen across the
industry, affecting browsers, operating systems, and server-side and IoT software [1, 2]. And
even these bugs are still the tip of the iceberg. Many more bugs do not get CVEs assigned, and
many others remain unknown to software vendors. Some are being silently exploited, others
cause hard to detect data corruption, and some lie dormant waiting to strike.
Typical Bugs
Before we dive deeper, let’s take a closer look at two of our most beloved insects.
A heap-buffer-overflow happens when an object of a certain size is allocated on the heap,
and then a pointer to this object is used to access memory outside of the object bounds.
T ypically, the object is an array of n elements, and the code accesses the i-th element where
i < 0 or i >= n.
int *array = new int[n]; // heap allocation
array[n] = 42; // buffer overflow
array[-1] = 42; // buffer overflow (underflow)
array[100500] = 42; // buffer overflow, assuming n <= 100500
a probability of 15/16 or ~93%. It is up to the software to decide array[14] = 0; // access within the same 16-byte granule
whether to increase this probability with other tricks. For Various software strategies are possible to improve bug detec-
example, in order to detect contiguous buffer overflows with tion for such cases with additional cost or complexity.
perfect accuracy, the allocator may enforce that tags for adjacent
chunks are never equal. Uses of MTE
With MTE, the heap memory is tagged inside malloc() and We envision several different usage modes for MTE.
free(), and the tag checking is performed by the hardware. It First, MTE is going to be a much nicer version of AddressSani-
means that recompilation will not be required for detecting tizer for testing and fuzzing. It will find more bugs at a fraction
heap-related bugs. MTE can also identify stack-use-after-return of the cost. In many cases it will allow testing using the same
and buffer overflows on the stack or in global variables, but it binary as shipped to production.
will require recompilation with extra compiler options.
Second, MTE could be used as a mechanism for testing in pro-
Comparison with AddressSanitizer duction (e.g., crowdsourced bug detection), always-on or enabled
AddressSanitizer is a widely used tool for detecting memory randomly. For client software, such as web browsers, it means
safety issues. It uses compiler instrumentation to observe all that when a bug happens on a user device it will be detected, and,
loads and stores. Its specialized malloc “poisons” red zones with user consent, an actionable bug report will be sent to the
around heap objects to detect buffer overflows and keeps freed vendor. For server-side software it means that even the rarest
memory in quarantine to detect use-after-free. The red zones bugs will be detected immediately once they get triggered.
and the quarantine are the major causes of AddressSanitizer’s Finally, MTE can be seen as a strong security mitigation. It
high memory overhead. is true that it prevents exploitation with less than 100% prob-
MTE is conceptually similar to AddressSanitizer: both detect ability, but the probability is still very high, and the first failed
bugs at runtime, both require special functionality in malloc and exploitation attempt will warn the user and the software vendor.
free, and both require some amount of compiler support. We believe that memory tagging will detect the most common
classes of memory safety bugs in the wild, helping vendors
However, the use of address tags makes MTE sufficiently dif- identify and fix them and discouraging malicious actors from
ferent: it does not require red zones or quarantine to detect bugs. exploiting them.
This allows MTE to consume less memory. Moreover, MTE
performs checking in hardware, thus eliminating the overhead Other clever ways to use MTE will likely be discovered. MTE
of compiler instrumentation for every load and store. may allow building debuggers with infinite hardware watch-
points, efficient race detectors, or faster garbage collectors.
However, we do not have to wait for MTE to eradicate this class sampled. This means that the overhead, and the bug detection
of bugs. For example, Clang/LLVM 9.0 will have an option [8] to probability, can be scaled to be arbitrarily small. The small prob-
automatically initialize all stack variables. ability of bug detection can be improved by deploying the tool at
large scale in production. We are beginning to detect bugs this
Safer Languages way in the Google Chrome browser and other software.
No discussion of memory safety in C and C++ can ignore the
GWP-ASan is not a replacement for AddressSanitizer or
existence of “safe languages.” Java, Go, Swift, and Rust, among
HWASAN since it handles a smaller subset of bugs and has very
others, are indeed much safer, and in many cases they are a bet-
low detection probability, but it finds bugs that evade testing and
ter choice for developing new software.
only manifest in production. In the most performance-critical
But none of them are really safe. Go and Swift have data races, applications, where even 1% overhead is prohibitively expensive,
Java’s huge runtime is itself written in C++, and only Rust we will be able to use MTE to implement sampled bug detection
comes close to being safe, at a cost of a (subjectively) steeper similar to GWP-ASan, but with a much lower cost and hence
learning curve. higher sampling and detection rate.
All of these languages, of course, have the “unsafe” escape hatch.
Whenever the unsafe section is used, it turns the language into
Conclusion
Once available in hardware, the ARM Memory Tagging Exten-
C, but just slightly worse, because fewer tools, practices, and
sion will reduce C and C++ memory unsafety from disastrous
habits are available for that language to avoid memory safety
to tolerable. Hopefully, other hardware vendors will implement
bugs. Here, again, Rust is probably the best with its support for
their variants of memory tagging. Before that happens, don’t
AddressSanitizer and fuzzing. MTE will be useful for Rust and
forget to test your software with all available testing tools (e.g.,
any other memory-safe language with “unsafe” code.
AddressSanitizer or HWASAN) and fuzzers (e.g., libFuzzer),
Besides, the billions of lines of C and C++ code are not going and harden your binaries in production.
away any time soon.
Acknowledgments
GWP-ASan I want to thank my colleagues Vlad Tsyrklevich, Dmitry Vyukov,
GWP-ASan [9] is another bug detection tool that finds heap- Alexander Potapenko, and Evgeniy Stepanov for helping me
use-after-free and heap-buffer-overflows. It relies on protected prepare this article.
guard pages, the old trick used in the Electric Fence Malloc
and similar tools. But there is a twist: guarded allocations are