External Tools Integration¶
Crucible's built-in triage handles replay and evidence contracts, while dedicated tools can add clustering and byte minimization. Their labels remain hypotheses until verified with process facts and target source.
CASR: Crash Analysis and Severity Rating¶
CASR provides automated crash clustering and heuristic severity labels for sanitizer-detected crashes. Check its current release requirements before installing.
Installation¶
# Install from crates.io (requires Rust toolchain)
cargo install casr --locked
# Verify
casr-libfuzzer --version
Triaging libFuzzer Crashes¶
Use casr-libfuzzer to process a crash directory against a harness binary:
casr-libfuzzer \
-i ./crashes/deep \
-o ./casr-out/deep \
-- ./harness/libfuzzer/crucible-libfuzzer-deep
This replays each crash file, captures the sanitizer output, and writes a .casrep JSON report per crash.
Clustering¶
Use casr-cluster to deduplicate and group crashes by stack similarity:
# Deduplicate
casr-cluster -d ./casr-out/deep
# Cluster into groups
casr-cluster -c ./casr-out/deep ./casr-clusters/deep
The cluster output directory contains one subdirectory per similarity cluster, with a representative .casrep and related artifacts. A cluster is not necessarily one source-level bug.
Interpreting Results¶
CASR reports include:
- Heuristic severity: Labels such as Exploitable or Probably Exploitable; do not use them as a ratified impact assessment
- Crash class: ASAN-specific crash type (e.g.,
heap-buffer-overflow) - Stack trace: Full sanitizer stack trace
- Registers: CPU register state at crash point (when available)
CASR vs. Crucible Triage
CASR and Crucible triage are complementary. crucible-triage focuses on replay with process facts, Exact/Stable identities, target attribution, taxonomy hints, and SARIF export. CASR excels at large-scale crash clustering. Feed representatives into Crucible as candidate inputs, then replay and inspect them; neither clusterer confirms a vulnerability or severity.
Batch Processing Multiple Campaigns¶
# Process all campaign directories
for campaign in deep model rpc-commands rpc-race whisper-audio; do
casr-libfuzzer \
-i "./crashes/${campaign}" \
-o "./casr-out/${campaign}" \
-- "./harness/libfuzzer/crucible-libfuzzer-${campaign}"
casr-cluster -c "./casr-out/${campaign}" "./casr-clusters/${campaign}"
done
# Summary of unique crashes per campaign
for d in ./casr-clusters/*/; do
echo "$(basename "$d"): $(ls -d "$d"/*/ 2>/dev/null | wc -l) unique"
done
halfempty: Crash Minimization¶
halfempty uses bisection strategies to minimize crash reproducers. Smaller reproducers make root cause analysis easier and produce cleaner CVE reports.
Installation¶
# Build from source (requires glib2 development headers)
# Fedora/RHEL:
sudo dnf install glib2-devel
# Ubuntu/Debian:
sudo apt install libglib2.0-dev
git clone https://github.com/googleprojectzero/halfempty.git
cd halfempty
make
sudo cp halfempty /usr/local/bin/
Minimizing a Crash¶
halfempty requires a script that exits 0 for "interesting" and non-zero otherwise. The predicate must preserve the expected identity, not merely the crash class; a smaller input can fall into a different bug.
Implement the predicate with triage.ObserveReplay plus triage.Identify, comparing ExactNamespace and Exact to the original witness. Until that wrapper exists, use Crucible's built-in identity-preserving minimization rather than a text grep.
Strategies¶
| Strategy | Description | Best For |
|---|---|---|
bisect | Binary search removal of byte ranges | General purpose, fastest |
zero | Replace byte ranges with zeros | Preserving file structure |
delete | Remove individual bytes | Fine-grained minimization |
Integration with Crucible Triage¶
Minimize crashes before triaging for cleaner reports:
Built-in minimization
crucible-triage --minimize natively recurses into crash subdirectories and preserves directory structure, reducing the need for external minimization tools in many workflows. Use halfempty when you need fine-grained control over the minimization strategy (bisect vs. zero vs. delete).
# Built-in minimization preserves the observed identity.
crucible-triage --artifact-kind input --crashes ./casr-clusters/deep \
--harness ./harness/libfuzzer/crucible-libfuzzer-deep \
--minimize --output ./reports
Workflow: Full Pipeline¶
A complete triage pipeline combining all tools:
flowchart TD
A[Fuzzing Campaign] --> B[Raw Crashes]
B --> C[CASR: Similarity clusters]
C --> D[Candidate representatives]
D --> E[halfempty: Minimize]
E --> F[Minimized Reproducers]
F --> G[crucible-triage: Reports + SARIF]
G --> H[Markdown Reports]
G --> I[SARIF for CI] # 1. Cluster with CASR
casr-libfuzzer -i ./crashes/deep -o ./casr-out -- ./harness/libfuzzer/crucible-libfuzzer-deep
casr-cluster -c ./casr-out ./casr-clusters
# 2. Replay candidates, minimize by identity, and generate internal reports and SARIF
crucible-triage \
--artifact-kind input \
--crashes ./casr-clusters \
--harness ./harness/libfuzzer/crucible-libfuzzer-deep \
--minimize \
--output ./reports \
--sarif ./results.sarif