From e65258cb6d1b3c92aca34a8cb851b65ba83fee53 Mon Sep 17 00:00:00 2001 From: Danny Canter Date: Sat, 9 May 2026 17:09:50 -0700 Subject: [PATCH] vminitd: Fix UDS length issue with large container IDs Today we craft the proxied sockets we create at a path that contains the container ID. This is normally completely fine, except if the ID is enormous, as it can cause the UDS path to be longer than the allowed length. --- Sources/Containerization/LinuxContainer.swift | 8 +-- Sources/Containerization/LinuxPod.swift | 8 +-- Sources/Integration/ContainerTests.swift | 58 +++++++++++++++++++ Sources/Integration/Suite.swift | 1 + 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/Sources/Containerization/LinuxContainer.swift b/Sources/Containerization/LinuxContainer.swift index b8eda640..d572cff2 100644 --- a/Sources/Containerization/LinuxContainer.swift +++ b/Sources/Containerization/LinuxContainer.swift @@ -447,8 +447,8 @@ public final class LinuxContainer: Container, Sendable { "/run/container/\(id)/rootfs" } - private static func guestSocketStagingPath(_ containerID: String, socketID: String) -> String { - "/run/container/\(containerID)/sockets/\(socketID).sock" + private static func guestSocketStagingPath(_ socketID: String) -> String { + "/run/container/sockets/\(socketID).sock" } } @@ -693,7 +693,7 @@ extension LinuxContainer { mounts.append( ContainerizationOCI.Mount( type: "bind", - source: Self.guestSocketStagingPath(self.id, socketID: socket.id), + source: Self.guestSocketStagingPath(socket.id), destination: socket.destination.path, options: ["bind"] )) @@ -1030,7 +1030,7 @@ extension LinuxContainer { let port: UInt32 if socket.direction == .into { port = self.hostVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue - socket.destination = URL(filePath: Self.guestSocketStagingPath(self.id, socketID: socket.id)) + socket.destination = URL(filePath: Self.guestSocketStagingPath(socket.id)) } else { port = self.guestVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue socket.source = rootInGuest.appending(path: socket.source.path) diff --git a/Sources/Containerization/LinuxPod.swift b/Sources/Containerization/LinuxPod.swift index 63c96ebc..bb91a76d 100644 --- a/Sources/Containerization/LinuxPod.swift +++ b/Sources/Containerization/LinuxPod.swift @@ -296,8 +296,8 @@ public final class LinuxPod: Sendable { "/run/container/\(containerID)/rootfs" } - private static func guestSocketStagingPath(_ containerID: String, socketID: String) -> String { - "/run/container/\(containerID)/sockets/\(socketID).sock" + private static func guestSocketStagingPath(_ socketID: String) -> String { + "/run/container/sockets/\(socketID).sock" } private static func guestVolumePath(_ volumeName: String) -> String { @@ -659,7 +659,7 @@ extension LinuxPod { mounts.append( ContainerizationOCI.Mount( type: "bind", - source: Self.guestSocketStagingPath(containerID, socketID: socket.id), + source: Self.guestSocketStagingPath(socket.id), destination: socket.destination.path, options: ["bind"] )) @@ -1057,7 +1057,7 @@ extension LinuxPod { let port: UInt32 if socket.direction == .into { port = self.hostVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue - socket.destination = URL(filePath: Self.guestSocketStagingPath(containerID, socketID: socket.id)) + socket.destination = URL(filePath: Self.guestSocketStagingPath(socket.id)) } else { port = self.guestVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue socket.source = rootInGuest.appending(path: socket.source.path) diff --git a/Sources/Integration/ContainerTests.swift b/Sources/Integration/ContainerTests.swift index eaf6e50b..404bfb43 100644 --- a/Sources/Integration/ContainerTests.swift +++ b/Sources/Integration/ContainerTests.swift @@ -1121,6 +1121,64 @@ extension IntegrationSuite { } } + // NOTE: Once upon a time our guest agent created any proxied unix sockets at + // a path that contained the container ID in it. The problem here is if the container + // ID is comically long we exceed the max length of a unix domain socket path. + func testUnixSocketIntoGuestLongContainerID() async throws { + let id = "test-unixsocket-long-id-" + String(repeating: "a", count: 40) + + let bs = try await bootstrap(id) + + let hostSocketPath = try createHostUnixSocket() + + let buffer = BufferWriter() + let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in + config.process.arguments = ["sleep", "100"] + config.sockets = [ + UnixSocketConfiguration( + source: URL(filePath: hostSocketPath), + destination: URL(filePath: "/tmp/test.sock"), + direction: .into + ) + ] + config.bootLog = bs.bootLog + } + + do { + try await container.create() + try await container.start() + + let lsExec = try await container.exec("ls-socket") { config in + config.arguments = ["ls", "-l", "/tmp/test.sock"] + config.stdout = buffer + } + + try await lsExec.start() + let status = try await lsExec.wait() + try await lsExec.delete() + + guard status.exitCode == 0 else { + throw IntegrationError.assert(msg: "ls command failed with status \(status)") + } + + guard let output = String(data: buffer.data, encoding: .utf8) else { + throw IntegrationError.assert(msg: "failed to convert ls output to UTF8") + } + + guard output.hasPrefix("s") else { + throw IntegrationError.assert( + msg: "expected socket file (starting with 's'), got: \(output)") + } + + try await container.kill(.kill) + try await container.wait() + try await container.stop() + } catch { + try? await container.stop() + throw error + } + } + func testNonClosureConstructor() async throws { let id = "test-container-non-closure-constructor" diff --git a/Sources/Integration/Suite.swift b/Sources/Integration/Suite.swift index 9eec1248..a0e0ba91 100644 --- a/Sources/Integration/Suite.swift +++ b/Sources/Integration/Suite.swift @@ -313,6 +313,7 @@ struct IntegrationSuite: AsyncParsableCommand { Test("container memory events OOM kill", testMemoryEventsOOMKill), Test("container no serial console", testNoSerialConsole), Test("unix socket into guest", testUnixSocketIntoGuest), + Test("unix socket into guest long container id", testUnixSocketIntoGuestLongContainerID), Test("unix socket into guest symlink", testUnixSocketIntoGuestSymlink), Test("container non-closure constructor", testNonClosureConstructor), Test("container test large stdio ingest", testLargeStdioOutput),