From 4a917a74de94e1e2753f9ec5951ee9fa776377f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Mon, 16 Feb 2026 12:18:11 +0100 Subject: [PATCH 1/2] Fix crash on Linux when testing the archive plugin (#641) Fix a rare crash in CI on the archive plugin. The crash is a race condition in `PluginUtils.swift`'s execute method. The code set a `terminationHandler` on the `Process` that called `readToEnd()` on the pipe, while simultaneously the `readabilityHandler` could still be firing on the same pipe's file handle. On Linux (x86_64, Ubuntu 24.04 in CI), this race corrupts memory during Swift runtime metadata resolution (`swift_conformsToProtocol, _swift_getGenericMetadata`), which manifests as the `SIGSEGV` we're seeing in `_dispatch_event_loop_drain`. The fix: I removed the `terminationHandler` entirely. Since `waitUntilExit()` is already called synchronously, we know the process is done. After `waitUntilExit()`, we set `readabilityHandler = nil` to stop the async reads, then do one final `readToEnd()` on the output queue to drain any remaining data. This eliminates the race between the readability handler and the termination handler competing over the same file handle. Co-authored-by: Sebastien Stormacq --- Plugins/AWSLambdaPackager/PluginUtils.swift | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Plugins/AWSLambdaPackager/PluginUtils.swift b/Plugins/AWSLambdaPackager/PluginUtils.swift index cce60fab..02f48bc8 100644 --- a/Plugins/AWSLambdaPackager/PluginUtils.swift +++ b/Plugins/AWSLambdaPackager/PluginUtils.swift @@ -92,16 +92,19 @@ struct Utils { if let customWorkingDirectory { process.currentDirectoryURL = URL(fileURLWithPath: customWorkingDirectory.path()) } - process.terminationHandler = { _ in - outputQueue.async { - outputHandler(try? pipe.fileHandleForReading.readToEnd()) - } - } try process.run() process.waitUntilExit() - // wait for output to be full processed + // Stop the readability handler before reading remaining data to avoid + // a race between the handler and readToEnd() on Linux, which can cause + // a SIGSEGV in the Swift runtime's metadata resolution. + pipe.fileHandleForReading.readabilityHandler = nil + + // Read any remaining data from the pipe + outputQueue.async { outputHandler(try? pipe.fileHandleForReading.readToEnd()) } + + // wait for output to be fully processed outputSync.wait() let output = outputMutex.withLock { $0 } From 11ef057299747f39514cd998ba0e76f3a6f186ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 17 Feb 2026 08:52:43 +0100 Subject: [PATCH 2/2] Fix more crashes in archive (#642) ## Issue # Related to intermittent CI crashes with Signal 11 (SIGSEGV) during `swift package archive` on Linux x86_64 (Ubuntu 24.04) ## Description of the changes Replaces `FileHandle.readabilityHandler` with a manual blocking read loop using `availableData` on a serial `DispatchQueue` in the `Utils.execute` method of the AWSLambdaPackager plugin. On Linux, `FileHandle.readabilityHandler`'s setter internally calls `_bridgeAnythingToObjectiveC`, which triggers `swift_dynamicCast` / `swift_conformsToProtocol`. When the Swift runtime is concurrently resolving type metadata on other threads, this causes a bad pointer dereference crash in `_dispatch_event_loop_drain`. The new approach uses a `while` loop calling `availableData` (blocks until data arrives, returns empty `Data` at EOF), completely avoiding the problematic Foundation/ObjC bridging code paths. ## New/existing dependencies impact assessment, if applicable No new dependencies were added to this change. ## Conventional Commits `fix: replace FileHandle.readabilityHandler with manual read loop in AWSLambdaPackager plugin` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Sebastien Stormacq --- Plugins/AWSLambdaPackager/PluginUtils.swift | 29 +++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Plugins/AWSLambdaPackager/PluginUtils.swift b/Plugins/AWSLambdaPackager/PluginUtils.swift index 02f48bc8..8cb7e22e 100644 --- a/Plugins/AWSLambdaPackager/PluginUtils.swift +++ b/Plugins/AWSLambdaPackager/PluginUtils.swift @@ -80,9 +80,6 @@ struct Utils { } let pipe = Pipe() - pipe.fileHandleForReading.readabilityHandler = { fileHandle in - outputQueue.async { outputHandler(fileHandle.availableData) } - } let process = Process() process.standardOutput = pipe @@ -93,17 +90,27 @@ struct Utils { process.currentDirectoryURL = URL(fileURLWithPath: customWorkingDirectory.path()) } + // Read from the pipe on a background thread using a manual read loop. + // We avoid FileHandle.readabilityHandler because on Linux its setter + // triggers _bridgeAnythingToObjectiveC / swift_dynamicCast which can + // crash with a SIGSEGV during concurrent Swift runtime metadata resolution. + let readFileHandle = pipe.fileHandleForReading + outputSync.enter() + outputQueue.async { + defer { outputSync.leave() } + // Read in a loop until EOF + while true { + let data = readFileHandle.availableData + if data.isEmpty { + break // EOF + } + outputHandler(data) + } + } + try process.run() process.waitUntilExit() - // Stop the readability handler before reading remaining data to avoid - // a race between the handler and readToEnd() on Linux, which can cause - // a SIGSEGV in the Swift runtime's metadata resolution. - pipe.fileHandleForReading.readabilityHandler = nil - - // Read any remaining data from the pipe - outputQueue.async { outputHandler(try? pipe.fileHandleForReading.readToEnd()) } - // wait for output to be fully processed outputSync.wait()