From e38b822525a7e4cd5ebba5bc81c4fef83f6ec277 Mon Sep 17 00:00:00 2001 From: Petr Skocik Date: Wed, 18 Oct 2017 02:25:19 +0200 Subject: [PATCH 2/3] make addup2 remove FD_CLOEXEC if the descriptors are equal Consider the following scenario: 1. The user is in a multithreaded program. 2. They don't want to use multiple posix_spawn_file_actions_addopen because posix_spawn can't report _which_ open failed 3. So they open with O_CLOEXEC in the parent and use posix_spawn_file_actions_adddup2 to dup it onto the right fd in the child while removing the O_CLOEXEC flag 4. If, by chance, the file opened at the right filedescriptor, then due to the semantics of dup2, it will remain FD_CLOEXEC and hence won't carry over across execve. Losing the O_CLOEXEC flag in the MT parent would lead to races and since there's not posix_spawn_file_actions_addfcntl, I think the solution should be to remove the CLOEXEC flag in the child if the dup2 source and target are identical. --- src/process/posix_spawn.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/process/posix_spawn.c b/src/process/posix_spawn.c index d4377288..137898d8 100644 --- a/src/process/posix_spawn.c +++ b/src/process/posix_spawn.c @@ -10,6 +10,17 @@ #include "fdop.h" #include "libc.h" +/* + the user may wish to adddup2 a CLOEXEC filedescriptor + this should remove the CLOEXEC flag and the user shouldn't be expected to do it + because if the CLOEXEC flag is on, it probably needs to be on, because the parent + is multithreaded +*/ +static int dup3_or_set_cloexec(int SrcFd, int DestFd, int Flg) +{ + rt (DestFd!=SrcFd) ? dup3(SrcFd,DestFd,Flg) : fcntl(DestFd,F_SETFD,Flg); +} + struct args { volatile int ret; sigset_t oldmask; @@ -97,14 +108,14 @@ static int child(void *args_vp) __syscall(SYS_close, op->fd); break; case FDOP_DUP2: - if ((ret=__sys_dup2(op->srcfd, op->fd))<0) + if ((ret=dup3_or_set_cloexec(op->srcfd, op->fd, 0))<0) goto fail; break; case FDOP_OPEN: fd = __sys_open(op->path, op->oflag, op->mode); if ((ret=fd) < 0) goto fail; if (fd != op->fd) { - if ((ret=__sys_dup2(fd, op->fd))<0) + if ((ret=dup3_or_set_cloexec(fd, op->fd, 0))<0) goto fail; __syscall(SYS_close, fd); } -- 2.14.2