coverage-analysis

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Coverage Analysis

覆盖率分析

Coverage analysis is essential for understanding which parts of your code are exercised during fuzzing. It helps identify fuzzing blockers like magic value checks and tracks the effectiveness of harness improvements over time.
覆盖率分析对于了解fuzzing期间代码的哪些部分被执行至关重要。它有助于识别诸如魔术值检查之类的fuzzing阻碍因素,并跟踪harness改进的长期有效性。

Overview

概述

Code coverage during fuzzing serves two critical purposes:
  1. Assessing harness effectiveness: Understand which parts of your application are actually executed by your fuzzing harnesses
  2. Tracking fuzzing progress: Monitor how coverage changes when updating harnesses, fuzzers, or the system under test (SUT)
Coverage is a proxy for fuzzer capability and performance. While coverage is not ideal for measuring fuzzer performance in absolute terms, it reliably indicates whether your harness works effectively in a given setup.
Fuzzing期间的代码覆盖率有两个关键用途:
  1. 评估harness有效性:了解应用程序的哪些部分实际被你的fuzzing harness执行
  2. 跟踪fuzzing进度:在更新harness、fuzzer或被测系统(SUT)时,监控覆盖率的变化
覆盖率是衡量fuzzer能力和性能的一个指标。尽管从绝对意义上讲,覆盖率并不是衡量fuzzer性能的理想指标,但它可以可靠地表明你的harness在特定设置下是否有效工作。

Key Concepts

核心概念

ConceptDescription
Coverage instrumentationCompiler flags that track which code paths are executed
Corpus coverageCoverage achieved by running all test cases in a fuzzing corpus
Magic value checksHard-to-discover conditional checks that block fuzzer progress
Coverage-guided fuzzingFuzzing strategy that prioritizes inputs that discover new code paths
Coverage reportVisual or textual representation of executed vs. unexecuted code
概念描述
Coverage instrumentation用于跟踪哪些代码路径被执行的编译器标志
Corpus coverage运行fuzzing corpus中所有测试用例所达到的覆盖率
Magic value checks难以发现的条件检查,会阻碍fuzzer的进展
Coverage-guided fuzzing优先选择能发现新代码路径的输入的fuzzing策略
Coverage report已执行代码与未执行代码的可视化或文本表示

When to Apply

适用场景

Apply this technique when:
  • Starting a new fuzzing campaign to establish a baseline
  • Fuzzer appears to plateau without finding new paths
  • After harness modifications to verify improvements
  • When migrating between different fuzzers
  • Identifying areas requiring dictionary entries or seed inputs
  • Debugging why certain code paths aren't reached
Skip this technique when:
  • Fuzzing campaign is actively finding crashes
  • Coverage infrastructure isn't set up yet
  • Working with extremely large codebases where full coverage reports are impractical
  • Fuzzer's internal coverage metrics are sufficient for your needs
适用于以下场景:
  • 启动新的fuzzing活动以建立基准
  • Fuzzer进入平台期,无法发现新路径
  • 修改harness后验证改进效果
  • 在不同fuzzer之间迁移时
  • 识别需要字典条目或种子输入的区域
  • 调试某些代码路径无法被触及的原因
以下场景可跳过:
  • Fuzzing活动正在积极发现崩溃问题
  • 尚未搭建覆盖率基础设施
  • 处理超大型代码库,生成完整覆盖率报告不切实际
  • Fuzzer的内部覆盖率指标已满足需求

Quick Reference

快速参考

TaskCommand/Pattern
LLVM coverage instrumentation (C/C++)
-fprofile-instr-generate -fcoverage-mapping
GCC coverage instrumentation
-ftest-coverage -fprofile-arcs
cargo-fuzz coverage (Rust)
cargo +nightly fuzz coverage <target>
Generate LLVM profile data
llvm-profdata merge -sparse file.profraw -o file.profdata
LLVM coverage report
llvm-cov report ./binary -instr-profile=file.profdata
LLVM HTML report
llvm-cov show ./binary -instr-profile=file.profdata -format=html -output-dir html/
gcovr HTML report
gcovr --html-details -o coverage.html
任务命令/模式
LLVM覆盖率插桩(C/C++)
-fprofile-instr-generate -fcoverage-mapping
GCC覆盖率插桩
-ftest-coverage -fprofile-arcs
cargo-fuzz覆盖率分析(Rust)
cargo +nightly fuzz coverage <target>
生成LLVM profile数据
llvm-profdata merge -sparse file.profraw -o file.profdata
生成LLVM覆盖率报告
llvm-cov report ./binary -instr-profile=file.profdata
生成LLVM HTML报告
llvm-cov show ./binary -instr-profile=file.profdata -format=html -output-dir html/
生成gcovr HTML报告
gcovr --html-details -o coverage.html

Ideal Coverage Workflow

理想的覆盖率工作流

The following workflow represents best practices for integrating coverage analysis into your fuzzing campaigns:
[Fuzzing Campaign]
       |
       v
[Generate Corpus]
       |
       v
[Coverage Analysis]
       |
       +---> Coverage Increased? --> Continue fuzzing with larger corpus
       |
       +---> Coverage Decreased? --> Fix harness or investigate SUT changes
       |
       +---> Coverage Plateaued? --> Add dictionary entries or seed inputs
Key principle: Use the corpus generated after each fuzzing campaign to calculate coverage, rather than real-time fuzzer statistics. This approach provides reproducible, comparable measurements across different fuzzing tools.
以下工作流是将覆盖率分析整合到fuzzing活动中的最佳实践:
[Fuzzing活动]
       |
       v
[生成Corpus]
       |
       v
[覆盖率分析]
       |
       +---> 覆盖率提升? --> 使用更大的corpus继续fuzzing
       |
       +---> 覆盖率下降? --> 修复harness或调查SUT变更
       |
       +---> 覆盖率停滞? --> 添加字典条目或种子输入
核心原则:使用每次fuzzing活动生成的corpus来计算覆盖率,而非fuzzer的实时统计数据。这种方法可在不同fuzzing工具间提供可复现、可对比的测量结果。

Step-by-Step

分步指南

Step 1: Build with Coverage Instrumentation

步骤1:通过覆盖率插桩构建

Choose your instrumentation method based on toolchain:
LLVM/Clang (C/C++):
bash
clang++ -fprofile-instr-generate -fcoverage-mapping \
  -O2 -DNO_MAIN \
  main.cc harness.cc execute-rt.cc -o fuzz_exec
GCC (C/C++):
bash
g++ -ftest-coverage -fprofile-arcs \
  -O2 -DNO_MAIN \
  main.cc harness.cc execute-rt.cc -o fuzz_exec_gcov
Rust:
bash
rustup toolchain install nightly --component llvm-tools-preview
cargo +nightly fuzz coverage fuzz_target_1
根据工具链选择插桩方式:
LLVM/Clang(C/C++):
bash
clang++ -fprofile-instr-generate -fcoverage-mapping \
  -O2 -DNO_MAIN \
  main.cc harness.cc execute-rt.cc -o fuzz_exec
GCC(C/C++):
bash
g++ -ftest-coverage -fprofile-arcs \
  -O2 -DNO_MAIN \
  main.cc harness.cc execute-rt.cc -o fuzz_exec_gcov
Rust:
bash
rustup toolchain install nightly --component llvm-tools-preview
cargo +nightly fuzz coverage fuzz_target_1

Step 2: Create Execution Runtime (C/C++ only)

步骤2:创建执行运行时(仅C/C++)

For C/C++ projects, create a runtime that executes your corpus:
cpp
// execute-rt.cc
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdint.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

void load_file_and_test(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (file == NULL) {
        printf("Failed to open file: %s\n", filename);
        return;
    }

    fseek(file, 0, SEEK_END);
    long filesize = ftell(file);
    rewind(file);

    uint8_t *buffer = (uint8_t*) malloc(filesize);
    if (buffer == NULL) {
        printf("Failed to allocate memory for file: %s\n", filename);
        fclose(file);
        return;
    }

    long read_size = (long) fread(buffer, 1, filesize, file);
    if (read_size != filesize) {
        printf("Failed to read file: %s\n", filename);
        free(buffer);
        fclose(file);
        return;
    }

    LLVMFuzzerTestOneInput(buffer, filesize);

    free(buffer);
    fclose(file);
}

int main(int argc, char **argv) {
    if (argc != 2) {
        printf("Usage: %s <directory>\n", argv[0]);
        return 1;
    }

    DIR *dir = opendir(argv[1]);
    if (dir == NULL) {
        printf("Failed to open directory: %s\n", argv[1]);
        return 1;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_REG) {
            char filepath[1024];
            snprintf(filepath, sizeof(filepath), "%s/%s", argv[1], entry->d_name);
            load_file_and_test(filepath);
        }
    }

    closedir(dir);
    return 0;
}
对于C/C++项目,创建一个用于执行corpus的运行时:
cpp
// execute-rt.cc
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdint.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

void load_file_and_test(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (file == NULL) {
        printf("Failed to open file: %s\n", filename);
        return;
    }

    fseek(file, 0, SEEK_END);
    long filesize = ftell(file);
    rewind(file);

    uint8_t *buffer = (uint8_t*) malloc(filesize);
    if (buffer == NULL) {
        printf("Failed to allocate memory for file: %s\n", filename);
        fclose(file);
        return;
    }

    long read_size = (long) fread(buffer, 1, filesize, file);
    if (read_size != filesize) {
        printf("Failed to read file: %s\n", filename);
        free(buffer);
        fclose(file);
        return;
    }

    LLVMFuzzerTestOneInput(buffer, filesize);

    free(buffer);
    fclose(file);
}

int main(int argc, char **argv) {
    if (argc != 2) {
        printf("Usage: %s <directory>\n", argv[0]);
        return 1;
    }

    DIR *dir = opendir(argv[1]);
    if (dir == NULL) {
        printf("Failed to open directory: %s\n", argv[1]);
        return 1;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_REG) {
            char filepath[1024];
            snprintf(filepath, sizeof(filepath), "%s/%s", argv[1], entry->d_name);
            load_file_and_test(filepath);
        }
    }

    closedir(dir);
    return 0;
}

Step 3: Execute on Corpus

步骤3:在Corpus上执行

LLVM (C/C++):
bash
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec corpus/
GCC (C/C++):
bash
./fuzz_exec_gcov corpus/
Rust: Coverage data is automatically generated when running
cargo fuzz coverage
.
LLVM(C/C++):
bash
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec corpus/
GCC(C/C++):
bash
./fuzz_exec_gcov corpus/
Rust: 运行
cargo fuzz coverage
时会自动生成覆盖率数据。

Step 4: Process Coverage Data

步骤4:处理覆盖率数据

LLVM:
bash
undefined
LLVM:
bash
undefined

Merge raw profile data

Merge raw profile data

llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata
llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata

Generate text report

Generate text report

llvm-cov report ./fuzz_exec
-instr-profile=fuzz.profdata
-ignore-filename-regex='harness.cc|execute-rt.cc'
llvm-cov report ./fuzz_exec
-instr-profile=fuzz.profdata
-ignore-filename-regex='harness.cc|execute-rt.cc'

Generate HTML report

Generate HTML report

llvm-cov show ./fuzz_exec
-instr-profile=fuzz.profdata
-ignore-filename-regex='harness.cc|execute-rt.cc'
-format=html -output-dir fuzz_html/

**GCC with gcovr:**
```bash
llvm-cov show ./fuzz_exec
-instr-profile=fuzz.profdata
-ignore-filename-regex='harness.cc|execute-rt.cc'
-format=html -output-dir fuzz_html/

**GCC + gcovr:**
```bash

Install gcovr (via pip for latest version)

Install gcovr (via pip for latest version)

python3 -m venv venv source venv/bin/activate pip3 install gcovr
python3 -m venv venv source venv/bin/activate pip3 install gcovr

Generate report

Generate report

gcovr --gcov-executable "llvm-cov gcov"
--exclude harness.cc --exclude execute-rt.cc
--root . --html-details -o coverage.html

**Rust:**
```bash
gcovr --gcov-executable "llvm-cov gcov"
--exclude harness.cc --exclude execute-rt.cc
--root . --html-details -o coverage.html

**Rust:**
```bash

Install required tools

Install required tools

cargo install cargo-binutils rustfilt
cargo install cargo-binutils rustfilt

Create HTML generation script

Create HTML generation script

cat <<'EOF' > ./generate_html #!/bin/sh if [ $# -lt 1 ]; then echo "Error: Name of fuzz target is required." echo "Usage: $0 fuzz_target [sources...]" exit 1 fi FUZZ_TARGET="$1" shift SRC_FILTER="$@" TARGET=$(rustc -vV | sed -n 's|host: ||p') cargo +nightly cov -- show -Xdemangler=rustfilt
"target/$TARGET/coverage/$TARGET/release/$FUZZ_TARGET"
-instr-profile="fuzz/coverage/$FUZZ_TARGET/coverage.profdata"
-show-line-counts-or-regions -show-instantiations
-format=html -o fuzz_html/ $SRC_FILTER EOF chmod +x ./generate_html
cat <<'EOF' > ./generate_html #!/bin/sh if [ $# -lt 1 ]; then echo "Error: Name of fuzz target is required." echo "Usage: $0 fuzz_target [sources...]" exit 1 fi FUZZ_TARGET="$1" shift SRC_FILTER="$@" TARGET=$(rustc -vV | sed -n 's|host: ||p') cargo +nightly cov -- show -Xdemangler=rustfilt
"target/$TARGET/coverage/$TARGET/release/$FUZZ_TARGET"
-instr-profile="fuzz/coverage/$FUZZ_TARGET/coverage.profdata"
-show-line-counts-or-regions -show-instantiations
-format=html -o fuzz_html/ $SRC_FILTER EOF chmod +x ./generate_html

Generate HTML report

Generate HTML report

./generate_html fuzz_target_1 src/lib.rs
undefined
./generate_html fuzz_target_1 src/lib.rs
undefined

Step 5: Analyze Results

步骤5:分析结果

Review the coverage report to identify:
  • Uncovered code blocks: Areas that may need better seed inputs or dictionary entries
  • Magic value checks: Conditional statements with hardcoded values that block progress
  • Dead code: Functions that may not be reachable through your harness
  • Coverage changes: Compare against baseline to track improvements or regressions
查看覆盖率报告以识别:
  • 未覆盖的代码块:可能需要更好的种子输入或字典条目的区域
  • 魔术值检查:包含硬编码值的条件语句,会阻碍fuzzing进展
  • 死代码:无法通过harness触及的函数
  • 覆盖率变化:与基准对比,跟踪改进或退化情况

Common Patterns

常见模式

Pattern: Identifying Magic Values

模式:识别魔术值

Problem: Fuzzer cannot discover paths guarded by magic value checks.
Coverage reveals:
cpp
// Coverage shows this block is never executed
if (buf == 0x7F454C46) {  // ELF magic number
    // start parsing buf
}
Solution: Add magic values to dictionary file:
undefined
问题:Fuzzer无法发现受魔术值检查保护的路径。
覆盖率分析揭示:
cpp
// Coverage shows this block is never executed
if (buf == 0x7F454C46) {  // ELF magic number
    // start parsing buf
}
解决方案:将魔术值添加到字典文件:
undefined

magic.dict

magic.dict

"\x7F\x45\x4C\x46"
undefined
"\x7F\x45\x4C\x46"
undefined

Pattern: Handling Crashing Inputs

模式:处理崩溃输入

Problem: Coverage generation fails when corpus contains crashing inputs.
Before:
bash
./fuzz_exec corpus/  # Crashes on bad input, no coverage generated
After:
cpp
// Fork before executing to isolate crashes
int main(int argc, char **argv) {
    // ... directory opening code ...

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_REG) {
            pid_t pid = fork();
            if (pid == 0) {
                // Child process - crash won't affect parent
                char filepath[1024];
                snprintf(filepath, sizeof(filepath), "%s/%s", argv[1], entry->d_name);
                load_file_and_test(filepath);
                exit(0);
            } else {
                // Parent waits for child
                waitpid(pid, NULL, 0);
            }
        }
    }
}
问题:当corpus包含崩溃输入时,覆盖率生成失败。
改进前:
bash
./fuzz_exec corpus/  # Crashes on bad input, no coverage generated
改进后:
cpp
// Fork before executing to isolate crashes
int main(int argc, char **argv) {
    // ... directory opening code ...

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_REG) {
            pid_t pid = fork();
            if (pid == 0) {
                // Child process - crash won't affect parent
                char filepath[1024];
                snprintf(filepath, sizeof(filepath), "%s/%s", argv[1], entry->d_name);
                load_file_and_test(filepath);
                exit(0);
            } else {
                // Parent waits for child
                waitpid(pid, NULL, 0);
            }
        }
    }
}

Pattern: CMake Integration

模式:CMake集成

Use Case: Adding coverage builds to CMake projects.
cmake
project(FuzzingProject)
cmake_minimum_required(VERSION 3.0)
使用场景:为CMake项目添加覆盖率构建。
cmake
project(FuzzingProject)
cmake_minimum_required(VERSION 3.0)

Main binary

Main binary

add_executable(program main.cc)
add_executable(program main.cc)

Fuzzing binary

Fuzzing binary

add_executable(fuzz main.cc harness.cc) target_compile_definitions(fuzz PRIVATE NO_MAIN=1) target_compile_options(fuzz PRIVATE -g -O2 -fsanitize=fuzzer) target_link_libraries(fuzz -fsanitize=fuzzer)
add_executable(fuzz main.cc harness.cc) target_compile_definitions(fuzz PRIVATE NO_MAIN=1) target_compile_options(fuzz PRIVATE -g -O2 -fsanitize=fuzzer) target_link_libraries(fuzz -fsanitize=fuzzer)

Coverage execution binary

Coverage execution binary

add_executable(fuzz_exec main.cc harness.cc execute-rt.cc) target_compile_definitions(fuzz_exec PRIVATE NO_MAIN) target_compile_options(fuzz_exec PRIVATE -O2 -fprofile-instr-generate -fcoverage-mapping) target_link_libraries(fuzz_exec -fprofile-instr-generate)

Build:
```bash
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
cmake --build . --target fuzz_exec
add_executable(fuzz_exec main.cc harness.cc execute-rt.cc) target_compile_definitions(fuzz_exec PRIVATE NO_MAIN) target_compile_options(fuzz_exec PRIVATE -O2 -fprofile-instr-generate -fcoverage-mapping) target_link_libraries(fuzz_exec -fprofile-instr-generate)

构建:
```bash
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
cmake --build . --target fuzz_exec

Advanced Usage

高级用法

Tips and Tricks

技巧与窍门

TipWhy It Helps
Use LLVM 18+ with
-show-directory-coverage
Organizes large reports by directory structure instead of flat file list
Export to lcov format for better HTML
llvm-cov export -format=lcov
+
genhtml
provides cleaner per-file reports
Compare coverage across campaignsStore
.profdata
files with timestamps to track progress over time
Filter harness code from reportsUse
-ignore-filename-regex
to focus on SUT coverage only
Automate coverage in CI/CDGenerate coverage reports automatically after scheduled fuzzing runs
Use gcovr 5.1+ for Clang 14+Older gcovr versions have compatibility issues with recent LLVM
技巧作用
使用LLVM 18+及
-show-directory-coverage
参数
按目录结构组织大型报告,而非扁平文件列表
导出为lcov格式以生成更优HTML报告
llvm-cov export -format=lcov
+
genhtml
可提供更清晰的单文件报告
跨活动对比覆盖率
.profdata
文件添加时间戳存储,以跟踪长期进度
从报告中过滤harness代码使用
-ignore-filename-regex
参数聚焦于SUT的覆盖率
在CI/CD中自动化覆盖率分析定期fuzzing运行后自动生成覆盖率报告
为Clang 14+使用gcovr 5.1+旧版gcovr与新版LLVM存在兼容性问题

Incremental Coverage Updates

增量覆盖率更新

GCC's gcov instrumentation incrementally updates
.gcda
files across multiple runs. This is useful for tracking coverage as you add test cases:
bash
undefined
GCC的gcov插桩可在多次运行中增量更新
.gcda
文件。这在添加测试用例时跟踪覆盖率非常有用:
bash
undefined

First run

First run

./fuzz_exec_gcov corpus_batch_1/ gcovr --html coverage_v1.html
./fuzz_exec_gcov corpus_batch_1/ gcovr --html coverage_v1.html

Second run (adds to existing coverage)

Second run (adds to existing coverage)

./fuzz_exec_gcov corpus_batch_2/ gcovr --html coverage_v2.html
./fuzz_exec_gcov corpus_batch_2/ gcovr --html coverage_v2.html

Start fresh

Start fresh

gcovr --delete # Remove .gcda files ./fuzz_exec_gcov corpus/
undefined
gcovr --delete # Remove .gcda files ./fuzz_exec_gcov corpus/
undefined

Handling Large Codebases

处理大型代码库

For projects with hundreds of source files:
  1. Filter by prefix: Only generate reports for relevant directories
    bash
    llvm-cov show ./fuzz_exec -instr-profile=fuzz.profdata /path/to/src/
  2. Use directory coverage: Group by directory to reduce clutter (LLVM 18+)
    bash
    llvm-cov show -show-directory-coverage -format=html -output-dir html/
  3. Generate JSON for programmatic analysis:
    bash
    llvm-cov export -format=lcov > coverage.json
对于包含数百个源文件的项目:
  1. 按前缀过滤:仅为相关目录生成报告
    bash
    llvm-cov show ./fuzz_exec -instr-profile=fuzz.profdata /path/to/src/
  2. 使用目录覆盖率:按目录分组以减少混乱(LLVM 18+)
    bash
    llvm-cov show -show-directory-coverage -format=html -output-dir html/
  3. 导出为JSON以进行程序化分析
    bash
    llvm-cov export -format=lcov > coverage.json

Differential Coverage

差异覆盖率

Compare coverage between two fuzzing campaigns:
bash
undefined
对比两次fuzzing活动的覆盖率:
bash
undefined

Campaign 1

Campaign 1

LLVM_PROFILE_FILE=campaign1.profraw ./fuzz_exec corpus1/ llvm-profdata merge -sparse campaign1.profraw -o campaign1.profdata
LLVM_PROFILE_FILE=campaign1.profraw ./fuzz_exec corpus1/ llvm-profdata merge -sparse campaign1.profraw -o campaign1.profdata

Campaign 2

Campaign 2

LLVM_PROFILE_FILE=campaign2.profraw ./fuzz_exec corpus2/ llvm-profdata merge -sparse campaign2.profraw -o campaign2.profdata
LLVM_PROFILE_FILE=campaign2.profraw ./fuzz_exec corpus2/ llvm-profdata merge -sparse campaign2.profraw -o campaign2.profdata

Compare

Compare

llvm-cov show ./fuzz_exec
-instr-profile=campaign2.profdata
-instr-profile=campaign1.profdata
-show-line-counts-or-regions
undefined
llvm-cov show ./fuzz_exec
-instr-profile=campaign2.profdata
-instr-profile=campaign1.profdata
-show-line-counts-or-regions
undefined

Anti-Patterns

反模式

Anti-PatternProblemCorrect Approach
Using fuzzer-reported coverage for comparisonsDifferent fuzzers calculate coverage differently, making cross-tool comparison meaninglessUse dedicated coverage tools (llvm-cov, gcovr) for reproducible measurements
Generating coverage with optimizations
-O3
optimizations can eliminate code, making coverage misleading
Use
-O2
or
-O0
for coverage builds
Not filtering harness codeHarness coverage inflates numbers and obscures SUT coverageUse
-ignore-filename-regex
or
--exclude
to filter harness files
Mixing LLVM and GCC instrumentationIncompatible formats cause parsing failuresStick to one toolchain for coverage builds
Ignoring crashing inputsCrashes prevent coverage generation, hiding real coverage dataFix crashes first, or use process forking to isolate them
Not tracking coverage over timeOne-time coverage checks miss regressions and improvementsStore coverage data with timestamps and track trends
反模式问题正确做法
使用fuzzer报告的覆盖率进行对比不同fuzzer的覆盖率计算方式不同,跨工具对比无意义使用专用覆盖率工具(llvm-cov、gcovr)获取可复现的测量结果
带优化选项生成覆盖率
-O3
优化会消除代码,导致覆盖率结果失真
覆盖率构建使用
-O2
-O0
未过滤harness代码Harness的覆盖率会夸大数值并掩盖SUT的覆盖率使用
-ignore-filename-regex
--exclude
参数过滤harness文件
混合使用LLVM与GCC插桩格式不兼容会导致解析失败统一使用同一工具链进行覆盖率构建
忽略崩溃输入崩溃会阻止覆盖率生成,隐藏真实的覆盖率数据先修复崩溃,或使用进程分叉隔离崩溃
未跟踪长期覆盖率变化单次覆盖率检查无法发现退化与改进为覆盖率数据添加时间戳并跟踪趋势

Tool-Specific Guidance

工具特定指南

libFuzzer

libFuzzer

libFuzzer uses LLVM's SanitizerCoverage by default for guiding fuzzing, but you need separate instrumentation for generating reports.
Build for coverage:
bash
clang++ -fprofile-instr-generate -fcoverage-mapping \
  -O2 -DNO_MAIN \
  main.cc harness.cc execute-rt.cc -o fuzz_exec
Execute corpus and generate report:
bash
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec corpus/
llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata
llvm-cov show ./fuzz_exec -instr-profile=fuzz.profdata -format=html -output-dir html/
Integration tips:
  • Don't use
    -fsanitize=fuzzer
    for coverage builds (it conflicts with profile instrumentation)
  • Reuse the same harness function (
    LLVMFuzzerTestOneInput
    ) with a different main function
  • Use the
    -ignore-filename-regex
    flag to exclude harness code from coverage reports
  • Consider using llvm-cov's
    -show-instantiation
    flag for template-heavy C++ code
libFuzzer默认使用SanitizerCoverage提供反馈;覆盖率分析用于评估harness的有效性。
为覆盖率构建:
bash
clang++ -fprofile-instr-generate -fcoverage-mapping \
  -O2 -DNO_MAIN \
  main.cc harness.cc execute-rt.cc -o fuzz_exec
执行corpus并生成报告:
bash
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec corpus/
llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata
llvm-cov show ./fuzz_exec -instr-profile=fuzz.profdata -format=html -output-dir html/
集成技巧:
  • 覆盖率构建不要使用
    -fsanitize=fuzzer
    (与profile插桩冲突)
  • 复用相同的harness函数(
    LLVMFuzzerTestOneInput
    ),搭配不同的主函数
  • 使用
    -ignore-filename-regex
    参数从覆盖率报告中排除harness代码
  • 对于重度使用模板的C++代码,可考虑使用llvm-cov的
    -show-instantiation
    参数

AFL++

AFL++

AFL++ provides its own coverage feedback mechanism, but for detailed reports use standard LLVM/GCC tools.
Build for coverage with LLVM:
bash
clang++ -fprofile-instr-generate -fcoverage-mapping \
  -O2 main.cc harness.cc execute-rt.cc -o fuzz_exec
Build for coverage with GCC:
bash
AFL_USE_ASAN=0 afl-gcc -ftest-coverage -fprofile-arcs \
  main.cc harness.cc execute-rt.cc -o fuzz_exec_gcov
Execute and generate report:
bash
undefined
AFL++提供自有覆盖率反馈机制,但如需详细报告需使用标准LLVM/GCC工具。
使用LLVM进行覆盖率构建:
bash
clang++ -fprofile-instr-generate -fcoverage-mapping \
  -O2 main.cc harness.cc execute-rt.cc -o fuzz_exec
使用GCC进行覆盖率构建:
bash
AFL_USE_ASAN=0 afl-gcc -ftest-coverage -fprofile-arcs \
  main.cc harness.cc execute-rt.cc -o fuzz_exec_gcov
执行并生成报告:
bash
undefined

LLVM approach

LLVM approach

LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec afl_output/queue/ llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata llvm-cov report ./fuzz_exec -instr-profile=fuzz.profdata
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec afl_output/queue/ llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata llvm-cov report ./fuzz_exec -instr-profile=fuzz.profdata

GCC approach

GCC approach

./fuzz_exec_gcov afl_output/queue/ gcovr --html-details -o coverage.html

**Integration tips:**
- Don't use AFL++'s instrumentation (`afl-clang-fast`) for coverage builds
- Use standard compilers with coverage flags instead
- AFL++'s `queue/` directory contains your corpus
- AFL++'s built-in coverage statistics are useful for real-time monitoring but not for detailed analysis
./fuzz_exec_gcov afl_output/queue/ gcovr --html-details -o coverage.html

**集成技巧:**
- 覆盖率构建不要使用AFL++的插桩(`afl-clang-fast`)
- 改用标准编译器搭配覆盖率标志
- AFL++的`queue/`目录包含你的corpus
- AFL++的内置覆盖率统计适用于实时监控,但不适用于详细分析

cargo-fuzz (Rust)

cargo-fuzz(Rust)

cargo-fuzz provides built-in coverage generation using LLVM tools.
Install prerequisites:
bash
rustup toolchain install nightly --component llvm-tools-preview
cargo install cargo-binutils rustfilt
Generate coverage data:
bash
cargo +nightly fuzz coverage fuzz_target_1
Create HTML report script:
bash
cat <<'EOF' > ./generate_html
#!/bin/sh
FUZZ_TARGET="$1"
shift
SRC_FILTER="$@"
TARGET=$(rustc -vV | sed -n 's|host: ||p')
cargo +nightly cov -- show -Xdemangler=rustfilt \
  "target/$TARGET/coverage/$TARGET/release/$FUZZ_TARGET" \
  -instr-profile="fuzz/coverage/$FUZZ_TARGET/coverage.profdata" \
  -show-line-counts-or-regions -show-instantiations \
  -format=html -o fuzz_html/ $SRC_FILTER
EOF
chmod +x ./generate_html
Generate report:
bash
./generate_html fuzz_target_1 src/lib.rs
Integration tips:
  • Always use the nightly toolchain for coverage
  • The
    -Xdemangler=rustfilt
    flag makes function names readable
  • Filter by source files (e.g.,
    src/lib.rs
    ) to focus on crate code
  • Use
    -show-line-counts-or-regions
    and
    -show-instantiations
    for better Rust-specific output
  • Corpus is located in
    fuzz/corpus/<target>/
cargo-fuzz使用LLVM工具提供内置的覆盖率生成功能。
安装前置依赖:
bash
rustup toolchain install nightly --component llvm-tools-preview
cargo install cargo-binutils rustfilt
生成覆盖率数据:
bash
cargo +nightly fuzz coverage fuzz_target_1
创建HTML报告脚本:
bash
cat <<'EOF' > ./generate_html
#!/bin/sh
FUZZ_TARGET="$1"
shift
SRC_FILTER="$@"
TARGET=$(rustc -vV | sed -n 's|host: ||p')
cargo +nightly cov -- show -Xdemangler=rustfilt \
  "target/$TARGET/coverage/$TARGET/release/$FUZZ_TARGET" \
  -instr-profile="fuzz/coverage/$FUZZ_TARGET/coverage.profdata" \
  -show-line-counts-or-regions -show-instantiations \
  -format=html -o fuzz_html/ $SRC_FILTER
EOF
chmod +x ./generate_html
生成报告:
bash
./generate_html fuzz_target_1 src/lib.rs
集成技巧:
  • 覆盖率分析始终使用nightly工具链
  • -Xdemangler=rustfilt
    参数可让函数名称更易读
  • 按源文件过滤(如
    src/lib.rs
    )以聚焦于 crate 代码
  • 使用
    -show-line-counts-or-regions
    -show-instantiations
    参数获取更适合Rust的输出
  • Corpus位于
    fuzz/corpus/<target>/
    目录

honggfuzz

honggfuzz

honggfuzz works with standard LLVM/GCC coverage instrumentation.
Build for coverage:
bash
undefined
honggfuzz可与标准LLVM/GCC覆盖率插桩配合使用。
为覆盖率构建:
bash
undefined

Use standard compiler, not honggfuzz compiler

Use standard compiler, not honggfuzz compiler

clang -fprofile-instr-generate -fcoverage-mapping
-O2 harness.c execute-rt.c -o fuzz_exec

**Execute corpus:**
```bash
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec honggfuzz_workspace/
Integration tips:
  • Don't use
    hfuzz-clang
    for coverage builds
  • honggfuzz corpus is typically in a workspace directory
  • Use the same LLVM workflow as libFuzzer
clang -fprofile-instr-generate -fcoverage-mapping
-O2 harness.c execute-rt.c -o fuzz_exec

**执行corpus:**
```bash
LLVM_PROFILE_FILE=fuzz.profraw ./fuzz_exec honggfuzz_workspace/
集成技巧:
  • 覆盖率构建不要使用
    hfuzz-clang
  • honggfuzz的corpus通常位于工作区目录
  • 使用与libFuzzer相同的LLVM工作流

Troubleshooting

故障排除

IssueCauseSolution
error: no profile data available
Profile wasn't generated or wrong pathVerify
LLVM_PROFILE_FILE
was set and
.profraw
file exists
Failed to load coverage
Mismatch between binary and profile dataRebuild binary with same flags used during execution
Coverage reports show 0%Wrong binary used for report generationUse the instrumented binary, not the fuzzing binary
no_working_dir_found
error (gcovr)
.gcda
files in unexpected location
Add
--gcov-ignore-errors=no_working_dir_found
flag
Crashes prevent coverage generationCorpus contains crashing inputsFilter crashes or use forking approach to isolate failures
Coverage decreases after harness changeHarness now skips certain code pathsReview harness logic; may need to support more input formats
HTML report is flat file listUsing older LLVM versionUpgrade to LLVM 18+ and use
-show-directory-coverage
incompatible instrumentation
Mixing LLVM and GCC coverageRebuild everything with same toolchain
问题原因解决方案
error: no profile data available
未生成profile或路径错误确认已设置
LLVM_PROFILE_FILE
.profraw
文件存在
Failed to load coverage
二进制文件与profile数据不匹配使用执行时相同的标志重新构建二进制文件
覆盖率报告显示0%生成报告时使用了错误的二进制文件使用带插桩的二进制文件,而非fuzzing二进制文件
no_working_dir_found
错误(gcovr)
.gcda
文件位置不符合预期
添加
--gcov-ignore-errors=no_working_dir_found
参数
崩溃阻止覆盖率生成Corpus包含崩溃输入过滤崩溃输入,或使用分叉方法隔离故障
修改harness后覆盖率下降Harness现在跳过了某些代码路径检查harness逻辑;可能需要支持更多输入格式
HTML报告为扁平文件列表使用了旧版LLVM升级到LLVM 18+并使用
-show-directory-coverage
参数
incompatible instrumentation
混合使用了LLVM与GCC覆盖率使用同一工具链重新构建所有内容

Related Skills

相关技能

Tools That Use This Technique

使用该技术的工具

SkillHow It Applies
libfuzzerUses SanitizerCoverage for feedback; coverage analysis evaluates harness effectiveness
aflppUses edge coverage for feedback; detailed analysis requires separate instrumentation
cargo-fuzzBuilt-in
cargo fuzz coverage
command for Rust projects
honggfuzzUses edge coverage; analyze with standard LLVM/GCC tools
技能应用方式
libfuzzer使用SanitizerCoverage提供反馈;覆盖率分析用于评估harness有效性
aflpp使用边缘覆盖率提供反馈;详细分析需要单独的插桩
cargo-fuzz为Rust项目提供内置的
cargo fuzz coverage
命令
honggfuzz使用边缘覆盖率;通过标准LLVM/GCC工具进行分析

Related Techniques

相关技术

SkillRelationship
fuzz-harness-writingCoverage reveals which code paths harness reaches; guides harness improvements
fuzzing-dictionariesCoverage identifies magic value checks that need dictionary entries
corpus-managementCoverage analysis helps curate corpora by identifying redundant test cases
sanitizersCoverage helps verify sanitizer-instrumented code is actually executed
技能关联关系
fuzz-harness-writing覆盖率分析揭示harness触及的代码路径;指导harness改进
fuzzing-dictionaries覆盖率分析识别需要字典条目的魔术值检查
corpus-management覆盖率分析帮助整理corpus,识别冗余测试用例
sanitizers覆盖率分析帮助验证带sanitizer插桩的代码是否被实际执行

Resources

资源

Key External Resources

关键外部资源

LLVM Source-Based Code Coverage Comprehensive guide to LLVM's profile instrumentation, including advanced features like branch coverage, region coverage, and integration with existing build systems. Covers compiler flags, runtime behavior, and profile data formats.
llvm-cov Command Guide Detailed CLI reference for llvm-cov commands including
show
,
report
, and
export
. Documents all filtering options, output formats, and integration with llvm-profdata.
gcovr Documentation Complete guide to gcovr tool for generating coverage reports from gcov data. Covers HTML themes, filtering options, multi-directory projects, and CI/CD integration patterns.
SanitizerCoverage Documentation Low-level documentation for LLVM's SanitizerCoverage instrumentation. Explains inline 8-bit counters, PC tables, and how fuzzers use coverage feedback for guidance.
On the Evaluation of Fuzzer Performance Research paper examining limitations of coverage as a fuzzing performance metric. Argues for more nuanced evaluation methods beyond simple code coverage percentages.
LLVM Source-Based Code Coverage LLVM profile插桩的全面指南,包括分支覆盖率、区域覆盖率等高级功能,以及与现有构建系统的集成。涵盖编译器标志、运行时行为和profile数据格式。
llvm-cov Command Guide llvm-cov命令的详细CLI参考,包括
show
report
export
子命令。记录了所有过滤选项、输出格式以及与llvm-profdata的集成。
gcovr Documentation 使用gcov数据生成覆盖率报告的gcovr工具完整指南。涵盖HTML主题、过滤选项、多目录项目和CI/CD集成模式。
SanitizerCoverage Documentation LLVM SanitizerCoverage插桩的底层文档。解释了内联8位计数器、PC表以及fuzzer如何使用覆盖率反馈进行引导。
On the Evaluation of Fuzzer Performance 研究论文,探讨了覆盖率作为fuzzing性能指标的局限性。主张采用更细致的评估方法,而非简单的代码覆盖率百分比。

Video Resources

视频资源

Not applicable - coverage analysis is primarily a tooling and workflow topic best learned through documentation and hands-on practice.
不适用 - 覆盖率分析主要是工具和工作流主题,通过文档和实践操作学习效果最佳。