Skip to content

Commit 90dea7f

Browse files
[6.0.2] Add a thread-safe implementation of Process.currentDirectoryURL (#5095)
* 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. * 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.
1 parent 59e6789 commit 90dea7f

File tree

3 files changed

+56
-6
lines changed

3 files changed

+56
-6
lines changed

Sources/CoreFoundation/CFPlatform.c

+29
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
#define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
5858
#endif
5959

60+
#ifndef __GLIBC_PREREQ
61+
#define __GLIBC_PREREQ(maj, min) 0
62+
#endif
63+
6064
extern void __CFGetUGIDs(uid_t *euid, gid_t *egid);
6165

6266
#if TARGET_OS_MAC
@@ -2274,6 +2278,31 @@ CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_
22742278
return posix_spawn_file_actions_addclose((posix_spawn_file_actions_t *)file_actions, filedes);
22752279
}
22762280

2281+
CF_EXPORT int _CFPosixSpawnFileActionsChdir(_CFPosixSpawnFileActionsRef file_actions, const char *path) {
2282+
#if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 29)
2283+
// Glibc versions prior to 2.29 don't support posix_spawn_file_actions_addchdir_np, impacting:
2284+
// - Amazon Linux 2 (EoL mid-2025)
2285+
return ENOSYS;
2286+
#elif defined(__GLIBC__) || TARGET_OS_DARWIN || defined(__FreeBSD__) || defined(__ANDROID__)
2287+
// Pre-standard posix_spawn_file_actions_addchdir_np version available in:
2288+
// - Solaris 11.3 (October 2015)
2289+
// - Glibc 2.29 (February 2019)
2290+
// - macOS 10.15 (October 2019)
2291+
// - musl 1.1.24 (October 2019)
2292+
// - FreeBSD 13.1 (May 2022)
2293+
// - Android 14 (October 2023)
2294+
return posix_spawn_file_actions_addchdir_np((posix_spawn_file_actions_t *)file_actions, path);
2295+
#else
2296+
// Standardized posix_spawn_file_actions_addchdir version (POSIX.1-2024, June 2024) available in:
2297+
// - Solaris 11.4 (August 2018)
2298+
// - NetBSD 10.0 (March 2024)
2299+
// Currently missing as of:
2300+
// - OpenBSD 7.5 (April 2024)
2301+
// - QNX 8 (December 2023)
2302+
return posix_spawn_file_actions_addchdir((posix_spawn_file_actions_t *)file_actions, path);
2303+
#endif
2304+
}
2305+
22772306
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]) {
22782307
return posix_spawn(pid, path, (posix_spawn_file_actions_t *)file_actions, (posix_spawnattr_t *)attrp, argv, envp);
22792308
}

Sources/CoreFoundation/include/ForSwiftFoundationOnly.h

+1
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ CF_EXPORT int _CFPosixSpawnFileActionsDestroy(_CFPosixSpawnFileActionsRef file_a
725725
CF_EXPORT void _CFPosixSpawnFileActionsDealloc(_CFPosixSpawnFileActionsRef file_actions);
726726
CF_EXPORT int _CFPosixSpawnFileActionsAddDup2(_CFPosixSpawnFileActionsRef file_actions, int filedes, int newfiledes);
727727
CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_actions, int filedes);
728+
CF_EXPORT int _CFPosixSpawnFileActionsChdir(_CFPosixSpawnFileActionsRef file_actions, const char *path);
728729
#ifdef __cplusplus
729730
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[]);
730731
#else

Sources/Foundation/Process.swift

+26-6
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,16 @@ open class Process: NSObject, @unchecked Sendable {
925925
for fd in addclose.filter({ $0 >= 0 }) {
926926
try _throwIfPosixError(_CFPosixSpawnFileActionsAddClose(fileActions, fd))
927927
}
928+
let useFallbackChdir: Bool
929+
if let dir = currentDirectoryURL?.path {
930+
let chdirResult = _CFPosixSpawnFileActionsChdir(fileActions, dir)
931+
useFallbackChdir = chdirResult == ENOSYS
932+
if !useFallbackChdir {
933+
try _throwIfPosixError(chdirResult)
934+
}
935+
} else {
936+
useFallbackChdir = false
937+
}
928938

929939
#if canImport(Darwin) || os(Android) || os(OpenBSD)
930940
var spawnAttrs: posix_spawnattr_t? = nil
@@ -947,15 +957,25 @@ open class Process: NSObject, @unchecked Sendable {
947957
}
948958
#endif
949959

950-
let fileManager = FileManager()
951-
let previousDirectoryPath = fileManager.currentDirectoryPath
952-
if let dir = currentDirectoryURL?.path, !fileManager.changeCurrentDirectoryPath(dir) {
953-
throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL)
960+
// Unsafe fallback for systems missing posix_spawn_file_actions_addchdir[_np]
961+
// This includes Glibc versions older than 2.29 such as on Amazon Linux 2
962+
let previousDirectoryPath: String?
963+
if useFallbackChdir {
964+
let fileManager = FileManager()
965+
previousDirectoryPath = fileManager.currentDirectoryPath
966+
if let dir = currentDirectoryURL?.path, !fileManager.changeCurrentDirectoryPath(dir) {
967+
throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL)
968+
}
969+
} else {
970+
previousDirectoryPath = nil
954971
}
955972

956973
defer {
957-
// Reset the previous working directory path.
958-
_ = fileManager.changeCurrentDirectoryPath(previousDirectoryPath)
974+
if let previousDirectoryPath {
975+
// Reset the previous working directory path.
976+
let fileManager = FileManager()
977+
_ = fileManager.changeCurrentDirectoryPath(previousDirectoryPath)
978+
}
959979
}
960980

961981
// Launch

0 commit comments

Comments
 (0)