core: generic spawn_crypto helper; offload all slow KDF hashing (OPER pbkdf2 too, and MKPASSWD) off the core thread

This commit is contained in:
Jean Chevronnet 2026-08-12 13:05:29 +00:00
parent e246699fef
commit 7cb58586b4
4 changed files with 83 additions and 17 deletions

View file

@ -450,24 +450,36 @@ impl Server {
});
}
/// Verify an OPER password on a worker thread, delivering the result back as
/// `Event::OperAuth`. bcrypt is deliberately expensive (tens to hundreds of ms),
/// so running it inline would freeze the single-threaded core — and an OPER flood
/// against a bcrypt block would be a trivial DoS. Bounded so the flood can't spawn
/// unlimited hash threads; returns `false` when at capacity.
pub fn spawn_auth(&self, uid: Uid, hash: String, pass: String, level: u32) -> bool {
/// Run an expensive credential operation (a KDF: bcrypt / pbkdf2) on a worker
/// thread and deliver its result back as the `Event` the closure builds. These
/// hashes are deliberately slow (tens to hundreds of ms), so running one inline
/// would freeze the single-threaded core — and a flood of them (OPER, TITLE, …)
/// against a KDF credential would be a trivial DoS. Bounded so the flood can't
/// spawn unlimited threads; returns `false` when at capacity (the caller then
/// rejects the attempt). A `Drop` guard keeps the counter correct even if the
/// closure panics.
pub fn spawn_crypto<F>(&self, f: F) -> bool
where
F: FnOnce() -> crate::ircd::Event + Send + 'static,
{
use std::sync::atomic::{AtomicUsize, Ordering};
static ACTIVE: AtomicUsize = AtomicUsize::new(0);
const MAX_ACTIVE: usize = 16;
struct Guard;
impl Drop for Guard {
fn drop(&mut self) {
ACTIVE.fetch_sub(1, Ordering::Relaxed);
}
}
if ACTIVE.fetch_add(1, Ordering::Relaxed) >= MAX_ACTIVE {
ACTIVE.fetch_sub(1, Ordering::Relaxed);
return false;
}
let tx = self.event_tx.clone();
std::thread::spawn(move || {
let ok = crate::modules::password_hash::verify(&hash, &pass);
ACTIVE.fetch_sub(1, Ordering::Relaxed);
let _ = tx.send(crate::ircd::Event::OperAuth { uid, ok, level });
let _guard = Guard; // decrements even on panic
let ev = f();
let _ = tx.send(ev);
});
true
}