077c9d
commit 7a16bdbb9ff4122af0a28dc20996c95352011fdd
077c9d
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
077c9d
Date:   Wed Aug 29 16:36:44 2018 -0300
077c9d
077c9d
    Fix misreported errno on preadv2/pwritev2 (BZ#23579)
077c9d
    
077c9d
    The fallback code of Linux wrapper for preadv2/pwritev2 executes
077c9d
    regardless of the errno code for preadv2, instead of the case where
077c9d
    the syscall is not supported.
077c9d
    
077c9d
    This fixes it by calling the fallback code iff errno is ENOSYS. The
077c9d
    patch also adds tests for both invalid file descriptor and invalid
077c9d
    iov_len and vector count.
077c9d
    
077c9d
    The only discrepancy between preadv2 and fallback code regarding
077c9d
    error reporting is when an invalid flags are used.  The fallback code
077c9d
    bails out earlier with ENOTSUP instead of EINVAL/EBADF when the syscall
077c9d
    is used.
077c9d
    
077c9d
    Checked on x86_64-linux-gnu on a 4.4.0 and 4.15.0 kernel.
077c9d
    
077c9d
            [BZ #23579]
077c9d
            * misc/tst-preadvwritev2-common.c (do_test_with_invalid_fd): New
077c9d
            test.
077c9d
            * misc/tst-preadvwritev2.c, misc/tst-preadvwritev64v2.c (do_test):
077c9d
            Call do_test_with_invalid_fd.
077c9d
            * sysdeps/unix/sysv/linux/preadv2.c (preadv2): Use fallback code iff
077c9d
            errno is ENOSYS.
077c9d
            * sysdeps/unix/sysv/linux/preadv64v2.c (preadv64v2): Likewise.
077c9d
            * sysdeps/unix/sysv/linux/pwritev2.c (pwritev2): Likewise.
077c9d
            * sysdeps/unix/sysv/linux/pwritev64v2.c (pwritev64v2): Likewise.
077c9d
077c9d
diff --git a/misc/tst-preadvwritev2-common.c b/misc/tst-preadvwritev2-common.c
077c9d
index f889a21544947042..50b9da3fea56d288 100644
077c9d
--- a/misc/tst-preadvwritev2-common.c
077c9d
+++ b/misc/tst-preadvwritev2-common.c
077c9d
@@ -19,9 +19,6 @@
077c9d
 #include <limits.h>
077c9d
 #include <support/check.h>
077c9d
 
077c9d
-static void
077c9d
-do_test_with_invalid_flags (void)
077c9d
-{
077c9d
 #ifndef RWF_HIPRI
077c9d
 # define RWF_HIPRI 0
077c9d
 #endif
077c9d
@@ -39,6 +36,68 @@ do_test_with_invalid_flags (void)
077c9d
 #endif
077c9d
 #define RWF_SUPPORTED	(RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT \
077c9d
 			 | RWF_APPEND)
077c9d
+
077c9d
+static void
077c9d
+do_test_with_invalid_fd (void)
077c9d
+{
077c9d
+  char buf[256];
077c9d
+  struct iovec iov = { buf, sizeof buf };
077c9d
+
077c9d
+  /* Check with flag being 0 to use the fallback code which calls pwritev
077c9d
+     or writev.  */
077c9d
+  TEST_VERIFY (preadv2 (-1, &iov, 1, -1, 0) == -1);
077c9d
+  TEST_COMPARE (errno, EBADF);
077c9d
+  TEST_VERIFY (pwritev2 (-1, &iov, 1, -1, 0) == -1);
077c9d
+  TEST_COMPARE (errno, EBADF);
077c9d
+
077c9d
+  /* Same tests as before but with flags being different than 0.  Since
077c9d
+     there is no emulation for any flag value, fallback code returns
077c9d
+     ENOTSUP.  This is different running on a kernel with preadv2/pwritev2
077c9d
+     support, where EBADF is returned).  */
077c9d
+  TEST_VERIFY (preadv2 (-1, &iov, 1, 0, RWF_HIPRI) == -1);
077c9d
+  TEST_VERIFY (errno == EBADF || errno == ENOTSUP);
077c9d
+  TEST_VERIFY (pwritev2 (-1, &iov, 1, 0, RWF_HIPRI) == -1);
077c9d
+  TEST_VERIFY (errno == EBADF || errno == ENOTSUP);
077c9d
+}
077c9d
+
077c9d
+static void
077c9d
+do_test_with_invalid_iov (void)
077c9d
+{
077c9d
+  {
077c9d
+    char buf[256];
077c9d
+    struct iovec iov;
077c9d
+
077c9d
+    iov.iov_base = buf;
077c9d
+    iov.iov_len = (size_t)SSIZE_MAX + 1;
077c9d
+
077c9d
+    TEST_VERIFY (preadv2 (temp_fd, &iov, 1, 0, 0) == -1);
077c9d
+    TEST_COMPARE (errno, EINVAL);
077c9d
+    TEST_VERIFY (pwritev2 (temp_fd, &iov, 1, 0, 0) == -1);
077c9d
+    TEST_COMPARE (errno, EINVAL);
077c9d
+
077c9d
+    /* Same as for invalid file descriptor tests, emulation fallback
077c9d
+       first checks for flag value and return ENOTSUP.  */
077c9d
+    TEST_VERIFY (preadv2 (temp_fd, &iov, 1, 0, RWF_HIPRI) == -1);
077c9d
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
077c9d
+    TEST_VERIFY (pwritev2 (temp_fd, &iov, 1, 0, RWF_HIPRI) == -1);
077c9d
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
077c9d
+  }
077c9d
+
077c9d
+  {
077c9d
+    /* An invalid iovec buffer should trigger an invalid memory access
077c9d
+       or an error (Linux for instance returns EFAULT).  */
077c9d
+    struct iovec iov[IOV_MAX+1] = { 0 };
077c9d
+
077c9d
+    TEST_VERIFY (preadv2 (temp_fd, iov, IOV_MAX + 1, 0, RWF_HIPRI) == -1);
077c9d
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
077c9d
+    TEST_VERIFY (pwritev2 (temp_fd, iov, IOV_MAX + 1, 0, RWF_HIPRI) == -1);
077c9d
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
077c9d
+  }
077c9d
+}
077c9d
+
077c9d
+static void
077c9d
+do_test_with_invalid_flags (void)
077c9d
+{
077c9d
   /* Set the next bit from the mask of all supported flags.  */
077c9d
   int invalid_flag = RWF_SUPPORTED != 0 ? __builtin_clz (RWF_SUPPORTED) : 2;
077c9d
   invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag);
077c9d
diff --git a/misc/tst-preadvwritev2.c b/misc/tst-preadvwritev2.c
077c9d
index be22802dbe00317f..cb58cbe41ecc639d 100644
077c9d
--- a/misc/tst-preadvwritev2.c
077c9d
+++ b/misc/tst-preadvwritev2.c
077c9d
@@ -30,6 +30,8 @@ do_test (void)
077c9d
 {
077c9d
   do_test_with_invalid_flags ();
077c9d
   do_test_without_offset ();
077c9d
+  do_test_with_invalid_fd ();
077c9d
+  do_test_with_invalid_iov ();
077c9d
 
077c9d
   return do_test_with_offset (0);
077c9d
 }
077c9d
diff --git a/misc/tst-preadvwritev64v2.c b/misc/tst-preadvwritev64v2.c
077c9d
index 8d3cc32b284dbf4c..6a9de54c786acc53 100644
077c9d
--- a/misc/tst-preadvwritev64v2.c
077c9d
+++ b/misc/tst-preadvwritev64v2.c
077c9d
@@ -32,6 +32,8 @@ do_test (void)
077c9d
 {
077c9d
   do_test_with_invalid_flags ();
077c9d
   do_test_without_offset ();
077c9d
+  do_test_with_invalid_fd ();
077c9d
+  do_test_with_invalid_iov ();
077c9d
 
077c9d
   return do_test_with_offset (0);
077c9d
 }
077c9d
diff --git a/sysdeps/unix/sysv/linux/preadv2.c b/sysdeps/unix/sysv/linux/preadv2.c
077c9d
index c8bf0764ef2629fc..bb08cbc5fd96962e 100644
077c9d
--- a/sysdeps/unix/sysv/linux/preadv2.c
077c9d
+++ b/sysdeps/unix/sysv/linux/preadv2.c
077c9d
@@ -32,7 +32,7 @@ preadv2 (int fd, const struct iovec *vector, int count, off_t offset,
077c9d
 # ifdef __NR_preadv2
077c9d
   ssize_t result = SYSCALL_CANCEL (preadv2, fd, vector, count,
077c9d
 				   LO_HI_LONG (offset), flags);
077c9d
-  if (result >= 0)
077c9d
+  if (result >= 0 || errno != ENOSYS)
077c9d
     return result;
077c9d
 # endif
077c9d
   /* Trying to emulate the preadv2 syscall flags is troublesome:
077c9d
diff --git a/sysdeps/unix/sysv/linux/preadv64v2.c b/sysdeps/unix/sysv/linux/preadv64v2.c
077c9d
index d7400a0252a8c6a1..b72a047347b1db0e 100644
077c9d
--- a/sysdeps/unix/sysv/linux/preadv64v2.c
077c9d
+++ b/sysdeps/unix/sysv/linux/preadv64v2.c
077c9d
@@ -30,7 +30,7 @@ preadv64v2 (int fd, const struct iovec *vector, int count, off64_t offset,
077c9d
 #ifdef __NR_preadv64v2
077c9d
   ssize_t result = SYSCALL_CANCEL (preadv64v2, fd, vector, count,
077c9d
 				   LO_HI_LONG (offset), flags);
077c9d
-  if (result >= 0)
077c9d
+  if (result >= 0 || errno != ENOSYS)
077c9d
     return result;
077c9d
 #endif
077c9d
   /* Trying to emulate the preadv2 syscall flags is troublesome:
077c9d
diff --git a/sysdeps/unix/sysv/linux/pwritev2.c b/sysdeps/unix/sysv/linux/pwritev2.c
077c9d
index 29c2264c8f3d949a..26333ebd43c5f0af 100644
077c9d
--- a/sysdeps/unix/sysv/linux/pwritev2.c
077c9d
+++ b/sysdeps/unix/sysv/linux/pwritev2.c
077c9d
@@ -28,7 +28,7 @@ pwritev2 (int fd, const struct iovec *vector, int count, off_t offset,
077c9d
 # ifdef __NR_pwritev2
077c9d
   ssize_t result = SYSCALL_CANCEL (pwritev2, fd, vector, count,
077c9d
 				   LO_HI_LONG (offset), flags);
077c9d
-  if (result >= 0)
077c9d
+  if (result >= 0 || errno != ENOSYS)
077c9d
     return result;
077c9d
 # endif
077c9d
   /* Trying to emulate the pwritev2 syscall flags is troublesome:
077c9d
diff --git a/sysdeps/unix/sysv/linux/pwritev64v2.c b/sysdeps/unix/sysv/linux/pwritev64v2.c
077c9d
index 42da321149bce40d..17ea905aa6a8db94 100644
077c9d
--- a/sysdeps/unix/sysv/linux/pwritev64v2.c
077c9d
+++ b/sysdeps/unix/sysv/linux/pwritev64v2.c
077c9d
@@ -30,7 +30,7 @@ pwritev64v2 (int fd, const struct iovec *vector, int count, off64_t offset,
077c9d
 #ifdef __NR_pwritev64v2
077c9d
   ssize_t result = SYSCALL_CANCEL (pwritev64v2, fd, vector, count,
077c9d
 				   LO_HI_LONG (offset), flags);
077c9d
-  if (result >= 0)
077c9d
+  if (result >= 0 || errno != ENOSYS)
077c9d
     return result;
077c9d
 #endif
077c9d
   /* Trying to emulate the pwritev2 syscall flags is troublesome: