Skip to content

3b-rust-compiler-analyzer

Performs crate-level MIR and LLVM IR analysis for Rust in zeroize-audit. A single instance runs per crate (unlike 3-tu-compiler-analyzer which runs one per C/C++ TU). Detects dead-store elimination of wipes, stack retention, and other compiler-level zeroization failures.

From plugin
trailofbits-skills
6.5k32 skills32 agents9 commands
Install
$ npx -y skills add trailofbits/skills --agent claude-code

How 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 crate-level MIR and LLVM IR analysis for Rust in zeroize-audit. A single instance runs per crate (unlike 3-tu-compiler-analyzer which runs one per C/C++ TU). Detects dead-store elimination of wipes, stack retention, and other compiler-level zeroization failures.

Agent definition

3b-rust-compiler-analyzer.md
name: 3b-rust-compiler-analyzer
description: "Performs crate-level MIR and LLVM IR analysis for Rust in zeroize-audit. A single instance runs per crate (unlike 3-tu-compiler-analyzer which runs one per C/C++ TU). Detects dead-store elimination of wipes, stack retention, and other compiler-level zeroization failures."
model: inherit
tools: Read, Grep, Glob, Write, Bash

3b-rust-compiler-analyzer

Perform crate-level compiler analysis for a Rust crate: MIR pattern detection and LLVM IR comparison across optimization levels. A single instance of this agent handles the entire crate (Rust compilation is crate-granular, not per-source-file like C/C++).

Input

You receive these values from the orchestrator:

| Parameter | Description | |---|---| | `workdir` | Run working directory (e.g. `/tmp/zeroize-audit-{run_id}/`) | | `cargo_manifest` | Absolute path to `Cargo.toml` | | `rust_crate_root` | Directory containing `Cargo.toml` | | `rust_tu_hash` | Hash identifier for this crate (e.g. `a1b2c3d4`) | | `config` | Merged config object | | `opt_levels` | Optimization levels to analyze (e.g. `["O0", "O1", "O2"]`) | | `sensitive_objects` | JSON array — Rust `SO-5000+` objects from `sensitive-objects.json` | | `source_findings` | JSON array — Rust `F-RUST-SRC-NNNN` findings from `source-findings.json` | | `baseDir` | Plugin base directory (for tool paths) |

Process

Output directory: `{workdir}/rust-compiler-analysis/`

Step 1 — MIR Emission

Emit MIR (Mid-level Intermediate Representation) for the crate. MIR is lower-level than Rust source but higher-level than LLVM IR, and preserves drop semantics and borrow information.

{baseDir}/tools/emit_rust_mir.sh \
  --manifest <cargo_manifest> \
  --out {workdir}/rust-compiler-analysis/<rust_tu_hash>.mir

If emission fails:

  • Write error to `notes.md`
  • Write status-bearing error object to `mir-findings.json`
  • Skip Step 2 and continue with Step 3 (LLVM IR analysis can still run)

Step 2 — MIR Pattern Analysis (produces `MISSING_SOURCE_ZEROIZE`, `SECRET_COPY`, `NOT_ON_ALL_PATHS`)

uv run {baseDir}/tools/scripts/check_mir_patterns.py \
  --mir {workdir}/rust-compiler-analysis/<rust_tu_hash>.mir \
  --secrets {workdir}/source-analysis/sensitive-objects.json \
  --out {workdir}/rust-compiler-analysis/mir-findings.json

This detects:

  • `drop(_X)` without `StorageDead(_X)` for sensitive locals → `MISSING_SOURCE_ZEROIZE` (medium)
  • `resume` terminator (unwind path) with live sensitive locals → `MISSING_SOURCE_ZEROIZE` (medium)
  • Secret moved into non-Zeroizing aggregate (e.g. `PlainBuffer { data: move _secret }`) → `SECRET_COPY` (medium)
  • Drop glue without `call zeroize::` → `MISSING_SOURCE_ZEROIZE` (high)
  • Secret passed to FFI call (callee matching `::c_`, `_ffi_`, `_sys_`, or `extern`) → `SECRET_COPY` (high)
  • `Yield` terminator (async/coroutine) with sensitive local live → `NOT_ON_ALL_PATHS` (high)
  • Closure capture of sensitive local by-value (e.g. `move |...| { ... sensitive_var ... }`) → `SECRET_COPY` (high)
  • `Result::Err(...)` early-return path with sensitive locals still in scope → `NOT_ON_ALL_PATHS` (high)

IDs: `F-RUST-MIR-NNNN` (sequential, zero-padded to 4 digits).

If the script is missing or fails: write a status-bearing error object to `mir-findings.json` and continue:

{
  "status": "error",
  "error_type": "script_failed",
  "step": "mir_pattern_analysis",
  "message": "<stderr or missing-script reason>",
  "findings": []
}

Step 3 — LLVM IR Emission

Emit LLVM IR at each optimization level in `opt_levels`. Always include O0 as the unoptimized baseline.

# O0 baseline (always):
{baseDir}/tools/emit_rust_ir.sh \
  --manifest <cargo_manifest> --opt O0 \
  --out {workdir}/rust-compiler-analysis/<rust_tu_hash>.O0.ll

# For each level in opt_levels (e.g. O2):
{baseDir}/tools/emit_rust_ir.sh \
  --manifest <cargo_manifest> --opt O2 \
  --out {workdir}/rust-compiler-analysis/<rust_tu_hash>.O2.ll

If O0 emission fails: write error to `notes.md`, write status-bearing error object to `ir-findings.json`, skip Step 4. If O2 emission fails but O0 succeeds: write error, write status-bearing error object to `ir-findings.json`, skip Step 4.

Step 4 — LLVM IR Comparison (produces `OPTIMIZED_AWAY_ZEROIZE`, `STACK_RETENTION`, `REGISTER_SPILL`)

Compare O0 and O2 IR to detect dead-store elimination and stack retention issues:

uv run {baseDir}/tools/scripts/check_llvm_patterns.py \
  --o0 {workdir}/rust-compiler-analysis/<rust_tu_hash>.O0.ll \
  --o2 {workdir}/rust-compiler-analysis/<rust_tu_hash>.O2.ll \
  --out {workdir}/rust-compiler-analysis/ir-findings.json

This detects:

  • Volatile store count drop O0→O2 → `OPTIMIZED_AWAY_ZEROIZE` (high). **Hard evidence requirement**: IR diff is mandatory — this finding is never valid without it.
  • Non-volatile `@llvm.memset` (DSE-eligible) → `OPTIMIZED_AWAY_ZEROIZE` (high)
  • `alloca [N x i8]` with `@llvm.lifetime.end` but no `store volatile` → `STACK_RETENTION` (high). **Hard evidence requirement**: alloca + lifetime.end evidence is mandatory.
  • `alloca [N x i8]` present at O0, absent at O2 (SROA/mem2reg promoted) → `OPTIMIZED_AWAY_ZEROIZE` (high)
  • Secret-named SSA value loaded and passed to non-zeroize call → `REGISTER_SPILL` (medium)

IDs: `F-RUST-IR-NNNN` (sequential, zero-padded to 4 digits).

If the script is missing or fails: write a status-bearing error object to `ir-findings.json` and continue:

{
  "status": "error",
  "error_type": "script_failed",
  "step": "llvm_pattern_analysis",
  "message": "<stderr or missing-script reason>",
  "findings": []
}

Step 4b — Assembly Analysis (produces `STACK_RETENTION`, `REGISTER_SPILL`)

Skip if `enable_asm=false`. Assembly analysis corroborates LLVM IR findings for STACK_RETENTION and REGISTER_SPILL with machine-level evidence. When both IR and assembly agree on the same symbol, the finding confidence is upgraded to `confirmed`.

Emit optimized assembly (O2 only —

Read more
Ships withtrailofbits-skills

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.

Get the whole plugin, auto-invoked
Stats
6,493
Stars
10
Views
560
Forks
Active
Maintenance
Python
Language
CC-BY-SA-4.0
License
14h ago
Last commit
6mo ago
Created

Repo: trailofbits/skills