Skip to content

fix(core): fix broken sweeper abort path in SpeculationEngine::new (no-supervisor branch) #3645

@bug-ops

Description

@bug-ops

Description

Security audit of PR #3640 identified a resource leak in the no-supervisor branch of SpeculationEngine::new_with_supervisor (and new) in crates/zeph-core/src/agent/speculative/mod.rs.

Actual Behavior

In the supervisor = None branch:

  1. tokio::spawn(sweeper_task) returns a JoinHandle
  2. jh.abort_handle() is called to get an AbortHandle
  3. std::mem::forget(jh) discards the JoinHandle
  4. The AbortHandle is immediately dropped without being stored

The TaskHandle stored in self.sweeper does NOT abort the real tokio task on Drop. The sweeper runs until the tokio runtime shuts down.

Impact

Not a memory-safety issue (safe Rust). The sweeper iterates an empty HashMap every 5 seconds (negligible CPU). However, in long-running test contexts or when the SpeculationEngine is dropped before the runtime shuts down, the sweeper task leaks.

Recommended Fix

Replace the dummy TaskHandle approach with Option<tokio::task::JoinHandle<()>> stored in the struct and aborted in Drop:

impl Drop for SpeculationEngine {
    fn drop(&mut self) {
        if let Some(handle) = self.sweeper.take() {
            handle.abort();
        }
    }
}

Files

  • crates/zeph-core/src/agent/speculative/mod.rs

Depends on

#3636 (merged in PR #3640)

Metadata

Metadata

Assignees

Labels

P3Research — medium-high complexitybugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions