Skip to content

Commit a33ed41

Browse files
authored
Merge pull request #2485 from weissi/jw-dont-inherit-fds
Process: do not inherit all file descriptors to child processes
2 parents 5e58226 + c6da1a0 commit a33ed41

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

Foundation/Process.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
import CoreFoundation
1111

12+
#if canImport(Darwin)
13+
import Darwin
14+
#endif
15+
1216
extension Process {
1317
public enum TerminationReason : Int {
1418
case exit
@@ -837,6 +841,21 @@ open class Process: NSObject {
837841
posix(_CFPosixSpawnFileActionsAddClose(fileActions, fd))
838842
}
839843

844+
#if canImport(Darwin)
845+
var spawnAttrs: posix_spawnattr_t? = nil
846+
posix_spawnattr_init(&spawnAttrs)
847+
posix_spawnattr_setflags(&spawnAttrs, .init(POSIX_SPAWN_CLOEXEC_DEFAULT))
848+
#else
849+
for fd in 3 ..< getdtablesize() {
850+
guard adddup2[fd] == nil &&
851+
!addclose.contains(fd) &&
852+
fd != taskSocketPair[1] else {
853+
continue // Do not double-close descriptors, or close those pertaining to Pipes or FileHandles we want inherited.
854+
}
855+
posix(_CFPosixSpawnFileActionsAddClose(fileActions, fd))
856+
}
857+
#endif
858+
840859
let fileManager = FileManager()
841860
let previousDirectoryPath = fileManager.currentDirectoryPath
842861
if !fileManager.changeCurrentDirectoryPath(currentDirectoryURL.path) {
@@ -850,9 +869,16 @@ open class Process: NSObject {
850869

851870
// Launch
852871
var pid = pid_t()
872+
#if os(macOS)
873+
guard _CFPosixSpawn(&pid, launchPath, fileActions, &spawnAttrs, argv, envp) == 0 else {
874+
throw _NSErrorWithErrno(errno, reading: true, path: launchPath)
875+
}
876+
#else
853877
guard _CFPosixSpawn(&pid, launchPath, fileActions, nil, argv, envp) == 0 else {
854878
throw _NSErrorWithErrno(errno, reading: true, path: launchPath)
855879
}
880+
#endif
881+
856882

857883
// Close the write end of the input and output pipes.
858884
if let pipe = standardInput as? Pipe {

TestFoundation/TestProcess.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,32 @@ class TestProcess : XCTestCase {
565565
XCTAssertEqual(String(data: stdoutData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines), "No files specified.")
566566
}
567567

568+
func test_fileDescriptorsAreNotInherited() throws {
569+
let task = Process()
570+
let someExtraFDs = [dup(1), dup(1), dup(1), dup(1), dup(1), dup(1), dup(1)]
571+
task.executableURL = xdgTestHelperURL()
572+
task.arguments = ["--print-open-file-descriptors"]
573+
task.standardInput = FileHandle.nullDevice
574+
let stdoutPipe = Pipe()
575+
task.standardOutput = stdoutPipe.fileHandleForWriting
576+
task.standardError = FileHandle.nullDevice
577+
XCTAssertNoThrow(try task.run())
578+
579+
try stdoutPipe.fileHandleForWriting.close()
580+
let stdoutData = try stdoutPipe.fileHandleForReading.readToEnd()
581+
task.waitUntilExit()
582+
let stdoutString = String(decoding: stdoutData ?? Data(), as: Unicode.UTF8.self)
583+
#if os(macOS)
584+
XCTAssertEqual("0\n1\n2\n", stdoutString)
585+
#else
586+
// on Linux we should also have a /dev/urandom open as well as some socket that Process uses for something.
587+
XCTAssert(stdoutString.utf8.starts(with: "0\n1\n2\n3\n".utf8))
588+
XCTAssertEqual(stdoutString.components(separatedBy: "\n").count, 6, "\(stdoutString)")
589+
#endif
590+
for fd in someExtraFDs {
591+
close(fd)
592+
}
593+
}
568594

569595
static var allTests: [(String, (TestProcess) -> () throws -> Void)] {
570596
var tests = [
@@ -599,6 +625,7 @@ class TestProcess : XCTestCase {
599625
tests += [
600626
("test_interrupt", test_interrupt),
601627
("test_suspend_resume", test_suspend_resume),
628+
("test_fileDescriptorsAreNotInherited", test_fileDescriptorsAreNotInherited),
602629
]
603630
#endif
604631
return tests

TestFoundation/xdgTestHelper/main.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@ func cat(_ args: ArraySlice<String>.Iterator) {
206206
exit(exitCode)
207207
}
208208

209+
#if !os(Windows)
210+
func printOpenFileDescriptors() {
211+
for fd in 0..<getdtablesize() {
212+
if fcntl(fd, F_GETFD) != -1 {
213+
print(fd)
214+
}
215+
}
216+
exit(0)
217+
}
218+
#endif
219+
209220
// -----
210221

211222
var arguments = ProcessInfo.processInfo.arguments.dropFirst().makeIterator()
@@ -264,8 +275,11 @@ case "--nspathfor":
264275
#if !os(Windows)
265276
case "--signal-test":
266277
signalTest()
278+
279+
case "--print-open-file-descriptors":
280+
printOpenFileDescriptors()
267281
#endif
268-
282+
269283
default:
270284
fatalError("These arguments are not recognized. Only run this from a unit test.")
271285
}

0 commit comments

Comments
 (0)