]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Fix misreported errno on preadv2/pwritev2 (BZ#23579)
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Wed, 29 Aug 2018 19:36:44 +0000 (16:36 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Fri, 28 Sep 2018 13:08:09 +0000 (10:08 -0300)
The fallback code of Linux wrapper for preadv2/pwritev2 executes
regardless of the errno code for preadv2, instead of the case where
the syscall is not supported.

This fixes it by calling the fallback code iff errno is ENOSYS. The
patch also adds tests for both invalid file descriptor and invalid
iov_len and vector count.

The only discrepancy between preadv2 and fallback code regarding
error reporting is when an invalid flags are used.  The fallback code
bails out earlier with ENOTSUP instead of EINVAL/EBADF when the syscall
is used.

Checked on x86_64-linux-gnu on a 4.4.0 and 4.15.0 kernel.

[BZ #23579]
* misc/tst-preadvwritev2-common.c (do_test_with_invalid_fd): New
test.
* misc/tst-preadvwritev2.c, misc/tst-preadvwritev64v2.c (do_test):
Call do_test_with_invalid_fd.
* sysdeps/unix/sysv/linux/preadv2.c (preadv2): Use fallback code iff
errno is ENOSYS.
* sysdeps/unix/sysv/linux/preadv64v2.c (preadv64v2): Likewise.
* sysdeps/unix/sysv/linux/pwritev2.c (pwritev2): Likewise.
* sysdeps/unix/sysv/linux/pwritev64v2.c (pwritev64v2): Likewise.

ChangeLog
misc/tst-preadvwritev2-common.c
misc/tst-preadvwritev2.c
misc/tst-preadvwritev64v2.c
sysdeps/unix/sysv/linux/preadv2.c
sysdeps/unix/sysv/linux/preadv64v2.c
sysdeps/unix/sysv/linux/pwritev2.c
sysdeps/unix/sysv/linux/pwritev64v2.c

index 0af91b490df4b0c2fa2d0224935f10ca1add326c..10fa0029500616d321626d57600ba16ab5b7a583 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2018-09-28  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+       [BZ #23579]
+       * misc/tst-preadvwritev2-common.c (do_test_with_invalid_fd,
+       do_test_with_invalid_iov): New tests.
+       * misc/tst-preadvwritev2.c, misc/tst-preadvwritev64v2.c (do_test):
+       Call do_test_with_invalid_fd and do_test_with_invalid_iov.
+       * sysdeps/unix/sysv/linux/preadv2.c (preadv2): Use fallback code iff
+       errno is ENOSYS.
+       * sysdeps/unix/sysv/linux/preadv64v2.c (preadv64v2): Likewise.
+       * sysdeps/unix/sysv/linux/pwritev2.c (pwritev2): Likewise.
+       * sysdeps/unix/sysv/linux/pwritev64v2.c (pwritev64v2): Likewise.
+
 2018-09-27  Joseph Myers  <joseph@codesourcery.com>
 
        * include/math.h [!_ISOMAC && !(__FINITE_MATH_ONLY__ &&
index f889a21544947042cd5db1d496bc3fe60569d952..50b9da3fea56d2887924a6695d0a86de99bfabfc 100644 (file)
@@ -19,9 +19,6 @@
 #include <limits.h>
 #include <support/check.h>
 
-static void
-do_test_with_invalid_flags (void)
-{
 #ifndef RWF_HIPRI
 # define RWF_HIPRI 0
 #endif
@@ -39,6 +36,68 @@ do_test_with_invalid_flags (void)
 #endif
 #define RWF_SUPPORTED  (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT \
                         | RWF_APPEND)
+
+static void
+do_test_with_invalid_fd (void)
+{
+  char buf[256];
+  struct iovec iov = { buf, sizeof buf };
+
+  /* Check with flag being 0 to use the fallback code which calls pwritev
+     or writev.  */
+  TEST_VERIFY (preadv2 (-1, &iov, 1, -1, 0) == -1);
+  TEST_COMPARE (errno, EBADF);
+  TEST_VERIFY (pwritev2 (-1, &iov, 1, -1, 0) == -1);
+  TEST_COMPARE (errno, EBADF);
+
+  /* Same tests as before but with flags being different than 0.  Since
+     there is no emulation for any flag value, fallback code returns
+     ENOTSUP.  This is different running on a kernel with preadv2/pwritev2
+     support, where EBADF is returned).  */
+  TEST_VERIFY (preadv2 (-1, &iov, 1, 0, RWF_HIPRI) == -1);
+  TEST_VERIFY (errno == EBADF || errno == ENOTSUP);
+  TEST_VERIFY (pwritev2 (-1, &iov, 1, 0, RWF_HIPRI) == -1);
+  TEST_VERIFY (errno == EBADF || errno == ENOTSUP);
+}
+
+static void
+do_test_with_invalid_iov (void)
+{
+  {
+    char buf[256];
+    struct iovec iov;
+
+    iov.iov_base = buf;
+    iov.iov_len = (size_t)SSIZE_MAX + 1;
+
+    TEST_VERIFY (preadv2 (temp_fd, &iov, 1, 0, 0) == -1);
+    TEST_COMPARE (errno, EINVAL);
+    TEST_VERIFY (pwritev2 (temp_fd, &iov, 1, 0, 0) == -1);
+    TEST_COMPARE (errno, EINVAL);
+
+    /* Same as for invalid file descriptor tests, emulation fallback
+       first checks for flag value and return ENOTSUP.  */
+    TEST_VERIFY (preadv2 (temp_fd, &iov, 1, 0, RWF_HIPRI) == -1);
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
+    TEST_VERIFY (pwritev2 (temp_fd, &iov, 1, 0, RWF_HIPRI) == -1);
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
+  }
+
+  {
+    /* An invalid iovec buffer should trigger an invalid memory access
+       or an error (Linux for instance returns EFAULT).  */
+    struct iovec iov[IOV_MAX+1] = { 0 };
+
+    TEST_VERIFY (preadv2 (temp_fd, iov, IOV_MAX + 1, 0, RWF_HIPRI) == -1);
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
+    TEST_VERIFY (pwritev2 (temp_fd, iov, IOV_MAX + 1, 0, RWF_HIPRI) == -1);
+    TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
+  }
+}
+
+static void
+do_test_with_invalid_flags (void)
+{
   /* Set the next bit from the mask of all supported flags.  */
   int invalid_flag = RWF_SUPPORTED != 0 ? __builtin_clz (RWF_SUPPORTED) : 2;
   invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag);
index be22802dbe00317f424076d127965328480026f4..cb58cbe41ecc639d13d16bfb8cabd7e8e835d747 100644 (file)
@@ -30,6 +30,8 @@ do_test (void)
 {
   do_test_with_invalid_flags ();
   do_test_without_offset ();
+  do_test_with_invalid_fd ();
+  do_test_with_invalid_iov ();
 
   return do_test_with_offset (0);
 }
index 8d3cc32b284dbf4c7eda57717812ee3b63ed4791..6a9de54c786acc53bd5161d51fdeda9cc06f664b 100644 (file)
@@ -32,6 +32,8 @@ do_test (void)
 {
   do_test_with_invalid_flags ();
   do_test_without_offset ();
+  do_test_with_invalid_fd ();
+  do_test_with_invalid_iov ();
 
   return do_test_with_offset (0);
 }
index c8bf0764ef2629fc8d75021ad032a3d85761f39e..bb08cbc5fd96962e51d2357cfdb3f280de172371 100644 (file)
@@ -32,7 +32,7 @@ preadv2 (int fd, const struct iovec *vector, int count, off_t offset,
 # ifdef __NR_preadv2
   ssize_t result = SYSCALL_CANCEL (preadv2, fd, vector, count,
                                   LO_HI_LONG (offset), flags);
-  if (result >= 0)
+  if (result >= 0 || errno != ENOSYS)
     return result;
 # endif
   /* Trying to emulate the preadv2 syscall flags is troublesome:
index d7400a0252a8c6a1df7568d24fb1bc24358a81b8..b72a047347b1db0ee33ee93ae97f73d702bb12cf 100644 (file)
@@ -30,7 +30,7 @@ preadv64v2 (int fd, const struct iovec *vector, int count, off64_t offset,
 #ifdef __NR_preadv64v2
   ssize_t result = SYSCALL_CANCEL (preadv64v2, fd, vector, count,
                                   LO_HI_LONG (offset), flags);
-  if (result >= 0)
+  if (result >= 0 || errno != ENOSYS)
     return result;
 #endif
   /* Trying to emulate the preadv2 syscall flags is troublesome:
index 29c2264c8f3d949aed1b9b2985d1cf22f7afa075..26333ebd43c5f0afe5476ec3134d3963e2b1ab45 100644 (file)
@@ -28,7 +28,7 @@ pwritev2 (int fd, const struct iovec *vector, int count, off_t offset,
 # ifdef __NR_pwritev2
   ssize_t result = SYSCALL_CANCEL (pwritev2, fd, vector, count,
                                   LO_HI_LONG (offset), flags);
-  if (result >= 0)
+  if (result >= 0 || errno != ENOSYS)
     return result;
 # endif
   /* Trying to emulate the pwritev2 syscall flags is troublesome:
index 42da321149bce40de47c5e1cfe593378dd76faa5..17ea905aa6a8db9435467343ec945c3a73e8121f 100644 (file)
@@ -30,7 +30,7 @@ pwritev64v2 (int fd, const struct iovec *vector, int count, off64_t offset,
 #ifdef __NR_pwritev64v2
   ssize_t result = SYSCALL_CANCEL (pwritev64v2, fd, vector, count,
                                   LO_HI_LONG (offset), flags);
-  if (result >= 0)
+  if (result >= 0 || errno != ENOSYS)
     return result;
 #endif
   /* Trying to emulate the pwritev2 syscall flags is troublesome: