Hand sanitizers are high in demand right now. Rightfully so! With an ongoing global pandemic, you want to stay protected from harmful infections. Developers like to take a similar approach with their software. Thus, an increasing number of C/C++ developers use Google sanitizers to detect software issues or vulnerabilities before they can cause any harm.
Henning Perl is Chief of Customer Success and COO of Code Intelligence. In this article, he discusses the role of Google sanitizers in modern application security, with a focus on address sanitizers. He answers some of the most frequently asked questions surrounding this topic.
An address sanitizer is a software library that you compile into your code to make your program crash more often. The question is, why would you even want that?
When you don't compile the program for production but tests, you want to make the program execution more strict to catch bugs. An address sanitizer makes the interaction with the memory more rigid.
When you are allocating bytes, and you write beyond the allocated buffer size, then a typical program might not crash because it might be valid to access information in the unallocated address. The program will only crash if it finds a bug at the end of a memory page. Address sanitizers force programs to crash reliably when memory violations occur, allowing you to inspect and resolve the violation in your code.
An address sanitizer primarily focuses on the interaction with the memory. It checks that you don't write or read beyond your allocated memory areas.
However, there are many other types of sanitizers. For example, thread sanitizers help you uncover data races that you mainly have in multithreaded programming. Then, there are leak sanitizers to ensure that all allocated memory is eventually freed. And lastly, undefined behavior sanitizers checking whether or not you are using constructs triggering unspecified or undefined behavior by the C standard.
They are all different kinds of niches, in which they make the program more strict. Although this slows the program down, it doesn't matter during testing nor fuzzing
There are alternatives to sanitizers, such as “Valgrind”, an established tool used to check software as it runs. However, sanitizers are more suited at finding error cases (Valgrind does not find stack or global out-of-bounds errors), the execution speed is faster, and they provide better support for multi-threaded applications.
With address sanitizers, you can find out-of-bounds accesses to the heap stack, global variable use after free, use after return, use after scope errors, where you have a dangling pointer, a double free, or an invalid free.
When an attacker has out-of-bounds write access to a memory location, the attacker could leverage that for a remote code execution, which is the holy grail of exploitation. It sends inputs to a program that writes more data than it should to areas that the attacker can then jump to, to execute the code. Using this attack vector, anything is possible. The program could just crash. It could corrupt data. It could even return sensitive data, as we have seen with heartbleed. Therefore, it's a highly critical bug to fix.
The easiest way to do this is to load the program into a debugger and reproduce the bug. Without a debugger, you could also inspect the stacktrace before the program is terminated by the sanitizers. The stack trace will give you a strong indication of which function caused the malicious action. However, if you want to inspect the set values, a debugger is the way to go.
In fuzzing, it is important that the program execution is as restricted as possible in order to trigger error cases reliably. If you send multiple inputs to the program, you want it to crash on the current input reliably. It is important to create reproducible crashes during the fuzzing process. Therefore, the program being tested should be in a clean state, and only the inputs that caused a crash should be captured. This is where sanitizers come in handy. Fuzzing a program that is compiled with a sanitizer helps causing it to crash on an interesting input that causes a violation
If you're not already using sanitizers to run your unit tests, you should start doing so! Done right, it is one of the easiest and most effective ways to start testing your software for security issues.
If you are working on small projects, you can already achieve a lot with open source sanitizers. But if you are working on complex applications, it might be helpful to evaluate some enterprise tools and integrate them into your CI/CD workflow. CI Fuzz, for example, is a testing platform that includes many well-known sanitizers. Being CI/CD-agnostic, it will benefit your development speed and the security of your software.
Before co-founding Code Intelligence, Henning worked as a project manager at Fraunhofer FKIE and completed his PhD on finding vulnerabilities in Open Source programs. As the COO and Chief of Customer Success, Henning helps companies implement our feedback-based fuzzing platform to make their software more secure.