dup2() may return an fd other than the desired fd if other threads are
involved.
fcntl() may close the fd used by other threads accidentally.
* lib/dup2.c (klibc_dup2dirfd): Ensure duplication to the desired fd.
(klibd_dup2): Do not close the desired fd.
* lib/fcntl.c (klibc_dupdirfd): New.
(klibc_fcntl): Use klibc_dupdirfd() instead of dup2().
---
lib/dup2.c | 32 +++++++++++++++++++++++++-------
lib/fcntl.c | 42 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/lib/dup2.c b/lib/dup2.c
index 69b37196de..3468fbcb6e 100644
--- a/lib/dup2.c
+++ b/lib/dup2.c
@@ -117,7 +117,7 @@ klibc_dup2dirfd (int fd, int desired_fd)
if (tempfd == -1)
return -1;
- if (tempfd == desired_fd)
+ if (tempfd >= desired_fd)
{
close (tempfd);
@@ -125,7 +125,29 @@ klibc_dup2dirfd (int fd, int desired_fd)
if (__libc_Back_ioFHToPath (fd, path, sizeof (path)))
return -1;
- return open(path, O_RDONLY);
+ for (;;)
+ {
+ close (desired_fd);
+
+ dupfd = open (path, O_RDONLY);
+ if (dupfd == -1)
+ return -1;
+
+ if (dupfd == desired_fd)
+ return dupfd;
+
+ /* If lower FD was closed by other threads, fill again. */
+ if (dupfd < desired_fd)
+ {
+ tempfd = dupfd;
+ break;
+ }
+
+ /* desired_fd was opened by other threads. Try again. */
+ /* FIXME: Closing desired_fd opened by other threads may lead to
+ unexpected behavior. */
+ close (dupfd);
+ }
}
dupfd = klibc_dup2dirfd (fd, desired_fd);
@@ -144,11 +166,7 @@ klibc_dup2 (int fd, int desired_fd)
dupfd = dup2 (fd, desired_fd);
if (dupfd == -1 && errno == ENOTSUP \
&& !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
- {
- close (desired_fd);
-
- return klibc_dup2dirfd (fd, desired_fd);
- }
+ return klibc_dup2dirfd (fd, desired_fd);
return dupfd;
}
diff --git a/lib/fcntl.c b/lib/fcntl.c
index f4cf86cf2e..abdd7b8989 100644
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
@@ -30,6 +30,7 @@
#ifdef __KLIBC__
# include <emx/io.h>
+# include <InnoTekLIBC/backend.h>
#endif
#if defined _WIN32 && ! defined __CYGWIN__
@@ -538,6 +539,41 @@ rpl_fcntl_DUPFD_CLOEXEC (int fd, int target)
#undef fcntl
#ifdef __KLIBC__
+static int
+klibc_dupdirfd (int fd, int minfd)
+{
+ int tempfd;
+ int dupfd;
+
+ tempfd = open ("NUL", O_RDONLY);
+ if (tempfd == -1)
+ return -1;
+
+ if (tempfd >= minfd)
+ {
+ close (tempfd);
+
+ char path[_MAX_PATH];
+ if (__libc_Back_ioFHToPath (fd, path, sizeof (path)))
+ return -1;
+
+ dupfd = open (path, O_RDONLY);
+ if (dupfd == -1)
+ return -1;
+
+ if (dupfd >= minfd)
+ return dupfd;
+
+ /* Lower FD was closed by other threads. Fill again. */
+ tempfd = dupfd;
+ }
+
+ dupfd = klibc_dupdirfd (fd, minfd);
+
+ close (tempfd);
+
+ return dupfd;
+}
static int
klibc_fcntl (int fd, int action, /* arg */...)
@@ -560,11 +596,7 @@ klibc_fcntl (int fd, int action, /* arg */...)
switch (action)
{
case F_DUPFD:
- /* Find available fd */
- while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
- arg++;
-
- result = dup2 (fd, arg);
+ result = klibc_dupdirfd (fd, arg);
break;
case F_GETFD:
--
2.50.1