To rerun a libFuzzer crash as a regression check, pass the crash file path to the fuzz binary instead of a corpus directory. libFuzzer then executes just those inputs without fuzzing. Keep the file in the repository and run it in CI, so the bug cannot quietly return.
What happens when you pass files
The LLVM libFuzzer documentation says that when you pass files instead of directories, libFuzzer reruns those inputs without fuzzing. That is exactly what a regression check needs: one input, one execution, a pass or a crash.
Crash inputs are typically named crash- followed by a SHA1 of the content. The -artifact_prefix option sets where those files are written, and -exact_artifact_path overrides the prefix and drops the checksum from the filename. If you want stable names in a repo, give each file a name that carries the bug ticket, not the hash.
Steps
- Reproduce. Run the fuzz binary with the crash file and confirm it fails the same way.
- Minimize. The
-minimize_crashflag shrinks a crash input. The docs say to combine it with-runs=Nor-max_total_time=Nto limit attempts. A smaller input is easier to read in a code review. - Fix the bug.
- Rerun the same file. It should now exit cleanly.
- Commit the file into a regression directory such as
tests/fuzz-regressions/, with a name that points to the ticket. - Add a CI step that runs the fuzz binary over that directory and fails the build on any crash.
A sketch of the command, shown as code because the exact binary name is yours:
./my_fuzzer tests/fuzz-regressions/
The same form works for a single file path. Because no fuzzing happens, the step finishes in seconds and fits a pull request check.
Keep regressions separate from the working corpus
Your working corpus grows as the fuzzer finds new coverage. The regression directory is a fixed set of past failures. Mixing them hides the purpose of each.
The docs describe -merge=1 for corpus minimization: inputs from the second and later directories that trigger new coverage are copied into the first, and the example uses a new empty directory as the first argument. Use that to shrink the working corpus. Do not run it over the regression directory, because a regression input that adds no new coverage would be dropped, and you want to keep it anyway.
What to review in a pull request
| Check | Why |
|---|---|
| The crash file is in the change | Proves the fix was tested against the real trigger |
| The file is small | Easier to read, faster to run |
| The CI step covers the directory | A file nobody runs is not a test |
| The commit message names the ticket | Future readers know why it exists |
Treat any crash file that came from a customer or from untrusted traffic carefully. It may contain data you should not store. Strip or replace sensitive content before committing, and confirm the bug still reproduces afterwards.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how a failing check blocks a merge, then picture your crash replay step in that spot.
Safeguard can enforce this kind of rule as a gate on pull requests, alongside dependency and SBOM scanning. The free scan at gold.safeguard.sh/scan is a way to see what that looks like on your own repository.
Quick check
Does rerunning a crash file take long?
No. libFuzzer runs the given inputs once without fuzzing, so it usually finishes in seconds.
Should the file be minimized first?
Preferably. Use -minimize_crash with a run or time limit, then confirm the smaller file still fails before the fix.
Sources
Last reviewed: 19 October 2026.