]> 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 18:16:04 +0000 (15:16 -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.

(cherry picked from commit 7a16bdbb9ff4122af0a28dc20996c95352011fdd)

ChangeLog
NEWS
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 4baa85bcfcb65e4e72aca34e52b3317b5f58351a..faa13471fbf30be2b439f380eaaf7860014ee94e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+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.
+       * NEWS: Add bug fixed.
+
 2018-09-28  Florian Weimer  <fweimer@redhat.com>
 
        [BZ #22753]
diff --git a/NEWS b/NEWS
index d5e3403e8ba52ff44a8bd847478ddb3937069139..299fba24b1a3adb6393c451624f8d73e8872fd41 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -151,6 +151,7 @@ The following bugs are resolved with this release:
   [23363] stdio-common/tst-printf.c has non-free license
   [23456] Wrong index_cpu_LZCNT
   [23459] COMMON_CPUID_INDEX_80000001 isn't populated for Intel processors
+  [23579] libc: Errors misreported in preadv2
 \f
 Version 2.26
 
index 8abedc14d0602b8e7aeeb1c3108678c9271760d6..9570882fa68645312c9f85859894237cedafd7a6 100644 (file)
 #include <limits.h>
 #include <support/check.h>
 
+#ifndef RWF_HIPRI
+# define RWF_HIPRI 0
+#endif
+#ifndef RWF_DSYNC
+# define RWF_DSYNC 0
+#endif
+#ifndef RWF_SYNC
+# define RWF_SYNC 0
+#endif
+#ifndef RWF_NOWAIT
+# define RWF_NOWAIT 0
+#endif
+#ifndef RWF_APPEND
+# define RWF_APPEND 0
+#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)
 {
-#define RWF_SUPPORTED  (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT)
   /* Set the next bit from the mask of all supported flags.  */
   int invalid_flag = __builtin_clz (RWF_SUPPORTED);
   invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag);
index 225f5e59c15b21708ecda7b3347f823216ad5990..bebd2e56d6edc96dc9283860625826ec78c53142 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 facdbdca616d98dc064d19cfeeb974136b783b53..3d03e32484df0d8adfd3dad040651705e023cfa4 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 a7a3aeeee691f84c56cf74d55094e0a1cb23c7d4..c7f91025f25b9b829feeae55be12fbab3829800a 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 53c946861c2ba6d8d53cab499faf40c9b02f4f3f..4dbea0a8d4d66e6087fce905e7b18e863c3c965e 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 17677df98d7ae05c48648f109db462650771dcab..a33ed56c55f992901e788e84809ba07156415435 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 f5753eede5b3f857d73deef1b665824351ceea08..f0ffd2f1f617ce845aff449cc3858e37f793e337 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: