cap outstanding DNS-resolver threads

Each DNS lookup for the SSRF guard spawns a thread that is never joined and can hang for the full OS resolver timeout, so a black-holed keyserver leaks one thread+stack per attempt. Track outstanding resolver threads with an atomic counter; over a cap of 4, report the host as unreachable instead of spawning another. Decrement only when the (possibly long-hung) lookup actually returns, so the cap reflects threads truly in flight.
This commit is contained in:
Matthew Jackson
2026-09-05 12:37:53 -07:00
parent 4a241e04bc
commit 7c544e3780
+16
View File
@@ -153,8 +153,21 @@ fn resolve_and_guard(url: &str) -> Result<Vec<SocketAddr>, (GuardFail, String)>
// resolver timeout and freeze the calling rip thread, so run it on a
// spawned thread with a bounded deadline (mirrors autorip/libfreemkv).
let addrs: Vec<SocketAddr> = {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc;
const DNS_TIMEOUT: Duration = Duration::from_secs(10);
// Each resolver thread can hang for the OS timeout and is never joined,
// so a black-holed keyserver leaks one thread+stack per attempt. Cap the
// outstanding ones; over the cap, report the host unreachable instead.
const MAX_DNS_THREADS: usize = 4;
static DNS_THREADS: AtomicUsize = AtomicUsize::new(0);
if DNS_THREADS.fetch_add(1, Ordering::SeqCst) >= MAX_DNS_THREADS {
DNS_THREADS.fetch_sub(1, Ordering::SeqCst);
return Err((
GuardFail::Unreachable,
"too many concurrent DNS resolutions in flight".into(),
));
}
let host = host.clone();
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
@@ -163,6 +176,9 @@ fn resolve_and_guard(url: &str) -> Result<Vec<SocketAddr>, (GuardFail, String)>
.map(|it| it.collect::<Vec<SocketAddr>>());
// Receiver may be gone after the timeout — ignore the send error.
let _ = tx.send(res);
// Decrement only when the (possibly long-hung) lookup actually
// returns, so the cap reflects threads truly in flight.
DNS_THREADS.fetch_sub(1, Ordering::SeqCst);
});
match rx.recv_timeout(DNS_TIMEOUT) {
Ok(Ok(addrs)) => addrs,