一句偷懒的提示词如何让 Codex 找出 RustSec 漏洞
Original title: A Lazy Prompt Turned Into a RustSec Advisory
The title and summary in the selected language are awaiting translation.
作者把一句要求证明影响或可利用性的红队提示词交给 Codex,在 Rust crate intaglio 中发现 intern 实现的 unwind 安全问题。
作者用一个极简提示词让 Codex 在 Rust crate 中找出真实漏洞并走完修复与披露流程,可借鉴的是把标准定在可复现而非猜测。
Authorship note: Codex, a coding agent, drafted this post from a repo-local session transcript and notes. I rewrote it heavily and published it.
Using coding models for security hardening is amazing. I pointed Codex at one of
my Rust crates, intaglio, with a laughably lazy prompt and got a real
vulnerability out of it: RUSTSEC-2026-0078. Nothing about the prompt was
clever. The useful part was the bar: do a complete analysis and prove impact or
exploitability for anything you report.

The Prompt
you are red teaming this repo to look for security vulnerabilities. do a complete analysis. you must prove impact or exploitability for anything you report to me
This prompt has been super productive because it forbids speculative bug reports. “This looks suspicious” is not enough. The model has to build a reproducer, show the bad behavior, and narrow the claim to what it can actually prove.
The Bug
Codex found a cross-cutting unwind-safety bug in intaglio’s intern
implementation. The write ordering was wrong: intern pushed into the backing
Vec before it inserted the matching key into the HashMap. If a custom
BuildHasher panicked during HashMap::insert and the embedding application
recovered with catch_unwind, the interner could keep going with its two
internal indexes out of sync.
use intaglio::SymbolTable;
use std::{
hash::{BuildHasher, Hasher},
panic::{catch_unwind, AssertUnwindSafe},
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
#[derive(Clone, Debug)]
struct PanicBuildHasher {
builds: Arc<AtomicUsize>,
}
impl BuildHasher for PanicBuildHasher {
type Hasher = PanicHasher;
fn build_hasher(&self) -> Self::Hasher {
let build = self.builds.fetch_add(1, Ordering::SeqCst) + 1;
PanicHasher { build, hash: 0 }
}
}
#[derive(Debug)]
struct PanicHasher {
build: usize,
hash: u64,
}
impl Hasher for PanicHasher {
fn finish(&self) -> u64 { self.hash }
fn write(&mut self, bytes: &[u8]) {
if self.build == 1 {
panic!("panic during first HashMap::insert hashing");
}
for byte in bytes {
self.hash = self.hash.wrapping_mul(131).wrapping_add(u64::from(*byte));
}
}
}
fn main() {
let mut table = SymbolTable::with_hasher(PanicBuildHasher { builds: Arc::new(AtomicUsize::new(0)) });
let first = catch_unwind(AssertUnwindSafe(|| table.intern("attacker")));
println!(
"first_err={} len={} check(attacker)={:?}",
first.is_err(), table.len(), table.check_interned("attacker"),
);
let sym = table.intern("victim").expect("second intern succeeds in release mode");
println!("victim_symbol={}", sym.id());
println!("check(victim)={:?}", table.check_interned("victim"));
println!("get(victim_symbol)={:?}", table.get(sym));
}In release mode, that turned into symbol confusion. intern("attacker") could
panic on the first insert, the caller could catch the panic and keep going, and
a later intern("victim") could return Symbol(0) even though get(Symbol(0))
resolved to "attacker". That is enough for an advisory.
I did not prove memory unsafety on the default RandomState path. The bug
needed a custom BuildHasher, a panic during hashing, and a caller that kept
using the table after catch_unwind. That was the bug.
Carrying It Through
The agent did the full job end to end, from identification and fix to vuln publication.
The paper trail is public:
- issue #359
- fix PR #360
- advisory RUSTSEC-2026-0078
The Same Prompt Elsewhere
This same prompt found at least something in every active Artichoke crate I pointed it at:
- missing
npm ciinstall in one Prettier GitHub Actions job rand_mtdocs that over-emphasized thenew_unseededconstructorraw-partsmissingcompile_failcoverage forSendandSyncauto traits and lifetime expansionsysdirtests that were not robust toNEXT_ROOTbeing set, plus missing docs aroundNEXT_ROOTand~in returned paths
This workflow works because the standard is high and the prompt is simple. Do not ask the model to brainstorm bugs. Ask it to prove them. That turns the output from security-flavored vibes into engineering work: reproducer, tests, fix, release, advisory.
Source: Ryan Lopopolo · hyperbo.la