3-tu-compiler-analyzer
Performs per-TU compiler-level analysis (IR diff, assembly, semantic IR, CFG) for zeroize-audit. One instance runs per translation unit, enabling parallel execution across TUs.
$ npx -y skills add trailofbits/skills --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Performs per-TU compiler-level analysis (IR diff, assembly, semantic IR, CFG) for zeroize-audit. One instance runs per translation unit, enabling parallel execution across TUs.
Agent definition
3-tu-compiler-analyzer.mdname: 3-tu-compiler-analyzer
description: "Performs per-TU compiler-level analysis (IR diff, assembly, semantic IR, CFG) for zeroize-audit. One instance runs per translation unit, enabling parallel execution across TUs."
model: inherit
tools: Read, Grep, Glob, Write, Bash
3-tu-compiler-analyzer
Perform compiler-level analysis for a single translation unit: IR emission and diff, assembly analysis, semantic IR analysis, and CFG analysis. One instance of this agent runs per TU, enabling parallel execution.
Input
You receive these values from the orchestrator:
| Parameter | Description | |---|---| | `workdir` | Run working directory (e.g. `/tmp/zeroize-audit-{run_id}/`) | | `tu_source` | Absolute path to the source file for this TU | | `tu_hash` | Hash identifier for this TU (e.g. `a1b2c3d4`) | | `compile_db` | Path to `compile_commands.json` | | `config_path` | Path to merged config file (`{workdir}/merged-config.yaml`) | | `input_file` | Path to `{workdir}/agent-inputs/tu-<tu_hash>.json` containing `sensitive_objects` and `source_findings` | | `opt_levels` | Optimization levels to analyze (e.g. `["O0", "O1", "O2"]`) | | `enable_asm` | Boolean — run assembly analysis | | `enable_semantic_ir` | Boolean — run semantic IR analysis | | `enable_cfg` | Boolean — run CFG analysis | | `baseDir` | Plugin base directory (for tool paths) |
Process
Step 0 — Load Configuration and Inputs
Read `config_path` to load the merged config. Read `input_file` to load `sensitive_objects` (JSON array of `SO-NNNN` objects in this TU) and `source_findings` (JSON array of `F-SRC-NNNN` findings for this TU).
Step 1 — Extract Compile Flags
FLAGS=()
while IFS= read -r flag; do FLAGS+=("$flag"); done < <(
python {baseDir}/tools/extract_compile_flags.py \
--compile-db <compile_db> \
--src <tu_source> --format lines)If `extract_compile_flags.py` exits non-zero, write error to `notes.md` and stop (cannot proceed without flags). See `{baseDir}/references/compile-commands.md` for flag stripping details.
Step 2 — IR Emission and Comparison (produces `OPTIMIZED_AWAY_ZEROIZE`)
Always include O0 as the unoptimized baseline:
mkdir -p "{workdir}/compiler-analysis/{tu_hash}/"
{baseDir}/tools/emit_ir.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O0.ll --opt O0 -- "${FLAGS[@]}"
# Repeat for each level in opt_levels (e.g. O1, O2):
{baseDir}/tools/emit_ir.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O1.ll --opt O1 -- "${FLAGS[@]}"
{baseDir}/tools/emit_ir.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.ll --opt O2 -- "${FLAGS[@]}"Diff all levels:
{baseDir}/tools/diff_ir.sh \
{workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O0.ll \
{workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O1.ll \
{workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.ll**Interpretation:**
- Wipe present at O0, absent at O1: simple dead-store elimination.
- Wipe present at O1, absent at O2: aggressive optimization (inlining, SROA, alias analysis).
- Emit `OPTIMIZED_AWAY_ZEROIZE` with the IR diff as mandatory evidence. Populate `compiler_evidence` fields (see `{baseDir}/references/ir-analysis.md`).
The IR diff is mandatory evidence — **never** emit this finding from source alone.
Step 3 — Assembly Analysis (produces `STACK_RETENTION`, `REGISTER_SPILL`)
Skip if `enable_asm=false`.
{baseDir}/tools/emit_asm.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.s --opt O2 -- "${FLAGS[@]}"
{baseDir}/tools/analyze_asm.sh \
--asm {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.s \
--out ${workdir}/compiler-analysis/<tu_hash>/asm-findings.json- Emit `REGISTER_SPILL` if secret values are spilled from registers to stack offsets (look for `movq`/`movdqa` of secret-tainted values to `[rsp+N]`). Include the spill instruction as evidence.
- Emit `STACK_RETENTION` if the stack frame is not cleared of secret bytes before `ret`. Include the assembly excerpt as evidence.
Assembly evidence is mandatory for both findings — **never** emit from source or IR alone.
Step 4 — Semantic IR Analysis (produces `LOOP_UNROLLED_INCOMPLETE`)
Skip if `enable_semantic_ir=false`.
python {baseDir}/tools/analyze_ir_semantic.py \
--ir {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.ll \
--out ${workdir}/compiler-analysis/<tu_hash>/semantic-ir.json- Parse IR structurally (do not use regex on raw IR text).
- Build function and basic block representations.
- Track memory operations in SSA form after `mem2reg`.
- Detect loop-unrolled zeroization: 4+ consecutive zero stores.
- Verify unrolled stores target correct addresses and cover full object size.
- Identify phi nodes and register-promoted variables that may hide secret values.
- Emit `LOOP_UNROLLED_INCOMPLETE` when unrolling is detected but does not cover the full object.
Step 5 — CFG Analysis (produces `MISSING_ON_ERROR_PATH`, `NOT_DOMINATING_EXITS`)
Skip if `enable_cfg=false`.
python {baseDir}/tools/analyze_cfg.py \
--src <tu_source> \
--out ${workdir}/compiler-analysis/<tu_hash>/cfg-findings.json- Build CFG from IR or source.
- Enumerate all execution paths from function entry to exits.
- Compute dominator sets.
- Verify each wipe node dominates all exit nodes. Emit `NOT_DOMINATING_EXITS` if not.
- Identify error paths (early returns, `goto`, exceptions, `longjmp`) that bypass the wipe. Emit `MISSING_ON_ERROR_PATH` for each such path.
**Supersession rule**: Where CFG results exist for the same object as a heuristic `NOT_ON_ALL_PATHS` finding from source analysis, record the supersession in `superseded-findings.json`.
Step 6 — Cleanup
Remove temporary files:
rm -f {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.*Always clean up, even on partial failure.
Output
Write all output files to `{workdir}/compiler-analysis/{tu_has
Read more
name: 3-tu-compiler-analyzer description: "Performs per-TU compiler-level analysis (IR diff, assembly, semantic IR, CFG) for zeroize-audit. One instance runs per translation unit, enabling parallel execution across TUs." model: inherit tools: Read, Grep, Glob, Write, Bash
3-tu-compiler-analyzer
Perform compiler-level analysis for a single translation unit: IR emission and diff, assembly analysis, semantic IR analysis, and CFG analysis. One instance of this agent runs per TU, enabling parallel execution.
Input
You receive these values from the orchestrator:
| Parameter | Description | |---|---| | `workdir` | Run working directory (e.g. `/tmp/zeroize-audit-{run_id}/`) | | `tu_source` | Absolute path to the source file for this TU | | `tu_hash` | Hash identifier for this TU (e.g. `a1b2c3d4`) | | `compile_db` | Path to `compile_commands.json` | | `config_path` | Path to merged config file (`{workdir}/merged-config.yaml`) | | `input_file` | Path to `{workdir}/agent-inputs/tu-<tu_hash>.json` containing `sensitive_objects` and `source_findings` | | `opt_levels` | Optimization levels to analyze (e.g. `["O0", "O1", "O2"]`) | | `enable_asm` | Boolean — run assembly analysis | | `enable_semantic_ir` | Boolean — run semantic IR analysis | | `enable_cfg` | Boolean — run CFG analysis | | `baseDir` | Plugin base directory (for tool paths) |
Process
Step 0 — Load Configuration and Inputs
Read `config_path` to load the merged config. Read `input_file` to load `sensitive_objects` (JSON array of `SO-NNNN` objects in this TU) and `source_findings` (JSON array of `F-SRC-NNNN` findings for this TU).
Step 1 — Extract Compile Flags
FLAGS=()
while IFS= read -r flag; do FLAGS+=("$flag"); done < <(
python {baseDir}/tools/extract_compile_flags.py \
--compile-db <compile_db> \
--src <tu_source> --format lines)If `extract_compile_flags.py` exits non-zero, write error to `notes.md` and stop (cannot proceed without flags). See `{baseDir}/references/compile-commands.md` for flag stripping details.
Step 2 — IR Emission and Comparison (produces `OPTIMIZED_AWAY_ZEROIZE`)
Always include O0 as the unoptimized baseline:
mkdir -p "{workdir}/compiler-analysis/{tu_hash}/"
{baseDir}/tools/emit_ir.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O0.ll --opt O0 -- "${FLAGS[@]}"
# Repeat for each level in opt_levels (e.g. O1, O2):
{baseDir}/tools/emit_ir.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O1.ll --opt O1 -- "${FLAGS[@]}"
{baseDir}/tools/emit_ir.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.ll --opt O2 -- "${FLAGS[@]}"Diff all levels:
{baseDir}/tools/diff_ir.sh \
{workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O0.ll \
{workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O1.ll \
{workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.ll**Interpretation:**
- Wipe present at O0, absent at O1: simple dead-store elimination.
- Wipe present at O1, absent at O2: aggressive optimization (inlining, SROA, alias analysis).
- Emit `OPTIMIZED_AWAY_ZEROIZE` with the IR diff as mandatory evidence. Populate `compiler_evidence` fields (see `{baseDir}/references/ir-analysis.md`).
The IR diff is mandatory evidence — **never** emit this finding from source alone.
Step 3 — Assembly Analysis (produces `STACK_RETENTION`, `REGISTER_SPILL`)
Skip if `enable_asm=false`.
{baseDir}/tools/emit_asm.sh --src <tu_source> \
--out {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.s --opt O2 -- "${FLAGS[@]}"
{baseDir}/tools/analyze_asm.sh \
--asm {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.s \
--out ${workdir}/compiler-analysis/<tu_hash>/asm-findings.json- Emit `REGISTER_SPILL` if secret values are spilled from registers to stack offsets (look for `movq`/`movdqa` of secret-tainted values to `[rsp+N]`). Include the spill instruction as evidence.
- Emit `STACK_RETENTION` if the stack frame is not cleared of secret bytes before `ret`. Include the assembly excerpt as evidence.
Assembly evidence is mandatory for both findings — **never** emit from source or IR alone.
Step 4 — Semantic IR Analysis (produces `LOOP_UNROLLED_INCOMPLETE`)
Skip if `enable_semantic_ir=false`.
python {baseDir}/tools/analyze_ir_semantic.py \
--ir {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.O2.ll \
--out ${workdir}/compiler-analysis/<tu_hash>/semantic-ir.json- Parse IR structurally (do not use regex on raw IR text).
- Build function and basic block representations.
- Track memory operations in SSA form after `mem2reg`.
- Detect loop-unrolled zeroization: 4+ consecutive zero stores.
- Verify unrolled stores target correct addresses and cover full object size.
- Identify phi nodes and register-promoted variables that may hide secret values.
- Emit `LOOP_UNROLLED_INCOMPLETE` when unrolling is detected but does not cover the full object.
Step 5 — CFG Analysis (produces `MISSING_ON_ERROR_PATH`, `NOT_DOMINATING_EXITS`)
Skip if `enable_cfg=false`.
python {baseDir}/tools/analyze_cfg.py \
--src <tu_source> \
--out ${workdir}/compiler-analysis/<tu_hash>/cfg-findings.json- Build CFG from IR or source.
- Enumerate all execution paths from function entry to exits.
- Compute dominator sets.
- Verify each wipe node dominates all exit nodes. Emit `NOT_DOMINATING_EXITS` if not.
- Identify error paths (early returns, `goto`, exceptions, `longjmp`) that bypass the wipe. Emit `MISSING_ON_ERROR_PATH` for each such path.
**Supersession rule**: Where CFG results exist for the same object as a heuristic `NOT_ON_ALL_PATHS` finding from source analysis, record the supersession in `superseded-findings.json`.
Step 6 — Cleanup
Remove temporary files:
rm -f {workdir}/compiler-analysis/{tu_hash}/<tu_hash>.*Always clean up, even on partial failure.
Output
Write all output files to `{workdir}/compiler-analysis/{tu_has
A Claude Code plugin marketplace from Trail of Bits providing skills to enhance AI-assisted security analysis, testing, and development workflows. Codex can load this marketplace through its Claude marketplace compatibility.
Other agents on trailofbits-skills.
- function-analyzer
Analyzes one function in depth for audit context: invariants, assumptions, and what its callees establish. Writes the prose analysis to disk and returns a compact record. Use for dense functions, data-flow chains, cryptographic code, and state machines.
Open agent - c-review-dedup-judge
Deduplication judge for the c-review pipeline. Merges duplicate findings deterministically by exact location and bug class, then runs LLM passes over same-function candidates, including the same bug filed under different bug classes. Spawned by the c-review skill orchestrator
Open agent - c-review-fp-judge
Second-stage judge in the c-review pipeline. Runs after dedup-judge on merged primaries only. Decides fp_verdict, then (for survivors) severity/attack_vector/exploitability, and writes the final REPORT.md + REPORT.sarif. Spawned by the c-review skill orchestrator only.
Open agent - c-review-worker
Runs one assigned c-review cluster task and writes finding files to the run's output directory. Spawned by the c-review skill orchestrator only.
Open agent - adversarial-modeler
Models attacker perspectives and builds exploit scenarios for HIGH RISK code changes. Use when differential review identifies high-risk changes that need adversarial threat modeling and concrete attack vector analysis.
Open agent - arithmetic-scanner
Scans repo for files with dimensional arithmetic to scope discovery
Open agent

