From 32e06d5967ff1db31379e82355d431c1bbd588d8 Mon Sep 17 00:00:00 2001 From: Trinity Agent Date: Wed, 11 Mar 2026 20:11:53 +0000 Subject: [PATCH] =?UTF-8?q?fix(mcp):=20memory=20leak=20=E2=80=94=20WebSock?= =?UTF-8?q?et=20frame=20payload=20not=20freed=20in=20handleConnection=20lo?= =?UTF-8?q?op=20(#205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added `allocated` flag to Frame struct to track when payload memory was allocated during unmasking. Added defer in handleConnection to free the payload when it was allocated, preventing memory leak on masked frames. Co-Authored-By: Claude Opus 4.6 --- tools/mcp/trinity_mcp/websocket_transport.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/mcp/trinity_mcp/websocket_transport.zig b/tools/mcp/trinity_mcp/websocket_transport.zig index 9a28758c3c..4493774101 100644 --- a/tools/mcp/trinity_mcp/websocket_transport.zig +++ b/tools/mcp/trinity_mcp/websocket_transport.zig @@ -31,6 +31,8 @@ pub const Frame = struct { fin: bool, opcode: Opcode, payload: []const u8, + /// True if payload was allocated and must be freed by caller + allocated: bool, }; /// WebSocket Server for MCP protocol @@ -131,6 +133,8 @@ pub const WebSocketServer = struct { std.log.err("Frame parse error: {}", .{err}); continue; }; + // Free allocated payload after processing (prevents memory leak on masked frames) + defer if (frame.allocated) self.allocator.free(frame.payload); // Handle different opcodes switch (frame.opcode) { @@ -246,18 +250,21 @@ pub const WebSocketServer = struct { // Unmask payload if needed var payload: []const u8 = payload_data; + var allocated = false; if (masked) { const unmasked = try self.allocator.alloc(u8, payload_len); for (0..payload_len) |i| { unmasked[i] = payload_data[i] ^ masking_key[i % 4]; } payload = unmasked; + allocated = true; } return Frame{ .fin = fin, .opcode = opcode, .payload = payload, + .allocated = allocated, }; }