litclaude-ai 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/README_ko-KR.md +374 -0
- package/RELEASE_CHECKLIST.md +165 -0
- package/bin/litclaude-ai.js +643 -0
- package/cover.png +0 -0
- package/docs/agents.md +67 -0
- package/docs/hooks.md +134 -0
- package/docs/lsp.md +40 -0
- package/docs/migration.md +209 -0
- package/docs/workflow-compatibility-audit.md +119 -0
- package/generate_cover.py +123 -0
- package/package.json +48 -0
- package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/litclaude/.lsp.json +13 -0
- package/plugins/litclaude/.mcp.json +9 -0
- package/plugins/litclaude/agents/boulder-executor.md +12 -0
- package/plugins/litclaude/agents/librarian-researcher.md +15 -0
- package/plugins/litclaude/agents/oracle-verifier.md +16 -0
- package/plugins/litclaude/agents/prometheus-planner.md +13 -0
- package/plugins/litclaude/agents/qa-runner.md +16 -0
- package/plugins/litclaude/agents/quality-reviewer.md +17 -0
- package/plugins/litclaude/bin/litclaude-hook.js +110 -0
- package/plugins/litclaude/bin/litclaude-hud.js +271 -0
- package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
- package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
- package/plugins/litclaude/commands/deep-interview.md +21 -0
- package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
- package/plugins/litclaude/commands/lit-loop.md +40 -0
- package/plugins/litclaude/commands/lit-plan.md +35 -0
- package/plugins/litclaude/commands/litgoal.md +30 -0
- package/plugins/litclaude/commands/review-work.md +35 -0
- package/plugins/litclaude/commands/start-work.md +36 -0
- package/plugins/litclaude/hooks/hooks.json +54 -0
- package/plugins/litclaude/lib/context-pressure.mjs +25 -0
- package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
- package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
- package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
- package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
- package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
- package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
- package/plugins/litclaude/lib/workflow-check.mjs +83 -0
- package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
- package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
- package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
- package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
- package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
- package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
- package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
- package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
- package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
- package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
- package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
- package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
- package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
- package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
- package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
- package/plugins/litclaude/skills/programming/SKILL.md +106 -0
- package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
- package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
- package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
- package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
- package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
- package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
- package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
- package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
- package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
- package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
- package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
- package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
- package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
- package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
- package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
- package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
- package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
- package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
- package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
- package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
- package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
- package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
- package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
- package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
- package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
- package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
- package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
- package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
- package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
- package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
- package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
- package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
- package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
- package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
- package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
- package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
- package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
- package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
- package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
- package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
- package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
- package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
- package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
- package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
- package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
- package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
- package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
- package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
- package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
- package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
- package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
- package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
- package/plugins/litclaude/skills/rules/SKILL.md +66 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
- package/scripts/audit-plan-checkboxes.mjs +37 -0
- package/scripts/doctor.mjs +41 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/postinstall.mjs +50 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +136 -0
- package/scripts/validate-plugin.mjs +72 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
|
|
2
|
+
# Rust Undefined Behavior Exorcist
|
|
3
|
+
|
|
4
|
+
You are a UB hunter. Your job is to find, classify, prove, and eliminate every instance of undefined behavior in Rust code. **Miri is your primary weapon** — everything else supplements where Miri cannot reach.
|
|
5
|
+
|
|
6
|
+
## Core Philosophy
|
|
7
|
+
|
|
8
|
+
1. **Miri first, always.** Before reading a single line of `unsafe`, run Miri. Before proposing a fix, run Miri. After applying a fix, run Miri. Miri is the oracle.
|
|
9
|
+
2. **Classify before fixing.** Every UB finding gets classified against the 14-category taxonomy (see [ub-taxonomy.md](ub-taxonomy.md)). This prevents misdiagnosis and ensures the fix targets the root cause, not a symptom.
|
|
10
|
+
3. **Prove the fix.** A fix is not done until Miri passes with full paranoia flags. If Miri cannot run the test (FFI, I/O), the fix is not done until the appropriate sanitizer passes.
|
|
11
|
+
4. **Bead handoff.** Each resolved UB instance is a "bead" — a discrete, documented, proven fix. Hand it off with: the UB category, the root cause, the fix, and the Miri proof.
|
|
12
|
+
|
|
13
|
+
## The UB Taxonomy
|
|
14
|
+
|
|
15
|
+
14 categories. The full reference is in [ub-taxonomy.md](ub-taxonomy.md). Memorize the categories; classify every finding:
|
|
16
|
+
|
|
17
|
+
| # | Category | Miri? |
|
|
18
|
+
|---|----------|-------|
|
|
19
|
+
| 1 | Aliasing violations (Stacked/Tree Borrows) | YES |
|
|
20
|
+
| 2 | Data races | YES |
|
|
21
|
+
| 3 | Use-after-free / dangling pointers | YES |
|
|
22
|
+
| 4 | Uninitialized memory | YES |
|
|
23
|
+
| 5 | Invalid values (type invariant violations) | YES |
|
|
24
|
+
| 6 | Misaligned pointer access | YES |
|
|
25
|
+
| 7 | Pin invariant violations | PARTIAL |
|
|
26
|
+
| 8 | FFI boundary UB | LIMITED |
|
|
27
|
+
| 9 | Incorrect Send/Sync implementations | YES (via race) |
|
|
28
|
+
| 10 | Out-of-bounds memory access | YES |
|
|
29
|
+
| 11 | Provenance violations | YES (strict mode) |
|
|
30
|
+
| 12 | Double free / invalid free | YES |
|
|
31
|
+
| 13 | Library / unsafe contract violations | PARTIAL |
|
|
32
|
+
| 14 | Unwinding across extern "C" | PARTIAL |
|
|
33
|
+
|
|
34
|
+
## The Hunt Workflow
|
|
35
|
+
|
|
36
|
+
### Phase 1: Reconnaissance
|
|
37
|
+
|
|
38
|
+
1. **Find all `unsafe` blocks and `unsafe impl`s:**
|
|
39
|
+
```bash
|
|
40
|
+
rg 'unsafe\s*(fn|impl|{|\{)' --type rust -n
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. **Find all `unsafe` trait implementations:**
|
|
44
|
+
```bash
|
|
45
|
+
rg 'unsafe\s+impl\s+(Send|Sync)' --type rust -n
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
3. **Find transmute / pointer casts / raw pointer derefs:**
|
|
49
|
+
```bash
|
|
50
|
+
rg '(transmute|transmute_copy|from_raw|into_raw|as_ptr|as_mut_ptr|offset|add|sub|read|write|copy|ptr::null)' --type rust -n
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
4. **Find FFI boundaries:**
|
|
54
|
+
```bash
|
|
55
|
+
rg 'extern\s+"C"' --type rust -n
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
5. **Count and catalog.** Create a hit list: file, line, `unsafe` category, initial risk assessment (high/medium/low based on the UB taxonomy).
|
|
59
|
+
|
|
60
|
+
### Phase 2: Miri Sweep (THE CRITICAL PHASE)
|
|
61
|
+
|
|
62
|
+
Run Miri with escalating strictness. **Do not skip any level.**
|
|
63
|
+
|
|
64
|
+
**Level 1 — Default (Stacked Borrows):**
|
|
65
|
+
```bash
|
|
66
|
+
cargo +nightly miri test 2>&1
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Level 2 — Strict Provenance + Symbolic Alignment:**
|
|
70
|
+
```bash
|
|
71
|
+
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
|
|
72
|
+
cargo +nightly miri test 2>&1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Level 3 — Full Paranoia (the audit standard):**
|
|
76
|
+
```bash
|
|
77
|
+
MIRIFLAGS="\
|
|
78
|
+
-Zmiri-strict-provenance \
|
|
79
|
+
-Zmiri-symbolic-alignment-check \
|
|
80
|
+
-Zmiri-preemption-rate=0.1 \
|
|
81
|
+
-Zmiri-backtrace=full \
|
|
82
|
+
-Zmiri-disable-isolation" \
|
|
83
|
+
cargo +nightly miri test 2>&1
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Level 4 — Tree Borrows (second model confirmation):**
|
|
87
|
+
```bash
|
|
88
|
+
MIRIFLAGS="\
|
|
89
|
+
-Zmiri-tree-borrows \
|
|
90
|
+
-Zmiri-strict-provenance \
|
|
91
|
+
-Zmiri-symbolic-alignment-check \
|
|
92
|
+
-Zmiri-preemption-rate=0.1 \
|
|
93
|
+
-Zmiri-backtrace=full \
|
|
94
|
+
-Zmiri-disable-isolation" \
|
|
95
|
+
cargo +nightly miri test 2>&1
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Interpret results:**
|
|
99
|
+
- Fails at Level 1 → Definite UB. Fix immediately.
|
|
100
|
+
- Passes Level 1, fails Level 2 → Provenance or alignment UB. Fix.
|
|
101
|
+
- Passes Levels 1-3, fails Level 4 → Tree Borrows found something Stacked Borrows missed (unusual). Investigate — may be a Tree Borrows false positive, but usually indicates fragile aliasing.
|
|
102
|
+
- Passes all 4 → Miri-clean. Proceed to supplementary tools.
|
|
103
|
+
|
|
104
|
+
### Phase 3: Supplementary Scans
|
|
105
|
+
|
|
106
|
+
For code Miri cannot fully cover:
|
|
107
|
+
|
|
108
|
+
**Concurrent code with custom atomics:**
|
|
109
|
+
```bash
|
|
110
|
+
RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests 2>&1
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**FFI-heavy code:**
|
|
114
|
+
```bash
|
|
115
|
+
RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target $(rustc -vV | rg host | awk '{print $2}') 2>&1
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Untrusted input parsing:**
|
|
119
|
+
```bash
|
|
120
|
+
cargo +nightly fuzz run <target> -- -max_total_time=300 2>&1
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Phase 4: Fix-and-Prove Loop
|
|
124
|
+
|
|
125
|
+
For each UB finding:
|
|
126
|
+
|
|
127
|
+
1. **Classify** against the 14-category taxonomy.
|
|
128
|
+
2. **Write the SAFETY comment** explaining what is wrong and what the fix must achieve.
|
|
129
|
+
3. **Apply the minimal fix.** Do not refactor — fix the UB and nothing else.
|
|
130
|
+
4. **Run Miri (Level 3 minimum) on the specific test that triggered the UB.**
|
|
131
|
+
5. **Run Miri (Level 3) on the full test suite** to check for regressions.
|
|
132
|
+
6. **Document the bead:**
|
|
133
|
+
```
|
|
134
|
+
BEAD: [Category #] [Short description]
|
|
135
|
+
FILE: [path:line]
|
|
136
|
+
ROOT CAUSE: [one sentence]
|
|
137
|
+
FIX: [one sentence]
|
|
138
|
+
PROOF: Miri Level [N] pass — [command used]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Phase 5: Hardening (Post-Fix)
|
|
142
|
+
|
|
143
|
+
After all beads are resolved:
|
|
144
|
+
|
|
145
|
+
1. **Add Miri to CI** if not already present (see [miri-sanitizers-loom.md](miri-sanitizers-loom.md) for the GitHub Actions config).
|
|
146
|
+
2. **Add `#[cfg(miri)]` regression tests** for each bead — these are the tests that originally caught the UB, locked in so it never returns.
|
|
147
|
+
3. **Review SAFETY comments** on every remaining `unsafe` block. Each must name the specific invariant from the taxonomy.
|
|
148
|
+
4. **Run the full paranoia sweep one final time** to confirm clean.
|
|
149
|
+
|
|
150
|
+
## Miri-First Decision Protocol
|
|
151
|
+
|
|
152
|
+
When the agent encounters `unsafe` code during ANY Rust task (not just audits):
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
Is there unsafe code in the changeset?
|
|
156
|
+
YES → Run Miri Level 1 before proceeding.
|
|
157
|
+
│ Miri fails?
|
|
158
|
+
│ YES → Stop. Classify. Fix. Prove. Then continue.
|
|
159
|
+
│ NO → Run Miri Level 2 (strict provenance).
|
|
160
|
+
│ Miri fails?
|
|
161
|
+
│ YES → Stop. Classify. Fix. Prove. Then continue.
|
|
162
|
+
│ NO → Proceed with the original task.
|
|
163
|
+
NO → Proceed normally.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This is not optional. **Every `unsafe` block gets Miri'd before it ships.**
|
|
167
|
+
|
|
168
|
+
## SAFETY Comment Standard
|
|
169
|
+
|
|
170
|
+
Every `unsafe` block requires a SAFETY comment within 5 lines above it. The comment must:
|
|
171
|
+
|
|
172
|
+
1. **Name the UB category** it could trigger (from the taxonomy).
|
|
173
|
+
2. **State the invariant** that makes this safe.
|
|
174
|
+
3. **Name who/what guarantees** the invariant (caller contract, type system, runtime check).
|
|
175
|
+
|
|
176
|
+
```rust
|
|
177
|
+
// SAFETY: [Category 4 — Uninitialized Memory]
|
|
178
|
+
// All N elements have been written to via `ptr::write` in the loop above.
|
|
179
|
+
// The loop runs exactly `len` times, and `len` was validated against the
|
|
180
|
+
// allocation size at line 42. MaybeUninit::assume_init is therefore sound.
|
|
181
|
+
unsafe { buf.assume_init() }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Bad SAFETY comments that must be rejected:
|
|
185
|
+
- `// SAFETY: we know this is safe` — Says nothing.
|
|
186
|
+
- `// SAFETY: this is fine because we tested it` — Testing does not prove absence of UB.
|
|
187
|
+
- `// SAFETY: the caller ensures correctness` — Which invariant? What is the contract?
|
|
188
|
+
- No SAFETY comment at all — Immediate failure.
|
|
189
|
+
|
|
190
|
+
## Audit Report Format
|
|
191
|
+
|
|
192
|
+
When completing a UB audit, produce a summary:
|
|
193
|
+
|
|
194
|
+
```markdown
|
|
195
|
+
## UB Audit Report
|
|
196
|
+
|
|
197
|
+
**Scope:** [crate/module/file]
|
|
198
|
+
**Miri version:** [output of `cargo +nightly miri --version`]
|
|
199
|
+
**Date:** [date]
|
|
200
|
+
|
|
201
|
+
### Findings
|
|
202
|
+
|
|
203
|
+
| # | Category | File:Line | Severity | Status |
|
|
204
|
+
|---|----------|-----------|----------|--------|
|
|
205
|
+
| 1 | Aliasing | src/buf.rs:42 | High | Fixed (Bead #1) |
|
|
206
|
+
| 2 | Uninit | src/ffi.rs:98 | High | Fixed (Bead #2) |
|
|
207
|
+
|
|
208
|
+
### Beads
|
|
209
|
+
|
|
210
|
+
#### Bead #1: Aliasing violation in buffer resize
|
|
211
|
+
- **Root cause:** `&mut` created while `&` to same slice existed
|
|
212
|
+
- **Fix:** Restructured to drop shared ref before taking mutable
|
|
213
|
+
- **Proof:** `cargo +nightly miri test -- test_buffer_resize` passes Level 3
|
|
214
|
+
|
|
215
|
+
### Miri CI Status
|
|
216
|
+
- [ ] Miri added to CI (Level 2 minimum)
|
|
217
|
+
- [ ] All SAFETY comments reviewed
|
|
218
|
+
- [ ] Regression tests added for each bead
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Common Fix Patterns
|
|
222
|
+
|
|
223
|
+
### Aliasing → Use `UnsafeCell` or restructure borrows
|
|
224
|
+
```rust
|
|
225
|
+
// BEFORE (UB: &mut while & exists)
|
|
226
|
+
let ptr = slice.as_ptr();
|
|
227
|
+
let mut_ref = &mut slice[0]; // UB: ptr still usable
|
|
228
|
+
|
|
229
|
+
// AFTER
|
|
230
|
+
let mut_ref = &mut slice[0];
|
|
231
|
+
// ptr is never created / used across the mutable borrow
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Uninitialized → Use `MaybeUninit::write` + `assume_init`
|
|
235
|
+
```rust
|
|
236
|
+
// BEFORE (UB: mem::uninitialized)
|
|
237
|
+
let x: T = unsafe { std::mem::uninitialized() };
|
|
238
|
+
|
|
239
|
+
// AFTER
|
|
240
|
+
let x: T = unsafe {
|
|
241
|
+
let mut uninit = MaybeUninit::<T>::uninit();
|
|
242
|
+
uninit.write(initial_value);
|
|
243
|
+
uninit.assume_init()
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Provenance → Use `expose_provenance` / `with_exposed_provenance`
|
|
248
|
+
```rust
|
|
249
|
+
// BEFORE (UB: provenance lost)
|
|
250
|
+
let addr = ptr as usize;
|
|
251
|
+
let recovered = addr as *const T;
|
|
252
|
+
|
|
253
|
+
// AFTER
|
|
254
|
+
let addr = ptr.expose_provenance();
|
|
255
|
+
let recovered = std::ptr::with_exposed_provenance::<T>(addr);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Send/Sync → Remove manual impl, use PhantomData
|
|
259
|
+
```rust
|
|
260
|
+
// BEFORE (unsound)
|
|
261
|
+
unsafe impl Send for MyType {}
|
|
262
|
+
|
|
263
|
+
// AFTER — if MyType truly needs Send, prove it:
|
|
264
|
+
// SAFETY: [Category 9 — Send/Sync]
|
|
265
|
+
// MyType's only non-Send field is `*mut Buffer`. Access to the buffer
|
|
266
|
+
// is guarded by `self.lock: Mutex<()>`, which provides the
|
|
267
|
+
// happens-before guarantee required by Send.
|
|
268
|
+
unsafe impl Send for MyType {}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### FFI → Validate at boundary
|
|
272
|
+
```rust
|
|
273
|
+
// BEFORE (UB: null pointer from C becomes &T)
|
|
274
|
+
let result = unsafe { ffi_call() };
|
|
275
|
+
|
|
276
|
+
// AFTER
|
|
277
|
+
let raw = unsafe { ffi_call() };
|
|
278
|
+
let result = NonNull::new(raw).ok_or(Error::NullFromFfi)?;
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Activation
|
|
282
|
+
|
|
283
|
+
This skill activates when:
|
|
284
|
+
- The user requests a "UB audit", "miri sweep", "unsafe audit", "soundness check", "rustonomicon audit", "race hunt"
|
|
285
|
+
- The agent encounters `unsafe` code during a Rust task and needs to verify it
|
|
286
|
+
- Miri reports a failure and the agent needs to classify and fix it
|
|
287
|
+
- The user asks "is this sound?" about Rust code
|
|
288
|
+
|
|
289
|
+
**Miri is not optional. Miri is the proof. Ship nothing `unsafe` without Miri's blessing.**
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# Miri, Sanitizers, Loom, and Fuzzing — The UB Detection Arsenal
|
|
2
|
+
|
|
3
|
+
Miri is the **primary weapon**. Everything else is supplementary for the gaps Miri cannot reach.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Miri — The First and Last Line of Defense
|
|
8
|
+
|
|
9
|
+
### What Miri Is
|
|
10
|
+
|
|
11
|
+
Miri is an interpreter for Rust's MIR (Mid-level IR). It executes your test suite inside a virtual machine that tracks every byte of memory for validity, provenance, alignment, initialization, and aliasing. It is **deterministic** — same inputs, same result — and it can find UB that no amount of testing on real hardware will ever trigger.
|
|
12
|
+
|
|
13
|
+
### Why Miri Is Non-Negotiable
|
|
14
|
+
|
|
15
|
+
- Detects 12 of 14 UB categories (see `ub-taxonomy.md`).
|
|
16
|
+
- Catches aliasing violations that compile and run correctly on every platform today but are UB that future compiler optimizations will exploit.
|
|
17
|
+
- Catches data races under a configurable scheduling model.
|
|
18
|
+
- Catches provenance violations that are impossible to observe on real hardware.
|
|
19
|
+
- **Zero false positives** — if Miri says it is UB, it is UB. Period.
|
|
20
|
+
|
|
21
|
+
### Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
rustup install nightly
|
|
25
|
+
rustup component add miri rust-src --toolchain nightly
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Verify:
|
|
29
|
+
```bash
|
|
30
|
+
cargo +nightly miri --version
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Running Miri
|
|
34
|
+
|
|
35
|
+
**Default run (Stacked Borrows, standard checks):**
|
|
36
|
+
```bash
|
|
37
|
+
cargo +nightly miri test
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**With nextest (recommended for projects already using nextest):**
|
|
41
|
+
```bash
|
|
42
|
+
cargo +nightly miri nextest run
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Specific test:**
|
|
46
|
+
```bash
|
|
47
|
+
cargo +nightly miri test -- test_name
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Run a binary:**
|
|
51
|
+
```bash
|
|
52
|
+
cargo +nightly miri run
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### MIRIFLAGS — The Dial-Up Knobs
|
|
56
|
+
|
|
57
|
+
These flags are set via the `MIRIFLAGS` environment variable. The agent should use ALL of the strictness flags during a UB audit.
|
|
58
|
+
|
|
59
|
+
#### Aliasing Model
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Default: Stacked Borrows (strict)
|
|
63
|
+
cargo +nightly miri test
|
|
64
|
+
|
|
65
|
+
# Tree Borrows (newer, more permissive — use as a second pass)
|
|
66
|
+
MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Protocol:** Run Stacked Borrows first. If it fails, fix it. Then run Tree Borrows to confirm. Code that passes Stacked Borrows is sound under both models.
|
|
70
|
+
|
|
71
|
+
#### Strict Provenance
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Catches `ptr as usize as *const T` roundtrips where provenance is lost. **Should be ON for every audit.**
|
|
78
|
+
|
|
79
|
+
#### Symbolic Alignment Checks
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
MIRIFLAGS="-Zmiri-symbolic-alignment-check" cargo +nightly miri test
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Catches alignment UB that happens to be aligned on your machine but is not guaranteed by the type system.
|
|
86
|
+
|
|
87
|
+
#### Data Race Detection Tuning
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Increase preemption rate to stress-test race conditions
|
|
91
|
+
MIRIFLAGS="-Zmiri-preemption-rate=0.5" cargo +nightly miri test
|
|
92
|
+
|
|
93
|
+
# Disable preemption (sequential scheduling — fewer races found but deterministic)
|
|
94
|
+
MIRIFLAGS="-Zmiri-preemption-rate=0" cargo +nightly miri test
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### The Full Paranoia Sweep (Use This for Audits)
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
MIRIFLAGS="\
|
|
101
|
+
-Zmiri-strict-provenance \
|
|
102
|
+
-Zmiri-symbolic-alignment-check \
|
|
103
|
+
-Zmiri-preemption-rate=0.1 \
|
|
104
|
+
-Zmiri-backtrace=full \
|
|
105
|
+
-Zmiri-disable-isolation" \
|
|
106
|
+
cargo +nightly miri test
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then a second pass with Tree Borrows:
|
|
110
|
+
```bash
|
|
111
|
+
MIRIFLAGS="\
|
|
112
|
+
-Zmiri-tree-borrows \
|
|
113
|
+
-Zmiri-strict-provenance \
|
|
114
|
+
-Zmiri-symbolic-alignment-check \
|
|
115
|
+
-Zmiri-preemption-rate=0.1 \
|
|
116
|
+
-Zmiri-backtrace=full \
|
|
117
|
+
-Zmiri-disable-isolation" \
|
|
118
|
+
cargo +nightly miri test
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Isolation and I/O
|
|
122
|
+
|
|
123
|
+
Miri runs in isolation by default — no file I/O, no network, no system calls. If your tests need the filesystem:
|
|
124
|
+
```bash
|
|
125
|
+
MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use sparingly — isolation is a feature, not a limitation. Tests that need I/O should have a separate `#[cfg(not(miri))]` path.
|
|
129
|
+
|
|
130
|
+
### Miri Limitations
|
|
131
|
+
|
|
132
|
+
| Cannot do | Workaround |
|
|
133
|
+
|-----------|-----------|
|
|
134
|
+
| Execute FFI / C code | ASAN, MSAN, Valgrind |
|
|
135
|
+
| Run I/O-heavy tests (default) | `-Zmiri-disable-isolation` or `#[cfg(not(miri))]` |
|
|
136
|
+
| Exhaustive interleaving exploration | loom |
|
|
137
|
+
| Find performance bugs | criterion, flamegraph |
|
|
138
|
+
| Run inline assembly | skip with `#[cfg(not(miri))]` |
|
|
139
|
+
| Test OS-specific behavior | real hardware + sanitizers |
|
|
140
|
+
|
|
141
|
+
### Miri in CI
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# GitHub Actions example
|
|
145
|
+
miri:
|
|
146
|
+
runs-on: ubuntu-latest
|
|
147
|
+
steps:
|
|
148
|
+
- uses: actions/checkout@v4
|
|
149
|
+
- uses: dtolnay/rust-toolchain@nightly
|
|
150
|
+
with:
|
|
151
|
+
components: miri, rust-src
|
|
152
|
+
- name: Miri test (Stacked Borrows + strict provenance)
|
|
153
|
+
run: |
|
|
154
|
+
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
|
|
155
|
+
cargo +nightly miri test
|
|
156
|
+
- name: Miri test (Tree Borrows)
|
|
157
|
+
run: |
|
|
158
|
+
MIRIFLAGS="-Zmiri-tree-borrows -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
|
|
159
|
+
cargo +nightly miri test
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Miri-Incompatible Test Gating
|
|
163
|
+
|
|
164
|
+
```rust
|
|
165
|
+
#[test]
|
|
166
|
+
#[cfg_attr(miri, ignore)] // Miri cannot run this (FFI, I/O, inline asm)
|
|
167
|
+
fn test_requires_real_hardware() {
|
|
168
|
+
// ...
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Or conditionally compile the test body:
|
|
172
|
+
#[test]
|
|
173
|
+
fn test_with_miri_fallback() {
|
|
174
|
+
#[cfg(miri)]
|
|
175
|
+
{
|
|
176
|
+
// Simplified version that avoids FFI
|
|
177
|
+
}
|
|
178
|
+
#[cfg(not(miri))]
|
|
179
|
+
{
|
|
180
|
+
// Full version with FFI
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Sanitizers — Where Miri Cannot Reach
|
|
188
|
+
|
|
189
|
+
Sanitizers are compiler instrumentation passes. They run your actual binary on real hardware with extra checks injected. Use them for FFI, I/O-heavy code, and integration tests.
|
|
190
|
+
|
|
191
|
+
### AddressSanitizer (ASAN)
|
|
192
|
+
|
|
193
|
+
Detects: use-after-free, buffer overflow, stack-use-after-return, double-free, memory leaks.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
On macOS:
|
|
200
|
+
```bash
|
|
201
|
+
RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target aarch64-apple-darwin
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### ThreadSanitizer (TSAN)
|
|
205
|
+
|
|
206
|
+
Detects: data races on non-atomic accesses across threads.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
RUSTFLAGS="-Zsanitizer=thread" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**When to use over Miri:** Integration tests involving real threads + real I/O + FFI. Miri's data-race detector is superior for pure-Rust code.
|
|
213
|
+
|
|
214
|
+
### MemorySanitizer (MSAN)
|
|
215
|
+
|
|
216
|
+
Detects: reads of uninitialized memory.
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
RUSTFLAGS="-Zsanitizer=memory -Zsanitizer-memory-track-origins" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**When to use over Miri:** FFI code where C/C++ may return uninitialized memory into Rust.
|
|
223
|
+
|
|
224
|
+
### UndefinedBehaviorSanitizer (UBSAN)
|
|
225
|
+
|
|
226
|
+
Detects: integer overflow, misaligned access, null dereference, and other C/C++-style UB at the LLVM level.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
RUSTFLAGS="-Zsanitizer=undefined" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Sanitizer Limitations
|
|
233
|
+
|
|
234
|
+
- Require nightly + `-Zbuild-std` (rebuilds the standard library with instrumentation).
|
|
235
|
+
- MSAN requires ALL dependencies (including C libs) to be instrumented — practically hard.
|
|
236
|
+
- Cannot catch aliasing violations (that is Miri's domain).
|
|
237
|
+
- Significant runtime overhead (2-15x slower).
|
|
238
|
+
- Linux has the best support; macOS works for ASAN; Windows support is minimal.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Loom — Exhaustive Concurrency Testing
|
|
243
|
+
|
|
244
|
+
Loom explores all possible thread interleavings of a bounded concurrent program. It is mandatory for lock-free and wait-free primitives.
|
|
245
|
+
|
|
246
|
+
### When to Use Loom
|
|
247
|
+
|
|
248
|
+
- Any `unsafe` code involving atomics with ordering weaker than `SeqCst`.
|
|
249
|
+
- Custom lock implementations.
|
|
250
|
+
- Lock-free queues, stacks, or other concurrent data structures.
|
|
251
|
+
- Any code where you chose `Relaxed`, `Acquire`, or `Release` ordering.
|
|
252
|
+
|
|
253
|
+
### When NOT to Use Loom
|
|
254
|
+
|
|
255
|
+
- Code using only `Mutex`/`RwLock` from std or `parking_lot` — the locks are sound, your usage is the question, and Miri + TSAN cover that.
|
|
256
|
+
- Async code (loom does not model async runtimes — use `tokio::test` + Miri instead).
|
|
257
|
+
|
|
258
|
+
### Setup
|
|
259
|
+
|
|
260
|
+
```toml
|
|
261
|
+
[dev-dependencies]
|
|
262
|
+
loom = "0.7"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Loom Test Pattern
|
|
266
|
+
|
|
267
|
+
```rust
|
|
268
|
+
#[cfg(loom)]
|
|
269
|
+
mod loom_tests {
|
|
270
|
+
use loom::sync::atomic::{AtomicUsize, Ordering};
|
|
271
|
+
use loom::sync::Arc;
|
|
272
|
+
use loom::thread;
|
|
273
|
+
|
|
274
|
+
#[test]
|
|
275
|
+
fn concurrent_increment_is_sound() {
|
|
276
|
+
loom::model(|| {
|
|
277
|
+
let counter = Arc::new(AtomicUsize::new(0));
|
|
278
|
+
|
|
279
|
+
let threads: Vec<_> = (0..2).map(|_| {
|
|
280
|
+
let c = counter.clone();
|
|
281
|
+
thread::spawn(move || {
|
|
282
|
+
c.fetch_add(1, Ordering::SeqCst);
|
|
283
|
+
})
|
|
284
|
+
}).collect();
|
|
285
|
+
|
|
286
|
+
for t in threads {
|
|
287
|
+
t.join().unwrap();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
assert_eq!(counter.load(Ordering::SeqCst), 2);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Conditional Compilation for Loom
|
|
297
|
+
|
|
298
|
+
```rust
|
|
299
|
+
#[cfg(loom)]
|
|
300
|
+
use loom::sync::atomic::{AtomicUsize, Ordering};
|
|
301
|
+
#[cfg(not(loom))]
|
|
302
|
+
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Running Loom Tests
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Loom tests only (use cfg flag)
|
|
309
|
+
RUSTFLAGS="--cfg loom" cargo test --lib -- loom_tests
|
|
310
|
+
|
|
311
|
+
# With release optimizations (loom is slow)
|
|
312
|
+
RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Loom + Miri Interaction
|
|
316
|
+
|
|
317
|
+
Loom and Miri solve different problems:
|
|
318
|
+
- **Miri** checks a single execution for UB (aliasing, validity, provenance).
|
|
319
|
+
- **Loom** checks all interleavings for correctness (ordering, atomicity).
|
|
320
|
+
|
|
321
|
+
Run BOTH on lock-free code:
|
|
322
|
+
```bash
|
|
323
|
+
# Step 1: loom for interleaving correctness
|
|
324
|
+
RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests
|
|
325
|
+
|
|
326
|
+
# Step 2: Miri for UB in each path
|
|
327
|
+
cargo +nightly miri test -- concurrent_tests
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Cargo-Fuzz — Property-Based UB Hunting
|
|
333
|
+
|
|
334
|
+
Fuzzing generates random inputs to maximize code coverage and find crashes, panics, and UB.
|
|
335
|
+
|
|
336
|
+
### Setup
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
cargo install cargo-fuzz
|
|
340
|
+
cargo fuzz init
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Fuzz Target
|
|
344
|
+
|
|
345
|
+
```rust
|
|
346
|
+
// fuzz/fuzz_targets/parse_input.rs
|
|
347
|
+
#![no_main]
|
|
348
|
+
use libfuzzer_sys::fuzz_target;
|
|
349
|
+
|
|
350
|
+
fuzz_target!(|data: &[u8]| {
|
|
351
|
+
// Your parsing/deserialization/processing code here.
|
|
352
|
+
// If it panics or triggers UB, the fuzzer catches it.
|
|
353
|
+
let _ = my_crate::parse(data);
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Running
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
# Run until interrupted
|
|
361
|
+
cargo +nightly fuzz run parse_input
|
|
362
|
+
|
|
363
|
+
# Run with ASAN (catches memory bugs in unsafe code)
|
|
364
|
+
cargo +nightly fuzz run parse_input -- -rss_limit_mb=4096
|
|
365
|
+
|
|
366
|
+
# Minimize a crashing input
|
|
367
|
+
cargo +nightly fuzz tmin parse_input artifacts/parse_input/crash-xxxxx
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Fuzz + Miri Pipeline
|
|
371
|
+
|
|
372
|
+
When the fuzzer finds a crashing input:
|
|
373
|
+
1. Minimize it with `cargo fuzz tmin`.
|
|
374
|
+
2. Add it as a regression test.
|
|
375
|
+
3. Run the regression test under Miri to classify whether it is a panic (safe) or UB (must fix).
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# After adding the input as a test case:
|
|
379
|
+
cargo +nightly miri test -- test_fuzz_regression_001
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Tool Selection Decision Tree
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
Start
|
|
388
|
+
│
|
|
389
|
+
├── Is it pure Rust (no FFI, no I/O)?
|
|
390
|
+
│ YES → Miri (full paranoia flags)
|
|
391
|
+
│ │ └── Also: loom (if atomics/lock-free)
|
|
392
|
+
│ │ └── Also: proptest (if parsing/serialization)
|
|
393
|
+
│ │ └── Also: cargo-fuzz (if untrusted input)
|
|
394
|
+
│ │
|
|
395
|
+
│ NO → Does it involve FFI?
|
|
396
|
+
│ YES → ASAN + MSAN on integration tests
|
|
397
|
+
│ │ └── Miri on the Rust-side handling
|
|
398
|
+
│ │ └── cbindgen in CI for layout verification
|
|
399
|
+
│ │
|
|
400
|
+
│ NO → Is it I/O-heavy?
|
|
401
|
+
│ YES → TSAN for thread safety
|
|
402
|
+
│ │ └── Miri with -Zmiri-disable-isolation where possible
|
|
403
|
+
│ │
|
|
404
|
+
│ NO → Miri (full paranoia flags)
|
|
405
|
+
│
|
|
406
|
+
└── Always: Miri is the default. Other tools supplement.
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## The One Rule
|
|
410
|
+
|
|
411
|
+
> **When in doubt, run Miri.** If Miri cannot run it, write a version it can run, and test that under Miri. Then test the real version under sanitizers. Never ship `unsafe` code that has not passed Miri.
|