From 96f94a2dfafb892123be173777d4305097f46f7c Mon Sep 17 00:00:00 2001 From: Aaron Paterson Date: Fri, 21 Aug 2026 01:03:46 +0000 Subject: [PATCH] Keep relaying after a connection the host end cannot serve A guest connection is served by opening one to the socket the relay was given, and that open fails whenever nothing accepts there any more: the process holding it exited, the socket was replaced, or the path names something that is no longer listening. The failure left the accept loop, so the listener stopped and the relay served nothing further. The socket stayed where it was, so every later caller found it, connected, and waited on a relay that had gone. A caller met neither the service nor the refusal that would have sent it to whatever it does without one. Fail the connection instead of the relay, and close the connection so the caller is told. A connect that never reaches the relay closes its own socket on the way out rather than leaving the descriptor behind. --- .../Containerization/UnixSocketRelay.swift | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/Sources/Containerization/UnixSocketRelay.swift b/Sources/Containerization/UnixSocketRelay.swift index 4069bf4e6..49770dd6e 100644 --- a/Sources/Containerization/UnixSocketRelay.swift +++ b/Sources/Containerization/UnixSocketRelay.swift @@ -138,18 +138,31 @@ extension UnixSocketRelay { state.withLock { $0.listener = listener $0.t = Task { - do { - defer { try? listener.finish() } - for await connection in listener { + defer { try? listener.finish() } + for await connection in listener { + do { try await self.handleGuestVsockConn( vsockConn: connection, hostConnectionPath: hostPath, port: self.port, log: self.log ) + } catch { + // What the host end cannot serve is this connection's + // failure and not the relay's. Closing the connection + // hands the caller the refusal it can act on, where + // giving up the loop would leave the socket in place + // with nothing behind it, and every later caller + // waiting on a relay that stopped listening. + try? connection.close() + self.log?.error( + "failed to relay a guest connection", + metadata: [ + "vport": "\(self.port)", + "path": "\(hostPath.path)", + "error": "\(error)", + ]) } - } catch { - self.log?.error("failed to setup relay between vsock \(self.port) and \(hostPath.path): \(error)") } } } @@ -199,7 +212,14 @@ extension UnixSocketRelay { "hostFd": "\(hostSocket.fileDescriptor)", "guestFd": "\(vsockConn.fileDescriptor)", ]) - try hostSocket.connect() + // The socket outlives this scope only when it is handed to the relay, + // so a connect that never gets there closes it on the way out. + do { + try hostSocket.connect() + } catch { + try? hostSocket.close() + throw error + } do { try await self.relay(