From 107cefbf467a84ecdd1ed08ee87127ec29454f78 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Mon, 9 Nov 2026 13:23:10 -0500 Subject: [PATCH] fix(native): a spawn whose dup2 source close-from closed fails with EBADF --- tools/nativeguest/hook/exec.c | 7 +++++-- tools/nativeguest/hook/posix_shm.c | 24 ++++++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/tools/nativeguest/hook/exec.c b/tools/nativeguest/hook/exec.c index 642ffa61c..5fcc957c9 100644 --- a/tools/nativeguest/hook/exec.c +++ b/tools/nativeguest/hook/exec.c @@ -878,13 +878,16 @@ static int ddnatv_posix_spawn(pid_t *pid, const char *path, return ret; } -static void apply_closefrom(posix_spawn_file_actions_t *actions); +static int apply_closefrom(posix_spawn_file_actions_t *actions); int posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { - if (file_actions) apply_closefrom((posix_spawn_file_actions_t *)file_actions); + if (file_actions) { + int bad = apply_closefrom((posix_spawn_file_actions_t *)file_actions); + if (bad) return bad; + } posix_spawnattr_t sanitized; return ddnatv_posix_spawn(pid, path, file_actions, spawn_attr_for_android(attrp, &sanitized), argv, envp, 0); } diff --git a/tools/nativeguest/hook/posix_shm.c b/tools/nativeguest/hook/posix_shm.c index feace5039..afd8a6f65 100644 --- a/tools/nativeguest/hook/posix_shm.c +++ b/tools/nativeguest/hook/posix_shm.c @@ -49,6 +49,7 @@ static struct closefrom_entry { int nkeep; int keep[CLOSEFROM_KEEP]; int closed_to; // the descriptors up to this one are closed by actions already added + int bad_source; // a dup2 after close-from duplicates a descriptor it closed: glibc's EBADF } g_closefrom[CLOSEFROM_SLOTS]; static pthread_mutex_t g_closefrom_lock = PTHREAD_MUTEX_INITIALIZER; @@ -101,7 +102,17 @@ int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *actions, int fd static int (*orig)(posix_spawn_file_actions_t *, int, int) = NULL; if (!orig) orig = (int (*)(posix_spawn_file_actions_t *, int, int))dlsym(RTLD_NEXT, "posix_spawn_file_actions_adddup2"); int ret = orig ? orig(actions, fd, newfd) : ENOSYS; - if (ret == 0) closefrom_note_target(actions, newfd); + if (ret == 0) { + pthread_mutex_lock(&g_closefrom_lock); + struct closefrom_entry *e = closefrom_entry(actions, 0); + if (e && e->from >= 0 && fd >= e->from) { + int reopened = 0; + for (int k = 0; k < e->nkeep; k++) reopened |= e->keep[k] == fd; + if (!reopened) e->bad_source = 1; + } + pthread_mutex_unlock(&g_closefrom_lock); + closefrom_note_target(actions, newfd); + } return ret; } @@ -125,12 +136,16 @@ int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *actions) { return orig ? orig(actions) : ENOSYS; } -static void apply_closefrom(posix_spawn_file_actions_t *actions) { +static int apply_closefrom(posix_spawn_file_actions_t *actions) { pthread_mutex_lock(&g_closefrom_lock); struct closefrom_entry *e = closefrom_entry(actions, 0); if (!e || e->from < 0) { pthread_mutex_unlock(&g_closefrom_lock); - return; + return 0; + } + if (e->bad_source) { + pthread_mutex_unlock(&g_closefrom_lock); + return EBADF; } int highest = e->highest; DIR *dir = opendir("/proc/self/fd"); @@ -152,7 +167,8 @@ static void apply_closefrom(posix_spawn_file_actions_t *actions) { e->closed_to = fd; } pthread_mutex_unlock(&g_closefrom_lock); + return 0; } #else -static void apply_closefrom(posix_spawn_file_actions_t *actions) { (void)actions; } +static int apply_closefrom(posix_spawn_file_actions_t *actions) { (void)actions; return 0; } #endif