Integrating libFuzzer with ASan and UBSan in Chromium

This article provides an overview of how the Chromium project combines libFuzzer with AddressSanitizer (ASan) and UndefinedBehaviorSanitizer (UBSan) to automate vulnerability discovery. By integrating this coverage-guided fuzzing engine with compiler-based sanitizers at the build level, Chromium developers can continuously feed mutated inputs into specific browser components and immediately detect hidden memory corruption and undefined behavior before code reaches production.

The Synergy Between Fuzzing and Sanitizers

On its own, libFuzzer is an in-process, coverage-guided evolutionary fuzzing engine. It feeds massive amounts of random and mutated data into a specific function to see if the program crashes. However, many severe security bugs do not cause an immediate crash; a slight buffer over-read or a signed integer overflow might silently corrupt data and allow the program to continue running.

This is where ASan and UBSan are essential. By compiling the fuzz target with these sanitizers, silent bugs are turned into immediate, noisy crashes:

  • libFuzzer navigates the code paths and generates the edge-case inputs.
  • AddressSanitizer (ASan) monitors memory operations to catch out-of-bounds accesses, use-after-free bugs, and memory leaks.
  • UndefinedBehaviorSanitizer (UBSan) monitors operations to catch illegal code behavior, such as using misaligned pointers, dividing by zero, or triggering signed integer overflows.

Build-Time Integration via Clang and GN

Because libFuzzer, ASan, and UBSan are all part of the LLVM compiler infrastructure, their integration in Chromium happens seamlessly at compile time using the Clang compiler and Chromium’s meta-build system, GN (Generate Ninja).

  1. Defining the Target: A Chromium developer writes a fuzz target—a C++ function named LLVMFuzzerTestOneInput that accepts a raw byte array and passes it to the Chromium API being tested. In the BUILD.gn file, this is defined using the fuzzer_test template.
  2. Configuring Build Flags: To integrate the tools, the build environment is configured using GN arguments (args.gn). The build system sets use_libfuzzer = true alongside either is_asan = true or is_ubsan = true (or both).
  3. Compiler Instrumentation: When Clang compiles the target, it performs two types of instrumentation simultaneously. First, it inserts SanitizerCoverage callbacks at the edges of basic blocks so libFuzzer can track which code paths are hit. Second, it inserts ASan's shadow-memory checks around every memory access and UBSan's runtime checks around potentially undefined arithmetic and type operations.

Runtime Execution Flow

Once the binary is compiled, the fuzzing engine and the sanitizers operate within the same process space. The execution follows a strict loop:

  1. Input Generation: libFuzzer generates a mutated data buffer in memory and passes it to LLVMFuzzerTestOneInput.
  2. Code Execution and Coverage Tracking: As the Chromium component processes the data, the SanitizerCoverage instrumentation reports back to libFuzzer which branches of code were executed. If the input triggers a newly discovered code path, libFuzzer saves that input to its corpus for future mutations.
  3. Sanitizer Monitoring: During that exact same execution, ASan replaces the standard malloc and free functions, surrounding allocated memory with poisoned "redzones." Simultaneously, UBSan validates that operations adhere to the C++ specification.
  4. Fault Interception: If a mutated input causes the Chromium code to touch an ASan redzone or violate a UBSan rule, the respective sanitizer immediately halts execution.

Crash Reporting and ClusterFuzz

When ASan or UBSan halts the program, it intercepts the crash Signal (like SIGSEGV or SIGABRT) and outputs a detailed symbolized stack trace pointing to the exact line of offending code.

Because libFuzzer shares the process, its internal signal handlers catch the sanitizer's abort routine. libFuzzer then takes the exact byte array that was in memory at the moment of the sanitizer violation and writes it to the disk as a crash artifact (e.g., crash-1a2b3c...).

In the Chromium ecosystem, this entire process is automated at scale using ClusterFuzz, Google’s distributed fuzzing infrastructure. ClusterFuzz compiles thousands of Chromium fuzzer_test targets with ASan and UBSan enabled, runs them continuously on virtual machines, collects the crash artifacts generated by the libFuzzer-sanitizer integration, deduplicates the findings, and automatically files detailed bug reports in the Chromium issue tracker.