diff --git a/libc/bionic/dup2.cpp b/libc/bionic/dup2.cpp index 0b8632bee..98c56469d 100644 --- a/libc/bionic/dup2.cpp +++ b/libc/bionic/dup2.cpp @@ -26,8 +26,19 @@ * SUCH DAMAGE. */ +#include #include int dup2(int old_fd, int new_fd) { + // If old_fd is equal to new_fd and a valid file descriptor, dup2 returns + // old_fd without closing it. This is not true of dup3, so we have to + // handle this case ourselves. + if (old_fd == new_fd) { + if (fcntl(old_fd, F_GETFD) == -1) { + return -1; + } + return old_fd; + } + return dup3(old_fd, new_fd, 0); } diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp index f5c0524c4..f54a4619b 100644 --- a/tests/unistd_test.cpp +++ b/tests/unistd_test.cpp @@ -801,3 +801,22 @@ TEST(unistd, sysconf) { VERIFY_SYSCONF_NOT_SUPPORT(_SC_XOPEN_UUCP); #endif // defined(__BIONIC__) } + +TEST(unistd, dup2_same) { + // POSIX says of dup2: + // If fildes2 is already a valid open file descriptor ... + // [and] fildes is equal to fildes2 ... dup2() shall return + // fildes2 without closing it. + // This isn't true of dup3(2), so we need to manually implement that. + + // Equal and valid. + int fd = open("/proc/version", O_RDONLY); + ASSERT_TRUE(fd != -1); + ASSERT_EQ(fd, dup2(fd, fd)); + ASSERT_EQ(0, close(fd)); // Check that dup2 didn't close fd. + + // Equal, but invalid. + errno = 0; + ASSERT_EQ(-1, dup2(fd, fd)); + ASSERT_EQ(EBADF, errno); +}