Fix misreported errno on preadv2/pwritev2 (BZ#23579)

Message ID 20180829212032.32507-1-adhemerval.zanella@linaro.org
State Accepted
Commit 7a16bdbb9ff4122af0a28dc20996c95352011fdd
Headers show
Series
  • Fix misreported errno on preadv2/pwritev2 (BZ#23579)
Related show

Commit Message

Adhemerval Zanella Aug. 29, 2018, 9:20 p.m.
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                             | 13 ++++++
 misc/tst-preadvwritev2-common.c       | 65 +++++++++++++++++++++++++--
 misc/tst-preadvwritev2.c              |  2 +
 misc/tst-preadvwritev64v2.c           |  2 +
 sysdeps/unix/sysv/linux/preadv2.c     |  2 +-
 sysdeps/unix/sysv/linux/preadv64v2.c  |  2 +-
 sysdeps/unix/sysv/linux/pwritev2.c    |  2 +-
 sysdeps/unix/sysv/linux/pwritev64v2.c |  2 +-
 8 files changed, 83 insertions(+), 7 deletions(-)

-- 
2.17.1

Comments

Adhemerval Zanella Sept. 20, 2018, 6:32 p.m. | #1
Ping.

On 29/08/2018 14:20, Adhemerval Zanella wrote:
> 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                             | 13 ++++++

>  misc/tst-preadvwritev2-common.c       | 65 +++++++++++++++++++++++++--

>  misc/tst-preadvwritev2.c              |  2 +

>  misc/tst-preadvwritev64v2.c           |  2 +

>  sysdeps/unix/sysv/linux/preadv2.c     |  2 +-

>  sysdeps/unix/sysv/linux/preadv64v2.c  |  2 +-

>  sysdeps/unix/sysv/linux/pwritev2.c    |  2 +-

>  sysdeps/unix/sysv/linux/pwritev64v2.c |  2 +-

>  8 files changed, 83 insertions(+), 7 deletions(-)

> 

> diff --git a/misc/tst-preadvwritev2-common.c b/misc/tst-preadvwritev2-common.c

> index f889a21544..50b9da3fea 100644

> --- a/misc/tst-preadvwritev2-common.c

> +++ b/misc/tst-preadvwritev2-common.c

> @@ -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);

> diff --git a/misc/tst-preadvwritev2.c b/misc/tst-preadvwritev2.c

> index be22802dbe..cb58cbe41e 100644

> --- a/misc/tst-preadvwritev2.c

> +++ b/misc/tst-preadvwritev2.c

> @@ -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);

>  }

> diff --git a/misc/tst-preadvwritev64v2.c b/misc/tst-preadvwritev64v2.c

> index 8d3cc32b28..6a9de54c78 100644

> --- a/misc/tst-preadvwritev64v2.c

> +++ b/misc/tst-preadvwritev64v2.c

> @@ -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);

>  }

> diff --git a/sysdeps/unix/sysv/linux/preadv2.c b/sysdeps/unix/sysv/linux/preadv2.c

> index c8bf0764ef..bb08cbc5fd 100644

> --- a/sysdeps/unix/sysv/linux/preadv2.c

> +++ b/sysdeps/unix/sysv/linux/preadv2.c

> @@ -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:

> diff --git a/sysdeps/unix/sysv/linux/preadv64v2.c b/sysdeps/unix/sysv/linux/preadv64v2.c

> index d7400a0252..b72a047347 100644

> --- a/sysdeps/unix/sysv/linux/preadv64v2.c

> +++ b/sysdeps/unix/sysv/linux/preadv64v2.c

> @@ -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:

> diff --git a/sysdeps/unix/sysv/linux/pwritev2.c b/sysdeps/unix/sysv/linux/pwritev2.c

> index 29c2264c8f..26333ebd43 100644

> --- a/sysdeps/unix/sysv/linux/pwritev2.c

> +++ b/sysdeps/unix/sysv/linux/pwritev2.c

> @@ -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:

> diff --git a/sysdeps/unix/sysv/linux/pwritev64v2.c b/sysdeps/unix/sysv/linux/pwritev64v2.c

> index 42da321149..17ea905aa6 100644

> --- a/sysdeps/unix/sysv/linux/pwritev64v2.c

> +++ b/sysdeps/unix/sysv/linux/pwritev64v2.c

> @@ -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:

>
Adhemerval Zanella Sept. 28, 2018, 2 p.m. | #2
I will commit this patch shortly if no one opposes it.

On 20/09/2018 15:32, Adhemerval Zanella wrote:
> Ping.

> 

> On 29/08/2018 14:20, Adhemerval Zanella wrote:

>> 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                             | 13 ++++++

>>  misc/tst-preadvwritev2-common.c       | 65 +++++++++++++++++++++++++--

>>  misc/tst-preadvwritev2.c              |  2 +

>>  misc/tst-preadvwritev64v2.c           |  2 +

>>  sysdeps/unix/sysv/linux/preadv2.c     |  2 +-

>>  sysdeps/unix/sysv/linux/preadv64v2.c  |  2 +-

>>  sysdeps/unix/sysv/linux/pwritev2.c    |  2 +-

>>  sysdeps/unix/sysv/linux/pwritev64v2.c |  2 +-

>>  8 files changed, 83 insertions(+), 7 deletions(-)

>>

>> diff --git a/misc/tst-preadvwritev2-common.c b/misc/tst-preadvwritev2-common.c

>> index f889a21544..50b9da3fea 100644

>> --- a/misc/tst-preadvwritev2-common.c

>> +++ b/misc/tst-preadvwritev2-common.c

>> @@ -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);

>> diff --git a/misc/tst-preadvwritev2.c b/misc/tst-preadvwritev2.c

>> index be22802dbe..cb58cbe41e 100644

>> --- a/misc/tst-preadvwritev2.c

>> +++ b/misc/tst-preadvwritev2.c

>> @@ -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);

>>  }

>> diff --git a/misc/tst-preadvwritev64v2.c b/misc/tst-preadvwritev64v2.c

>> index 8d3cc32b28..6a9de54c78 100644

>> --- a/misc/tst-preadvwritev64v2.c

>> +++ b/misc/tst-preadvwritev64v2.c

>> @@ -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);

>>  }

>> diff --git a/sysdeps/unix/sysv/linux/preadv2.c b/sysdeps/unix/sysv/linux/preadv2.c

>> index c8bf0764ef..bb08cbc5fd 100644

>> --- a/sysdeps/unix/sysv/linux/preadv2.c

>> +++ b/sysdeps/unix/sysv/linux/preadv2.c

>> @@ -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:

>> diff --git a/sysdeps/unix/sysv/linux/preadv64v2.c b/sysdeps/unix/sysv/linux/preadv64v2.c

>> index d7400a0252..b72a047347 100644

>> --- a/sysdeps/unix/sysv/linux/preadv64v2.c

>> +++ b/sysdeps/unix/sysv/linux/preadv64v2.c

>> @@ -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:

>> diff --git a/sysdeps/unix/sysv/linux/pwritev2.c b/sysdeps/unix/sysv/linux/pwritev2.c

>> index 29c2264c8f..26333ebd43 100644

>> --- a/sysdeps/unix/sysv/linux/pwritev2.c

>> +++ b/sysdeps/unix/sysv/linux/pwritev2.c

>> @@ -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:

>> diff --git a/sysdeps/unix/sysv/linux/pwritev64v2.c b/sysdeps/unix/sysv/linux/pwritev64v2.c

>> index 42da321149..17ea905aa6 100644

>> --- a/sysdeps/unix/sysv/linux/pwritev64v2.c

>> +++ b/sysdeps/unix/sysv/linux/pwritev64v2.c

>> @@ -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:

>>
Florian Weimer Sept. 28, 2018, 2:10 p.m. | #3
* Adhemerval Zanella:

> 	[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.


Looks okay to me.

I see that we return ENOTSUP for invalid flags, but the kernel returns
EINVAL.  But this is a preexisting problem, unrelated to this patch.

Thanks,
Florian
Adhemerval Zanella Sept. 28, 2018, 2:24 p.m. | #4
On 28/09/2018 11:10, Florian Weimer wrote:
> * Adhemerval Zanella:

> 

>> 	[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.

> 

> Looks okay to me.

> 

> I see that we return ENOTSUP for invalid flags, but the kernel returns

> EINVAL.  But this is a preexisting problem, unrelated to this patch.


Yeah, I described this discrepancy in commit message and I can't really see
how to easy emulate it.
Florian Weimer Sept. 28, 2018, 2:37 p.m. | #5
* Adhemerval Zanella:

> On 28/09/2018 11:10, Florian Weimer wrote:

>> * Adhemerval Zanella:

>> 

>>> 	[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.

>> 

>> Looks okay to me.

>> 

>> I see that we return ENOTSUP for invalid flags, but the kernel returns

>> EINVAL.  But this is a preexisting problem, unrelated to this patch.

>

> Yeah, I described this discrepancy in commit message and I can't really see

> how to easy emulate it.


I think there are two issues: EBADF vs flag error, and that the flag
error is ENOTSUP, not EINVAL as the kernel returns (according to the
manual page at least).  The second part would be fixable, but not the
first part.

Thanks,
Florian
Adhemerval Zanella Sept. 28, 2018, 7:57 p.m. | #6
On 28/09/2018 11:37, Florian Weimer wrote:
> * Adhemerval Zanella:

> 

>> On 28/09/2018 11:10, Florian Weimer wrote:

>>> * Adhemerval Zanella:

>>>

>>>> 	[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.

>>>

>>> Looks okay to me.

>>>

>>> I see that we return ENOTSUP for invalid flags, but the kernel returns

>>> EINVAL.  But this is a preexisting problem, unrelated to this patch.

>>

>> Yeah, I described this discrepancy in commit message and I can't really see

>> how to easy emulate it.

> 

> I think there are two issues: EBADF vs flag error, and that the flag

> error is ENOTSUP, not EINVAL as the kernel returns (according to the

> manual page at least).  The second part would be fixable, but not the

> first part.


The EINVAL for invalid flags is not what I am seeing with 4.15.0:

preadv2(3, [{iov_base=0x7ffcb69d1620, iov_len=32}], 1, 0, 0x20 /* RWF_??? */) = -1 EOPNOTSUPP (Operation not supported)
pwritev2(3, [{iov_base="\20\0\0\0\0\0\0\0\254\205\256[\0\0\0\0P\255(\16\0\0\0\0\254\205\256[\0\0\0\0", iov_len=32}], 1, 0, 0x20 /* RWF_??? */) = -1 EOPNOTSUPP (Operation not supported)

And this I confirmed that is what kernels is doing:

pwritev2
\_ do_writev | do_pwritev
   \_ vfs_writev
      \_ do_iter_write
         \_ do_iter_readv_writev
            \_ kiocb_set_rw_flags

 665 static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter,
 666                 loff_t *ppos, int type, rwf_t flags)
 667 {
 668         struct kiocb kiocb;
 669         ssize_t ret;
 670 
 671         init_sync_kiocb(&kiocb, filp);
 672         ret = kiocb_set_rw_flags(&kiocb, flags);
 673         if (ret)
 674                 return ret;

static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags)
{
        if (unlikely(flags & ~RWF_SUPPORTED))
                return -EOPNOTSUPP;


         \_ do_loop_readv_writev

 687 static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,        
 688                 loff_t *ppos, int type, rwf_t flags)                                 
 689 {       
 690         ssize_t ret = 0;                                                             
 691         
 692         if (flags & ~RWF_HIPRI)
 693                 return -EOPNOTSUPP;     

I think we should update man-pages regarding this behaviour.
Szabolcs Nagy Oct. 23, 2018, 1:09 p.m. | #7
On 29/08/18 22:20, Adhemerval Zanella wrote:
> 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.



with build-many-glibcs.py in
logs/glibcs/i686-gnu/010-glibcs-i686-gnu-check-log.txt i see

In file included from tst-preadvwritev2.c:26:
tst-preadvwritev2-common.c: In function ‘do_test_with_invalid_iov’:
tst-preadvwritev2-common.c:89:22: error: ‘IOV_MAX’ undeclared (first use in this function); did you mean ‘INT_MAX’?
     struct iovec iov[IOV_MAX+1] = { 0 };
                      ^~~~~~~
                      INT_MAX
tst-preadvwritev2-common.c:89:22: note: each undeclared identifier is reported only once for each function it appears in
tst-preadvwritev2-common.c:89:18: error: unused variable ‘iov’ [-Werror=unused-variable]
     struct iovec iov[IOV_MAX+1] = { 0 };
                  ^~~
cc1: all warnings being treated as errors
make[3]: *** [../o-iterator.mk:9: /data/glibc-build-j2/build/glibcs/i686-gnu/glibc/misc/tst-preadvwritev2.o] Error 1

...
> +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);

> +  }

> +}
Adhemerval Zanella Oct. 23, 2018, 2:18 p.m. | #8
On 23/10/2018 10:09, Szabolcs Nagy wrote:
> On 29/08/18 22:20, Adhemerval Zanella wrote:

>> 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.

> 

> 

> with build-many-glibcs.py in

> logs/glibcs/i686-gnu/010-glibcs-i686-gnu-check-log.txt i see

> 

> In file included from tst-preadvwritev2.c:26:

> tst-preadvwritev2-common.c: In function ‘do_test_with_invalid_iov’:

> tst-preadvwritev2-common.c:89:22: error: ‘IOV_MAX’ undeclared (first use in this function); did you mean ‘INT_MAX’?

>      struct iovec iov[IOV_MAX+1] = { 0 };

>                       ^~~~~~~

>                       INT_MAX

> tst-preadvwritev2-common.c:89:22: note: each undeclared identifier is reported only once for each function it appears in

> tst-preadvwritev2-common.c:89:18: error: unused variable ‘iov’ [-Werror=unused-variable]

>      struct iovec iov[IOV_MAX+1] = { 0 };

>                   ^~~

> cc1: all warnings being treated as errors

> make[3]: *** [../o-iterator.mk:9: /data/glibc-build-j2/build/glibcs/i686-gnu/glibc/misc/tst-preadvwritev2.o] Error 1

> 


It seems HURD uses the generic bits/uio_lim.h which does not define
IOV_MAX. Loos like we need to define it on test for Hurd. I will fix
it, thanks to bring this up.
Joseph Myers Oct. 23, 2018, 9:37 p.m. | #9
On Tue, 23 Oct 2018, Szabolcs Nagy wrote:

> with build-many-glibcs.py in

> logs/glibcs/i686-gnu/010-glibcs-i686-gnu-check-log.txt i see

> 

> In file included from tst-preadvwritev2.c:26:

> tst-preadvwritev2-common.c: In function ‘do_test_with_invalid_iov’:

> tst-preadvwritev2-common.c:89:22: error: ‘IOV_MAX’ undeclared (first use in this function); did you mean ‘INT_MAX’?

>      struct iovec iov[IOV_MAX+1] = { 0 };

>                       ^~~~~~~

>                       INT_MAX

> tst-preadvwritev2-common.c:89:22: note: each undeclared identifier is reported only once for each function it appears in

> tst-preadvwritev2-common.c:89:18: error: unused variable ‘iov’ [-Werror=unused-variable]

>      struct iovec iov[IOV_MAX+1] = { 0 };

>                   ^~~

> cc1: all warnings being treated as errors

> make[3]: *** [../o-iterator.mk:9: /data/glibc-build-j2/build/glibcs/i686-gnu/glibc/misc/tst-preadvwritev2.o] Error 1


I guess this wasn't readily visible to people running build-many-glibcs.py 
tests because the current expected baseline is that the compilation 
testsuite doesn't have clean results on i686-gnu, ever since the C11 
threads support went in (so that this build failure appeared is only 
visible if you actually look into the log files, not if you just look at 
the summary results of build-many-glibcs.py).

It would of course be good to have C11 threads support for Hurd (sharing 
as much as possible of the code to implement C11 threads on top of 
pthreads, and the associated tests for C11 threads, with those used with 
NPTL).  Until we have that, maybe it would be possible to XFAIL the 
relevant conform/ tests in some Hurd sysdeps Makefile (with a comment 
referencing a bug open in Bugzilla about the lack of C11 threads support 
for Hurd)?  Finding regressions with build-many-glibcs.py works much 
better if the normal expected state is no failures at all.

(ia64 has also been broken with GCC mainline for over a month - 
<https://gcc.gnu.org/ml/gcc-patches/2018-09/msg01052.html>.)

-- 
Joseph S. Myers
joseph@codesourcery.com
Adhemerval Zanella Oct. 24, 2018, 11:27 a.m. | #10
On 23/10/2018 18:37, Joseph Myers wrote:
> On Tue, 23 Oct 2018, Szabolcs Nagy wrote:

> 

>> with build-many-glibcs.py in

>> logs/glibcs/i686-gnu/010-glibcs-i686-gnu-check-log.txt i see

>>

>> In file included from tst-preadvwritev2.c:26:

>> tst-preadvwritev2-common.c: In function ‘do_test_with_invalid_iov’:

>> tst-preadvwritev2-common.c:89:22: error: ‘IOV_MAX’ undeclared (first use in this function); did you mean ‘INT_MAX’?

>>      struct iovec iov[IOV_MAX+1] = { 0 };

>>                       ^~~~~~~

>>                       INT_MAX

>> tst-preadvwritev2-common.c:89:22: note: each undeclared identifier is reported only once for each function it appears in

>> tst-preadvwritev2-common.c:89:18: error: unused variable ‘iov’ [-Werror=unused-variable]

>>      struct iovec iov[IOV_MAX+1] = { 0 };

>>                   ^~~

>> cc1: all warnings being treated as errors

>> make[3]: *** [../o-iterator.mk:9: /data/glibc-build-j2/build/glibcs/i686-gnu/glibc/misc/tst-preadvwritev2.o] Error 1

> 

> I guess this wasn't readily visible to people running build-many-glibcs.py 

> tests because the current expected baseline is that the compilation 

> testsuite doesn't have clean results on i686-gnu, ever since the C11 

> threads support went in (so that this build failure appeared is only 

> visible if you actually look into the log files, not if you just look at 

> the summary results of build-many-glibcs.py).

> 

> It would of course be good to have C11 threads support for Hurd (sharing 

> as much as possible of the code to implement C11 threads on top of 

> pthreads, and the associated tests for C11 threads, with those used with 

> NPTL).  Until we have that, maybe it would be possible to XFAIL the 

> relevant conform/ tests in some Hurd sysdeps Makefile (with a comment 

> referencing a bug open in Bugzilla about the lack of C11 threads support 

> for Hurd)?  Finding regressions with build-many-glibcs.py works much 

> better if the normal expected state is no failures at all.


I agree it would be better to just XFAIL C11 threads conformance on Hurd,
I don't plan to work on it in near feature and I presume it is a low
priority issue for Hurd developers.

> 

> (ia64 has also been broken with GCC mainline for over a month - 

> <https://gcc.gnu.org/ml/gcc-patches/2018-09/msg01052.html>.)

>
Samuel Thibault Oct. 24, 2018, 11:57 p.m. | #11
Hello,

Adhemerval Zanella, le mer. 24 oct. 2018 08:27:22 -0300, a ecrit:
> On 23/10/2018 18:37, Joseph Myers wrote:

> > maybe it would be possible to XFAIL the relevant conform/ tests in

> > some Hurd sysdeps Makefile (with a comment referencing a bug open in

> > Bugzilla about the lack of C11 threads support for Hurd)?

> 

> I agree it would be better to just XFAIL C11 threads conformance on Hurd,

> I don't plan to work on it in near feature and I presume it is a low

> priority issue for Hurd developers.


Indeed, so I did so.

Samuel

Patch

diff --git a/misc/tst-preadvwritev2-common.c b/misc/tst-preadvwritev2-common.c
index f889a21544..50b9da3fea 100644
--- a/misc/tst-preadvwritev2-common.c
+++ b/misc/tst-preadvwritev2-common.c
@@ -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);
diff --git a/misc/tst-preadvwritev2.c b/misc/tst-preadvwritev2.c
index be22802dbe..cb58cbe41e 100644
--- a/misc/tst-preadvwritev2.c
+++ b/misc/tst-preadvwritev2.c
@@ -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);
 }
diff --git a/misc/tst-preadvwritev64v2.c b/misc/tst-preadvwritev64v2.c
index 8d3cc32b28..6a9de54c78 100644
--- a/misc/tst-preadvwritev64v2.c
+++ b/misc/tst-preadvwritev64v2.c
@@ -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);
 }
diff --git a/sysdeps/unix/sysv/linux/preadv2.c b/sysdeps/unix/sysv/linux/preadv2.c
index c8bf0764ef..bb08cbc5fd 100644
--- a/sysdeps/unix/sysv/linux/preadv2.c
+++ b/sysdeps/unix/sysv/linux/preadv2.c
@@ -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:
diff --git a/sysdeps/unix/sysv/linux/preadv64v2.c b/sysdeps/unix/sysv/linux/preadv64v2.c
index d7400a0252..b72a047347 100644
--- a/sysdeps/unix/sysv/linux/preadv64v2.c
+++ b/sysdeps/unix/sysv/linux/preadv64v2.c
@@ -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:
diff --git a/sysdeps/unix/sysv/linux/pwritev2.c b/sysdeps/unix/sysv/linux/pwritev2.c
index 29c2264c8f..26333ebd43 100644
--- a/sysdeps/unix/sysv/linux/pwritev2.c
+++ b/sysdeps/unix/sysv/linux/pwritev2.c
@@ -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:
diff --git a/sysdeps/unix/sysv/linux/pwritev64v2.c b/sysdeps/unix/sysv/linux/pwritev64v2.c
index 42da321149..17ea905aa6 100644
--- a/sysdeps/unix/sysv/linux/pwritev64v2.c
+++ b/sysdeps/unix/sysv/linux/pwritev64v2.c
@@ -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: