Skip to content

budget_config RwLock .unwrap() panics on poison, breaking every LLM turn #3447

Description

@houko

Severity

P2 — first poisoned read crashes every subsequent LLM turn with a budget check.

Files

  • crates/librefang-kernel/src/kernel/mod.rs:756
  • crates/librefang-kernel/src/kernel/mod.rs:765

Finding

budget_config() and update_budget_config() use
self.budget_config.read().unwrap() / .write().unwrap(). If any thread panics
while holding the write guard (e.g., the FnOnce closure in
update_budget_config panics — it's user-supplied via f), the lock becomes
poisoned and every subsequent budget read panics, including the hot path
called on every LLM turn for cost gating.

Other places in this same file already use
unwrap_or_else(|e| e.into_inner()) (e.g., skill_registry.read() at line 5927),
demonstrating awareness — budget_config was not migrated.

Evidence

// kernel/mod.rs:755-767
pub fn budget_config(&self) -> librefang_types::config::BudgetConfig {
    self.budget_config.read().unwrap().clone()
}
pub fn update_budget_config(&self, f: impl FnOnce(&mut BudgetConfig)) {
    let mut guard = self.budget_config.write().unwrap();
    f(&mut guard);  // user-supplied closure — panic poisons the lock
}

Suggested Fix

Replace both with .read().unwrap_or_else(|e| e.into_inner()) and
.write().unwrap_or_else(|e| e.into_inner()), matching the pattern at
line 5927. Optionally wrap the FnOnce in
std::panic::AssertUnwindSafe + catch_unwind to surface budget-mutation panics
as errors instead of poison.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/kernelCore kernel (scheduling, RBAC, workflows)area/runtimeAgent loop, LLM drivers, WASM sandboxbugSomething isn't workingseverity/mediumBug or limitation with workaround / partial impact

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions