Skip to content

Commit 829f773

Browse files
committed
fix(server): eliminate TOCTOU races in MCP server and plugin registry
- server.rs: Hold write lock during entire state check and modification in handle_initialize to prevent concurrent initialization races - registry.rs: Use HashMap entry API to atomically check-and-insert plugin registration to prevent duplicate registration races Fixes #5262, #5260
1 parent 440a502 commit 829f773

File tree

2 files changed

+21
-15
lines changed

2 files changed

+21
-15
lines changed

src/cortex-mcp-server/src/server.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,17 @@ impl McpServer {
222222
}
223223

224224
async fn handle_initialize(&self, params: Option<Value>) -> Result<Value, JsonRpcError> {
225-
// Check state
226-
let current_state = *self.state.read().await;
227-
if current_state != ServerState::Uninitialized {
228-
return Err(JsonRpcError::invalid_request("Server already initialized"));
225+
// Atomic check-and-transition: hold write lock during entire state check and modification
226+
// to prevent TOCTOU race conditions where multiple concurrent initialize requests
227+
// could both pass the uninitialized check before either sets the state
228+
{
229+
let mut state_guard = self.state.write().await;
230+
if *state_guard != ServerState::Uninitialized {
231+
return Err(JsonRpcError::invalid_request("Server already initialized"));
232+
}
233+
*state_guard = ServerState::Initializing;
229234
}
230235

231-
*self.state.write().await = ServerState::Initializing;
232-
233236
// Parse params
234237
let init_params: InitializeParams = params
235238
.map(serde_json::from_value)

src/cortex-plugins/src/registry.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -674,18 +674,21 @@ impl PluginRegistry {
674674
let info = plugin.info().clone();
675675
let id = info.id.clone();
676676

677-
{
678-
let plugins = self.plugins.read().await;
679-
if plugins.contains_key(&id) {
680-
return Err(PluginError::AlreadyExists(id));
681-
}
682-
}
683-
677+
// Use entry API to atomically check-and-insert within a single write lock
678+
// to prevent TOCTOU race conditions where multiple concurrent registrations
679+
// could both pass the contains_key check before either inserts
684680
let handle = PluginHandle::new(plugin);
685-
686681
{
687682
let mut plugins = self.plugins.write().await;
688-
plugins.insert(id.clone(), handle);
683+
use std::collections::hash_map::Entry;
684+
match plugins.entry(id.clone()) {
685+
Entry::Occupied(_) => {
686+
return Err(PluginError::AlreadyExists(id));
687+
}
688+
Entry::Vacant(entry) => {
689+
entry.insert(handle);
690+
}
691+
}
689692
}
690693

691694
{

0 commit comments

Comments
 (0)