Hello, developers.
I’m trying to use Fuzz Introspector’s RuntimeCoverageAnalysis on an lcms target built with the FI-provided LLVM toolchain and llvm-cov show -show-branches=count. The HTML report is generated correctly and other analyses work (OptimalTargets, AnnotatedCFG, etc.), but branch blockers are never found:
- The log shows:
overlay_calltree_with_coverage: [+] found 0 branch blockers.
- The generated
branch-blockers.json contains an empty list:
{"cms_transform_fuzzer": []}
This happens even though the cms_transform_fuzzer.covreport file clearly contains many Branch (line:col): [True: X, False: Y] entries from llvm-cov.
Environment
- Fuzz Introspector: built from
main
- FI checkout path:
/home/fa1c4/Desktop/fuzz-introspector
- FI layout:
pwd && ls
/home/fa1c4/Desktop/fuzz-introspector
api_test.sh build_all.sh ci_checks.sh CODE_OF_CONDUCT.md Dockerfile LICENSE README.md scripts src tools
build CHARTER.pdf code_checks.sh doc frontends oss_fuzz_integration requirements.txt SECURITY.md tests
- LLVM/clang: using the toolchain built by FI (from
build/llvm-build/bin):
FI_ROOT=/home/fa1c4/Desktop/fuzz-introspector
CLANG_BASE="$FI_ROOT/build/llvm-build/bin"
export PATH="$CLANG_BASE:$PATH"
export AR="$CLANG_BASE/llvm-ar"
export RANLIB="$CLANG_BASE/llvm-ranlib"
export CC="$CLANG_BASE/clang"
export CXX="$CLANG_BASE/clang++"
❯ $FI_ROOT/build/llvm-build/bin/clang --version
clang version 21.1.0-rc3 (https://github.com/llvm/llvm-project/ 6096d35ea93c75f648a253a00775b4d74915c819)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/fa1c4/Desktop/fuzz-introspector/build/llvm-build/bin
❯ $FI_ROOT/build/llvm-build/bin/llvm-cov --version
LLVM (http://llvm.org/):
LLVM version 21.1.0-rc3
Optimized build.
Target: lcms (Little CMS) + FI
Build script
This is the script I use to build lcms with FI instrumentation and coverage:
#!/usr/bin/env bash
# FI_build_lcms.sh: build lcms target with Fuzz Introspector + coverage
set -euo pipefail
set -x
ROOT=$(pwd)
# Path to Fuzz Introspector checkout (adjust if different)
FI_ROOT=/home/fa1c4/Desktop/fuzz-introspector
CLANG_BASE="$FI_ROOT/build/llvm-build/bin"
export PATH="$CLANG_BASE:$PATH"
export AR="$CLANG_BASE/llvm-ar"
export RANLIB="$CLANG_BASE/llvm-ranlib"
export CC="$CLANG_BASE/clang"
export CXX="$CLANG_BASE/clang++"
# Common compile flags: hardening + coverage + FI instrumentation
common_cflags="-O2 -Wall -Wextra -D_FORTIFY_SOURCE=2 \
-fstack-protector-strong -fstack-clash-protection \
-fPIE -pie -fPIC -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-z,separate-code \
-fprofile-instr-generate -fcoverage-mapping \
-fsanitize=fuzzer-no-link -g -flto"
export CFLAGS="$common_cflags"
export CXXFLAGS="$common_cflags -std=c++11"
# Link with libFuzzer (FI uses this)
export LIB_FUZZING_ENGINE="-fsanitize=fuzzer"
# Enable Fuzz Introspector instrumentation in clang
export FUZZ_INTROSPECTOR=1
# Clean previous build
[ -d ./lcms ] && rm -rf lcms
[ -d ./out ] && rm -rf out
mkdir ./out
# Get lcms + fuzzer harness
git clone https://github.com/mm2/Little-CMS.git lcms
cp /home/fa1c4/Desktop/lcms_cov/ext1_cms_transform_fuzzer.cc ./lcms/cms_transform_fuzzer.cc
export SRC="$ROOT/lcms"
export OUT="$ROOT/out"
cd "$SRC"
./autogen.sh
./configure
# Optional: use gold for faster LTO, like jsoncpp FI example
export LDFLAGS="-fuse-ld=gold"
# Keep bear so you still get compile_commands.json if you need it
bear -- make -j"$(nproc)" clean
bear -- make -j"$(nproc)" all
# Build FI-instrumented libFuzzer target
$CXX $CXXFLAGS cms_transform_fuzzer.cc -I include/ src/.libs/liblcms2.a \
$LIB_FUZZING_ENGINE -o "$OUT/cms_transform_fuzzer"
echo "[+] Done Building (FI): $OUT/cms_transform_fuzzer"
Steps to reproduce
1. Build lcms target with FI
cd /home/fa1c4/Desktop/fuzz-introspector
# (Build FI + LLVM toolchain via repo scripts, then:)
cd /home/fa1c4/Desktop
./FI_build_lcms.sh
Relevant build log (tail):
[Log level 1] : 09:37:14 : Fuzz introspector is running
[Log level 2] : 09:37:14 : Using default configuration
[Log level 1] : 09:37:14 : Running introspector on ld-temp.o
[Log level 1] : 09:37:14 : This is a fuzzer, performing analysis
[Log level 1] : 09:37:14 : Logging next yaml tile to fuzzerLogFile-0-1rgEopXyfV.data.yaml
[Log level 1] : 09:37:14 : Wrapping all functions
[Log level 1] : 09:37:15 : Ended wrapping all functions
[Log level 1] : 09:37:15 : Finished introspector module
[+] Done Building (FI): /home/fa1c4/Desktop/lcms_cov/out/cms_transform_fuzzer
2. Run the fuzzer once to collect coverage
cd /home/fa1c4/Desktop/lcms_cov/out
LLVM_PROFILE_FILE=cms_transform_fuzzer.profraw \
./cms_transform_fuzzer ../corpus -runs=0
Example output:
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1129346469
INFO: Loaded 1 modules (7552 inline 8-bit counters): 7552 [0x..., 0x...),
INFO: Loaded 1 PC tables (7552 PCs): 7552 [0x...,0x...),
INFO: 1058 files found in ../corpus
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 34798 bytes
INFO: seed corpus: files: 1058 min: 2b max: 34798b total: 1768529b rss: 29Mb
#512 pulse cov: 1284 ft: 2874 corp: 344/252Kb exec/s: 170 rss: 211Mb
#1024 pulse cov: 1433 ft: 3359 corp: 593/639Kb exec/s: 170 rss: 211Mb
#1059 INITED cov: 1436 ft: 3377 corp: 607/811Kb exec/s: 176 rss: 211Mb
#1059 DONE cov: 1436 ft: 3377 corp: 607/811Kb lim: 32548 exec/s: 176 rss: 211Mb
Done 1059 runs in 6 second(s)
3. Generate coverage report with llvm-cov
LLVM_BIN=/home/fa1c4/Desktop/fuzz-introspector/build/llvm-build/bin
$LLVM_BIN/llvm-profdata merge -sparse cms_transform_fuzzer.profraw \
-o cms_transform_fuzzer.profdata
$LLVM_BIN/llvm-cov show \
-instr-profile=cms_transform_fuzzer.profdata \
-object=./cms_transform_fuzzer \
-show-branches=count \
-line-coverage-gt=0 \
> cms_transform_fuzzer.covreport
At this point cms_transform_fuzzer.covreport contains many Branch (line:col): [True: X, False: Y] lines (and of course line coverage entries).
4. Copy FI raw data
cp ../lcms/fuzzerLogFile-0-*.data* .
ls
cms_transform_fuzzer cms_transform_fuzzer.profraw fuzzerLogFile-0-1rgEopXyfV.data.debug_all_globals fuzzerLogFile-0-1rgEopXyfV.data.yaml
cms_transform_fuzzer.covreport fuzzerLogFile-0-1rgEopXyfV.data fuzzerLogFile-0-1rgEopXyfV.data.debug_all_types
cms_transform_fuzzer.profdata fuzzerLogFile-0-1rgEopXyfV.data.debug_all_functions fuzzerLogFile-0-1rgEopXyfV.data.debug_info
5. Correlate binaries to FI logs
python /home/fa1c4/Desktop/fuzz-introspector/src/main.py \
correlate --binaries-dir=.
2025-12-09 09:41:09.143 INFO cli - main: Running fuzz introspector post-processing
2025-12-09 09:41:09.144 INFO utils - scan_executables_for_fuzz_introspector_logs: File: ./cms_transform_fuzzer is executable
2025-12-09 09:41:09.304 INFO utils - scan_executables_for_fuzz_introspector_logs: Found match fuzzerLogFile-0-1rgEopXyfV
2025-12-09 09:41:09.306 INFO commands - correlate_binaries_to_logs: Pairings: [{'executable_path': './cms_transform_fuzzer', 'fuzzer_log_file': 'fuzzerLogFile-0-1rgEopXyfV'}]
2025-12-09 09:41:09.308 INFO cli - main: Ending fuzz introspector post-processing
SRC="/home/fa1c4/Desktop/lcms_cov/lcms" QT_QPA_PLATFORM=offscreen \
python /home/fa1c4/Desktop/fuzz-introspector/src/main.py report \
--target-dir=. \
--correlation-file=./exe_to_fuzz_introspector_logs.yaml \
--analyses OptimalTargets RuntimeCoverageAnalysis \
FuzzEngineInputAnalysis AnnotatedCFG \
--output-json OptimalTargets RuntimeCoverageAnalysis \
FuzzEngineInputAnalysis AnnotatedCFG
...
2025-12-09 09:41:17.689 INFO fuzzer_profile - accummulate_profile: cms_transform_fuzzer: loading coverage
2025-12-09 09:41:17.690 INFO fuzzer_profile - _load_coverage: Loading coverage of type c-cpp
2025-12-09 09:41:17.690 INFO code_coverage - load_llvm_coverage: Loading LLVM coverage for target cms_transform_fuzzer
2025-12-09 09:41:17.691 INFO code_coverage - load_llvm_coverage: Found 1 coverage reports
2025-12-09 09:41:17.691 INFO code_coverage - load_llvm_coverage: Using the following coverages ['./cms_transform_fuzzer.covreport']
2025-12-09 09:41:17.691 INFO code_coverage - load_llvm_coverage: Reading coverage report: ./cms_transform_fuzzer.covreport
...
2025-12-09 09:41:18.057 INFO utils - get_target_coverage_url: Extracting coverage for /covreport/linux -- cms_transform_fuzzer
2025-12-09 09:41:18.057 INFO analysis - overlay_calltree_with_coverage: Using coverage url: /covreport/linux
...
2025-12-09 09:41:18.111 INFO analysis - overlay_calltree_with_coverage: Updating branch complexities
2025-12-09 09:41:18.115 INFO analysis - overlay_calltree_with_coverage: [+] found 0 branch blockers.
7. branch-blockers.json content
cat branch-blockers.json
{"cms_transform_fuzzer": []}
Observed behavior
- FI clearly loads the
.covreport and uses it (the HTML report shows line coverage; OptimalTargets etc. work).
- However,
RuntimeCoverageAnalysis / branch blocker discovery always finds zero branch blockers:
2025-12-09 09:41:18.115 INFO analysis - overlay_calltree_with_coverage: [+] found 0 branch blockers.
- This is surprising because the
cms_transform_fuzzer.covreport file from llvm-cov show -show-branches=count does include many Branch (line:col): [True: X, False: Y] entries for the lcms code (e.g., Branch (7:7): [True: 2, False: 1.05k], etc.).
Expected behavior
Given that:
- The coverage report includes branch coverage (
Branch (line:col)),
- FI successfully sees the coverage file and uses it,
- And
RuntimeCoverageAnalysis runs without errors,
…I expected:
overlay_calltree_with_coverage to detect some branch blockers in the lcms call tree, and
branch-blockers.json to contain a non-empty list of blocker locations for cms_transform_fuzzer (or at least to reflect branches present in the coverage report).
Questions / help requested
- Is there a known incompatibility between the current
llvm-cov show output (with -show-branches=count) and the branch coverage parsing in code_coverage.load_llvm_coverage?
- Is my
llvm-cov show invocation missing flags that FI expects for branch blocker analysis?
- Could
branch_cov_map be staying empty due to assumptions about the text format that have changed in newer LLVM versions?
Hello, developers.
I’m trying to use Fuzz Introspector’s
RuntimeCoverageAnalysison an lcms target built with the FI-provided LLVM toolchain andllvm-cov show -show-branches=count. The HTML report is generated correctly and other analyses work (OptimalTargets, AnnotatedCFG, etc.), but branch blockers are never found:overlay_calltree_with_coverage: [+] found 0 branch blockers.branch-blockers.jsoncontains an empty list:{"cms_transform_fuzzer": []}This happens even though the
cms_transform_fuzzer.covreportfile clearly contains manyBranch (line:col): [True: X, False: Y]entries fromllvm-cov.Environment
main/home/fa1c4/Desktop/fuzz-introspectorbuild/llvm-build/bin):Target: lcms (Little CMS) + FI
Build script
This is the script I use to build lcms with FI instrumentation and coverage:
Steps to reproduce
1. Build lcms target with FI
Relevant build log (tail):
2. Run the fuzzer once to collect coverage
cd /home/fa1c4/Desktop/lcms_cov/out LLVM_PROFILE_FILE=cms_transform_fuzzer.profraw \ ./cms_transform_fuzzer ../corpus -runs=0Example output:
3. Generate coverage report with llvm-cov
At this point
cms_transform_fuzzer.covreportcontains manyBranch (line:col): [True: X, False: Y]lines (and of course line coverage entries).4. Copy FI raw data
5. Correlate binaries to FI logs
python /home/fa1c4/Desktop/fuzz-introspector/src/main.py \ correlate --binaries-dir=. 2025-12-09 09:41:09.143 INFO cli - main: Running fuzz introspector post-processing 2025-12-09 09:41:09.144 INFO utils - scan_executables_for_fuzz_introspector_logs: File: ./cms_transform_fuzzer is executable 2025-12-09 09:41:09.304 INFO utils - scan_executables_for_fuzz_introspector_logs: Found match fuzzerLogFile-0-1rgEopXyfV 2025-12-09 09:41:09.306 INFO commands - correlate_binaries_to_logs: Pairings: [{'executable_path': './cms_transform_fuzzer', 'fuzzer_log_file': 'fuzzerLogFile-0-1rgEopXyfV'}] 2025-12-09 09:41:09.308 INFO cli - main: Ending fuzz introspector post-processing SRC="/home/fa1c4/Desktop/lcms_cov/lcms" QT_QPA_PLATFORM=offscreen \ python /home/fa1c4/Desktop/fuzz-introspector/src/main.py report \ --target-dir=. \ --correlation-file=./exe_to_fuzz_introspector_logs.yaml \ --analyses OptimalTargets RuntimeCoverageAnalysis \ FuzzEngineInputAnalysis AnnotatedCFG \ --output-json OptimalTargets RuntimeCoverageAnalysis \ FuzzEngineInputAnalysis AnnotatedCFG ... 2025-12-09 09:41:17.689 INFO fuzzer_profile - accummulate_profile: cms_transform_fuzzer: loading coverage 2025-12-09 09:41:17.690 INFO fuzzer_profile - _load_coverage: Loading coverage of type c-cpp 2025-12-09 09:41:17.690 INFO code_coverage - load_llvm_coverage: Loading LLVM coverage for target cms_transform_fuzzer 2025-12-09 09:41:17.691 INFO code_coverage - load_llvm_coverage: Found 1 coverage reports 2025-12-09 09:41:17.691 INFO code_coverage - load_llvm_coverage: Using the following coverages ['./cms_transform_fuzzer.covreport'] 2025-12-09 09:41:17.691 INFO code_coverage - load_llvm_coverage: Reading coverage report: ./cms_transform_fuzzer.covreport ... 2025-12-09 09:41:18.057 INFO utils - get_target_coverage_url: Extracting coverage for /covreport/linux -- cms_transform_fuzzer 2025-12-09 09:41:18.057 INFO analysis - overlay_calltree_with_coverage: Using coverage url: /covreport/linux ... 2025-12-09 09:41:18.111 INFO analysis - overlay_calltree_with_coverage: Updating branch complexities 2025-12-09 09:41:18.115 INFO analysis - overlay_calltree_with_coverage: [+] found 0 branch blockers.7. branch-blockers.json content
cat branch-blockers.json {"cms_transform_fuzzer": []}Observed behavior
.covreportand uses it (the HTML report shows line coverage; OptimalTargets etc. work).RuntimeCoverageAnalysis/ branch blocker discovery always finds zero branch blockers:cms_transform_fuzzer.covreportfile fromllvm-cov show -show-branches=countdoes include manyBranch (line:col): [True: X, False: Y]entries for the lcms code (e.g.,Branch (7:7): [True: 2, False: 1.05k], etc.).Expected behavior
Given that:
Branch (line:col)),RuntimeCoverageAnalysisruns without errors,…I expected:
overlay_calltree_with_coverageto detect some branch blockers in the lcms call tree, andbranch-blockers.jsonto contain a non-empty list of blocker locations forcms_transform_fuzzer(or at least to reflect branches present in the coverage report).Questions / help requested
llvm-cov showoutput (with-show-branches=count) and the branch coverage parsing incode_coverage.load_llvm_coverage?llvm-cov showinvocation missing flags that FI expects for branch blocker analysis?branch_cov_mapbe staying empty due to assumptions about the text format that have changed in newer LLVM versions?