Severity
P1 — repeated reload cycles accumulate orphan node/python processes holding fds, sockets, workspace locks.
Files
crates/librefang-runtime-mcp/src/lib.rs:1009-1036 (connect_stdio)
Finding
connect_stdio builds tokio::process::Command and hands it to
TokioChildProcess::new(...) without ever calling .kill_on_drop(true).
Tokio defaults kill_on_drop to false, so when an McpConnection is
dropped (server config removed, hot reload, daemon panic, OAuth retry
replacing the connection) the subprocess is only signaled via stdin
EOF.
A misbehaving MCP server that ignores EOF (or is mid-write) becomes a
zombie running indefinitely, holding file descriptors, network sockets, and
potentially a lock on workspace files. There is also no Drop impl on
McpConnection or stored Child handle that calls .kill() explicitly,
and no test asserting the child dies.
Evidence
let transport = TokioChildProcess::new(
tokio::process::Command::new(&resolved_command).configure(|cmd| {
cmd.args(&args_owned);
cmd.env_clear();
// ... no .kill_on_drop(true), no .stderr(...) call
Suggested Fix
Inside the configure closure add cmd.kill_on_drop(true);. Optionally
implement Drop for McpConnection to abort lingering tasks and explicitly
kill() the rmcp child handle if TokioChildProcess exposes one.
Severity
P1 — repeated reload cycles accumulate orphan node/python processes holding fds, sockets, workspace locks.
Files
crates/librefang-runtime-mcp/src/lib.rs:1009-1036(connect_stdio)Finding
connect_stdiobuildstokio::process::Commandand hands it toTokioChildProcess::new(...)without ever calling.kill_on_drop(true).Tokio defaults
kill_on_droptofalse, so when anMcpConnectionisdropped (server config removed, hot reload, daemon panic, OAuth retry
replacing the connection) the subprocess is only signaled via stdin
EOF.
A misbehaving MCP server that ignores EOF (or is mid-write) becomes a
zombie running indefinitely, holding file descriptors, network sockets, and
potentially a lock on workspace files. There is also no
Dropimpl onMcpConnectionor storedChildhandle that calls.kill()explicitly,and no test asserting the child dies.
Evidence
Suggested Fix
Inside the
configureclosure addcmd.kill_on_drop(true);. Optionallyimplement
Drop for McpConnectionto abort lingering tasks and explicitlykill()the rmcp child handle ifTokioChildProcessexposes one.