...And you will know me by the trail of bits
Understanding AddressSanitizer: Better memory safety for your code
By Dominik Klemba and Dominik Czarnota
This post will guide you through using AddressSanitizer (ASan), a compiler plugin that helps developers detect memory issues in code that can lead to remote code execution attacks (such as WannaCry or this WebP implementation bug). ASan inserts checks around memory accesses during compile time, and crashes the program upon detecting improper memory access. It is widely used during fuzzing due to its ability to detect bugs missed by unit testing and its better performance compared to other similar tools.
ASan was designed for C and C++, but it can also be used with Objective-C, Rust, Go, and Swift. This post will focus on C++ and demonstrate how to use ASan, explain its error outputs, explore implementation fundamentals, and discuss ASan’s limitations and common mistakes, which will help you grasp previously undetected bugs.
Finally, we share a concrete example of a real bug we encountered during an audit that was missed by ASan and can be detected with our changes. This case motivated us to research ASan bug detection capabilities and contribute dozens of upstreamed commits to the LLVM project. These commits resulted in the following changes:
- Extended container sanitization ASan API in LLVM16 by adding support for unaligned memory buffers and adding a function for double-ended contiguous containers. Thanks to that, since LLVM17, std::vector annotations work with all allocators by default.
- Added std::deque annotations in LLVM17. For details, check the libc++ 17 release notes.
- Added annotations for the long string case of std::string in LLVM18 (with all allocators by default). Check the libc++18 release notes for more details.
- We have recently upstreamed short string annotations (read about “short string optimization”), and there is a high probability that they will be included in libc++19, assuming no new concerns or issues arise. Keep an eye on the libc++19 release notes.
- Redzones are not added between variables in structures.
- Redzones are not added between array elements.
- Padding in structures is not poisoned (example).
- Access to allocated, but not yet used, memory in a container won’t be detected, unless the container annotates itself like C++’s std::vector, std::deque, or std::string (in some cases). Note that std::basic_string (with external buffers) and std::deque are annotated in libc++ (thanks to our patches) while std::string is also annotated in Microsoft C++ standard library.
- Incorrect access to memory managed by a custom allocator won’t raise an error unless the allocator performs annotations.
- Only suffixes of a memory granule may be poisoned; therefore, access before an unaligned object may not be detected.
- ASan may not detect memory errors if a random address is accessed. As long as the random number generator returns an addressable address, access won’t be considered incorrect
- ASan doesn’t understand context and only checks values in shadow memory. If a random address being accessed is annotated as some error in shadow memory, ASan will correctly report that error, even if its bug title may not make much sense.
- Because ASan does not understand what programs are intended to do, accessing an array with an incorrect index may not be detected if the resulting address is still addressable, as shown in figure 18.
- ASAN_POISON_MEMORY_REGION(addr, size)
- ASAN_UNPOISON_MEMORY_REGION(addr, size)
Categories: Security Posts