]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
dup2, fcntl: Fix the race condition on OS/2 kLIBC
authorKO Myung-Hun <komh78@gmail.com>
Tue, 2 Dec 2025 11:43:28 +0000 (20:43 +0900)
committerPaul Eggert <eggert@cs.ucla.edu>
Tue, 2 Dec 2025 18:43:07 +0000 (10:43 -0800)
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().

ChangeLog
lib/dup2.c
lib/fcntl.c

index 6ec725b5908b2b0a234eb4fc2b5aa87ef691b85e..8ea5a65cf989c28457c458461def0fa61ea0ddd3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2025-12-02  KO Myung-Hun  <komh78@gmail.com>
+
+       dup2, fcntl: Fix the race condition on OS/2 kLIBC
+       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().
+
 2025-12-02  Bruno Haible  <bruno@clisp.org>
 
        Fix compilation error in C++ mode on FreeBSD 15.
index 69b37196dee121aa6777d204d31420f5684ad8af..3468fbcb6e8a3600a5032983bffed5f9b7305b8c 100644 (file)
@@ -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;
 }
index f4cf86cf2e35497584c5b67cddf458cba14bdd4b..abdd7b89890571d869723f7327566d59159e7c76 100644 (file)
@@ -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: