SDL: Close file descriptors not used by the child process

From 7ff015ceaee15f0fca49c1eff6694ffb6f8170c1 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 14 Sep 2024 10:16:20 -0700
Subject: [PATCH] Close file descriptors not used by the child process

---
 src/process/posix/SDL_posixprocess.c | 40 ++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/src/process/posix/SDL_posixprocess.c b/src/process/posix/SDL_posixprocess.c
index 3aaef3cef27e8..5331e1c01914c 100644
--- a/src/process/posix/SDL_posixprocess.c
+++ b/src/process/posix/SDL_posixprocess.c
@@ -22,6 +22,7 @@
 
 #ifdef SDL_PROCESS_POSIX
 
+#include <dirent.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
@@ -102,6 +103,41 @@ static bool GetStreamFD(SDL_PropertiesID props, const char *property, int *resul
     return true;
 }
 
+static bool AddFileDescriptorCloseActions(posix_spawn_file_actions_t *fa)
+{
+    DIR *dir = opendir("/proc/self/fd");
+    if (dir) {
+        struct dirent *entry;
+        while ((entry = readdir(dir)) != NULL) {
+            int fd = SDL_atoi(entry->d_name);
+            if (fd <= STDERR_FILENO) {
+                continue;
+            }
+
+            int flags = fcntl(fd, F_GETFD);
+            if (flags < 0 || (flags & FD_CLOEXEC)) {
+                continue;
+            }
+            if (posix_spawn_file_actions_addclose(fa, fd) != 0) {
+                closedir(dir);
+                return SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
+            }
+        }
+        closedir(dir);
+    } else {
+        for (int fd = sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; --fd) {
+            int flags = fcntl(fd, F_GETFD);
+            if (flags < 0 || (flags & FD_CLOEXEC)) {
+                continue;
+            }
+            if (posix_spawn_file_actions_addclose(fa, fd) != 0) {
+                return SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
+            }
+        }
+    }
+    return true;
+}
+
 bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
 {
     char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
@@ -137,6 +173,10 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
         goto posix_spawn_fail_attr;
     }
 
+    if (!AddFileDescriptorCloseActions(&fa)) {
+        goto posix_spawn_fail_all;
+    }
+
     // Background processes don't have access to the terminal
     if (process->background) {
         if (stdin_option == SDL_PROCESS_STDIO_INHERITED) {