Fix dup2 in the case where the two fds are equal.

dup3's behavior differs from dup2 in this case, so we need to paper
over that in the C library.

Change-Id: I313cd6f226db5e237f61866f324c5ecdd12bf762
diff --git a/libc/bionic/dup2.cpp b/libc/bionic/dup2.cpp
index 0b8632b..98c5646 100644
--- a/libc/bionic/dup2.cpp
+++ b/libc/bionic/dup2.cpp
@@ -26,8 +26,19 @@
  * SUCH DAMAGE.
  */
 
+#include <fcntl.h>
 #include <unistd.h>
 
 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 f5c0524..f54a461 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -801,3 +801,22 @@
   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);
+}