Compute differential coverage for fuzzer runs.
Project description
Differential Coverage
Absolute coverage numbers aren't painting the whole picture: Different fuzzers may reach the same total coverage, but cover different parts of the program. Fuzzer (A) may reach more coverage than fuzzer (B), but (B) may reach coverage that (A) doesn't reach — so it's still valuable.
Definition
Differential coverage is a measure for this. It was proposed by Leonelli et al. in their paper on TwinFuzz[^1]. This implementation relies on the formulas from the SBFT’25 Competition Report[^2]. There, Crump et al. define a fuzzers overall score compared to others as
$$ relscore(f,b,s,e) = \left|f_0\in F|e\notin \text{cov}(b,t,s)\forall t\in \text{trials}(f_0,b)\right| \times\frac {\left|{t\in \text{trials}(f,b)\ |\ e \in \text{cov}(b,t,s)}\right|} {\left|{t\in \text{trials}(f,b)\ |\ \text{cov}(b,t,s)\neq \emptyset}\right|} $$
The score of a fuzzer is then
$$ \text{score}(f,b,s)=\sum_{e\in E}\text{relscore}(f,b,s,e) $$
Explanation
This can be simplified to the following:
$$ \text{differential coverage}(f,e) = \text{number of fuzzers that never hit }e \times\frac {\text{number of trials of }f\text{ that hit }e} {\text{number of trials of }f\text{ with non-empty cov}} $$ $$ \text{score}(f) = \text{sum of differential coverage}(f,e)\text{ over all edges e} $$
Usage
Assume <input_dir> is a directory of directories for each fuzzer, where each fuzzer subdirectory contains coverage files for each trial. Currently, the output format of afl-showmap is supported (lines with edge_id:count, where we only care if count > 0).
PRs for other data formats welcome :)
Command Line Interface
differential_coverage <input_dir>
Example
tests/sample_coverage provides sample coverage data to explain differential coverage. It contains 3 fuzzers and 3 edges:
- Edge 1 is always hit by all fuzzers
- Edge 2 is always hit by
fuzzer_c, sometimes hit byfuzzer_a, and never hit byfuzzer_b - Edge 3 is always hit by
fuzzer_bandfuzzer_c, but only sometimes byfuzzer_a
differential_coverage tests/sample_coverage
then produces
fuzzer_c: 1.00
fuzzer_a: 0.50
fuzzer_b: 0.00
API
from pathlib import Path
from differential_coverage import (
calculate_scores_for_campaign,
calculate_differential_coverage_scores,
)
# From a campaign directory (one subdir per fuzzer, each with coverage files):
scores = read_campaign_and_calculate_score(Path("path/to/campaign_dir"))
# -> dict[str, float]: fuzzer name -> score
# From in-memory campaign data (fuzzer -> trial_id -> edge_id -> count):
campaign = {...} # dict[str, dict[str, dict[int, int]]]
scores = calculate_differential_coverage_scores(campaign)
# -> dict[str, float]: fuzzer name -> score
Installation
pip install .
Development
Install dev dependencies and set up the pre-commit hook (runs a couple of checks before committing):
pip install -e ".[dev]"
pre-commit install
[^1]: TWINFUZZ: Differential Testing of Video Hardware Acceleration Stacks, https://www.ndss-symposium.org/ndss-paper/twinfuzz-differential-testing-of-video-hardware-acceleration-stacks/
[^2]: SBFT’25 Competition Report — Fuzzing Track, https://ieeexplore.ieee.org/document/11086561
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file differential_coverage-0.1.0.tar.gz.
File metadata
- Download URL: differential_coverage-0.1.0.tar.gz
- Upload date:
- Size: 4.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2be8aab709cd5003d76b66524dee857d16e6e6a1adb11379425abc31cc40d17a
|
|
| MD5 |
83c37d5ea82656b706a4ede1786cbb17
|
|
| BLAKE2b-256 |
17b6e877b4405f6afadf952f3f6b7e5fd6588fbdb940206b0017eb6ae11d480e
|
Provenance
The following attestation bundles were made for differential_coverage-0.1.0.tar.gz:
Publisher:
ci.yml on riesentoaster/differential-coverage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
differential_coverage-0.1.0.tar.gz -
Subject digest:
2be8aab709cd5003d76b66524dee857d16e6e6a1adb11379425abc31cc40d17a - Sigstore transparency entry: 909599230
- Sigstore integration time:
-
Permalink:
riesentoaster/differential-coverage@621cdacf981fffa17a6bffc41b55982099e63b14 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/riesentoaster
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@621cdacf981fffa17a6bffc41b55982099e63b14 -
Trigger Event:
push
-
Statement type:
File details
Details for the file differential_coverage-0.1.0-py3-none-any.whl.
File metadata
- Download URL: differential_coverage-0.1.0-py3-none-any.whl
- Upload date:
- Size: 4.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9609f8828e1ba71889e1cbb7e66e37d4fbb2fe7244683ac97f6e243f57b3f56
|
|
| MD5 |
1a7ad9df4e66f140c4474fb0042c2c90
|
|
| BLAKE2b-256 |
567449b712fc51025bb69721a24bd3c9136993d80e6089ac75221501b28518eb
|
Provenance
The following attestation bundles were made for differential_coverage-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on riesentoaster/differential-coverage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
differential_coverage-0.1.0-py3-none-any.whl -
Subject digest:
f9609f8828e1ba71889e1cbb7e66e37d4fbb2fe7244683ac97f6e243f57b3f56 - Sigstore transparency entry: 909599233
- Sigstore integration time:
-
Permalink:
riesentoaster/differential-coverage@621cdacf981fffa17a6bffc41b55982099e63b14 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/riesentoaster
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@621cdacf981fffa17a6bffc41b55982099e63b14 -
Trigger Event:
push
-
Statement type: