From 4615aa063073164237bd8df5707ae49088074d1d Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Sat, 15 Jun 2024 23:47:10 -0700 Subject: [PATCH 1/2] Add a thread-safe implementation of Process.currentDirectoryURL posix_spawn_file_actions_addchdir_np is the official way to set the working directory of a spawned process and is supported on both macOS and glibc (Linux, etc.). This makes Process.currentDirectoryURL thread-safe, as the current approach will result in the working directory being nondeterministically assigned when spawning processes across multiple threads, using different working directories. --- Sources/CoreFoundation/CFPlatform.c | 29 +++++++++++++++++++ .../include/ForSwiftFoundationOnly.h | 1 + Sources/Foundation/Process.swift | 14 ++------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Sources/CoreFoundation/CFPlatform.c b/Sources/CoreFoundation/CFPlatform.c index 38d8c5278e..b13b778c32 100644 --- a/Sources/CoreFoundation/CFPlatform.c +++ b/Sources/CoreFoundation/CFPlatform.c @@ -57,6 +57,10 @@ #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding() #endif +#ifndef __GLIBC_PREREQ +#define __GLIBC_PREREQ(maj, min) 0 +#endif + extern void __CFGetUGIDs(uid_t *euid, gid_t *egid); #if TARGET_OS_MAC @@ -2274,6 +2278,31 @@ CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_ return posix_spawn_file_actions_addclose((posix_spawn_file_actions_t *)file_actions, filedes); } +CF_EXPORT int _CFPosixSpawnFileActionsChdir(_CFPosixSpawnFileActionsRef file_actions, const char *path) { + #if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 29) + // Glibc versions prior to 2.29 don't support posix_spawn_file_actions_addchdir_np, impacting: + // - Amazon Linux 2 (EoL mid-2025) + return ENOSYS; + #elif defined(__GLIBC__) || TARGET_OS_DARWIN || defined(__FreeBSD__) || defined(__ANDROID__) + // Pre-standard posix_spawn_file_actions_addchdir_np version available in: + // - Solaris 11.3 (October 2015) + // - Glibc 2.29 (February 2019) + // - macOS 10.15 (October 2019) + // - musl 1.1.24 (October 2019) + // - FreeBSD 13.1 (May 2022) + // - Android 14 (October 2023) + return posix_spawn_file_actions_addchdir_np((posix_spawn_file_actions_t *)file_actions, path); + #else + // Standardized posix_spawn_file_actions_addchdir version (POSIX.1-2024, June 2024) available in: + // - Solaris 11.4 (August 2018) + // - NetBSD 10.0 (March 2024) + // Currently missing as of: + // - OpenBSD 7.5 (April 2024) + // - QNX 8 (December 2023) + return posix_spawn_file_actions_addchdir((posix_spawn_file_actions_t *)file_actions, path); + #endif +} + CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *_Nullable const argv[_Nullable _CF_RESTRICT], char *_Nullable const envp[_Nullable _CF_RESTRICT]) { return posix_spawn(pid, path, (posix_spawn_file_actions_t *)file_actions, (posix_spawnattr_t *)attrp, argv, envp); } diff --git a/Sources/CoreFoundation/include/ForSwiftFoundationOnly.h b/Sources/CoreFoundation/include/ForSwiftFoundationOnly.h index a2ba56cd95..f3c7f51bb2 100644 --- a/Sources/CoreFoundation/include/ForSwiftFoundationOnly.h +++ b/Sources/CoreFoundation/include/ForSwiftFoundationOnly.h @@ -725,6 +725,7 @@ CF_EXPORT int _CFPosixSpawnFileActionsDestroy(_CFPosixSpawnFileActionsRef file_a CF_EXPORT void _CFPosixSpawnFileActionsDealloc(_CFPosixSpawnFileActionsRef file_actions); CF_EXPORT int _CFPosixSpawnFileActionsAddDup2(_CFPosixSpawnFileActionsRef file_actions, int filedes, int newfiledes); CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_actions, int filedes); +CF_EXPORT int _CFPosixSpawnFileActionsChdir(_CFPosixSpawnFileActionsRef file_actions, const char *path); #ifdef __cplusplus CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *const argv[], char *const envp[]); #else diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index ee35c23a33..de2f2e43fd 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -925,6 +925,9 @@ open class Process: NSObject, @unchecked Sendable { for fd in addclose.filter({ $0 >= 0 }) { try _throwIfPosixError(_CFPosixSpawnFileActionsAddClose(fileActions, fd)) } + if let dir = currentDirectoryURL?.path { + try _throwIfPosixError(_CFPosixSpawnFileActionsChdir(fileActions, dir)) + } #if canImport(Darwin) || os(Android) || os(OpenBSD) var spawnAttrs: posix_spawnattr_t? = nil @@ -947,17 +950,6 @@ open class Process: NSObject, @unchecked Sendable { } #endif - let fileManager = FileManager() - let previousDirectoryPath = fileManager.currentDirectoryPath - if let dir = currentDirectoryURL?.path, !fileManager.changeCurrentDirectoryPath(dir) { - throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL) - } - - defer { - // Reset the previous working directory path. - _ = fileManager.changeCurrentDirectoryPath(previousDirectoryPath) - } - // Launch var pid = pid_t() From 4dcbb8b572c60fcee7bf0e146523d0980e260c6f Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Sat, 28 Sep 2024 16:29:38 -0700 Subject: [PATCH 2/2] Restore thread-unsafe fallback for setting the Process working directory Swift still needs to support Amazon Linux 2 until it EoLs in mid-2025. So restore the thread-unsafe fallback for systems with glibc older than version 2.29, which was removed in #4981. --- Sources/Foundation/Process.swift | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index de2f2e43fd..758dd1dfd4 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -925,8 +925,15 @@ open class Process: NSObject, @unchecked Sendable { for fd in addclose.filter({ $0 >= 0 }) { try _throwIfPosixError(_CFPosixSpawnFileActionsAddClose(fileActions, fd)) } + let useFallbackChdir: Bool if let dir = currentDirectoryURL?.path { - try _throwIfPosixError(_CFPosixSpawnFileActionsChdir(fileActions, dir)) + let chdirResult = _CFPosixSpawnFileActionsChdir(fileActions, dir) + useFallbackChdir = chdirResult == ENOSYS + if !useFallbackChdir { + try _throwIfPosixError(chdirResult) + } + } else { + useFallbackChdir = false } #if canImport(Darwin) || os(Android) || os(OpenBSD) @@ -950,6 +957,27 @@ open class Process: NSObject, @unchecked Sendable { } #endif + // Unsafe fallback for systems missing posix_spawn_file_actions_addchdir[_np] + // This includes Glibc versions older than 2.29 such as on Amazon Linux 2 + let previousDirectoryPath: String? + if useFallbackChdir { + let fileManager = FileManager() + previousDirectoryPath = fileManager.currentDirectoryPath + if let dir = currentDirectoryURL?.path, !fileManager.changeCurrentDirectoryPath(dir) { + throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL) + } + } else { + previousDirectoryPath = nil + } + + defer { + if let previousDirectoryPath { + // Reset the previous working directory path. + let fileManager = FileManager() + _ = fileManager.changeCurrentDirectoryPath(previousDirectoryPath) + } + } + // Launch var pid = pid_t()