diff mbox series

[2/4] linux-user: Fix accept4(SOCK_NONBLOCK) syscall

Message ID 20230707131928.89500-3-deller@gmx.de
State New
Headers show
Series linux-user: Fix fcntl64() and accept4() for 32-bit targets | expand

Commit Message

Helge Deller July 7, 2023, 1:19 p.m. UTC
The accept4() syscall takes two flags only: SOCK_NONBLOCK and
SOCK_CLOEXEC.
Even the real Linux kernel returns -EINVAL if any other bits
have been set.

Change the implementation of accept4() to recognize those two values
only, instead of using the fcntl_flags_tbl[] bitmask translation.

Beside this correction in behaviour, it actually fixes the accept4()
emulation for hppa, mips and alpha targets for which SOCK_NONBLOCK is
different than TARGET_SOCK_NONBLOCK.

I noticed this wrong behaviour with the testcase of the debian lwt package
which failed (by timeout while hanging in the read() syscall) in qemu but
succeeded on real hardware.

Signed-off-by: Helge Deller <deller@gmx.de>
---
 linux-user/syscall.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

--
2.41.0

Comments

Richard Henderson July 7, 2023, 8:15 p.m. UTC | #1
On 7/7/23 14:19, Helge Deller wrote:
> The accept4() syscall takes two flags only: SOCK_NONBLOCK and
> SOCK_CLOEXEC.
> Even the real Linux kernel returns -EINVAL if any other bits
> have been set.
> 
> Change the implementation of accept4() to recognize those two values
> only, instead of using the fcntl_flags_tbl[] bitmask translation.
> 
> Beside this correction in behaviour, it actually fixes the accept4()
> emulation for hppa, mips and alpha targets for which SOCK_NONBLOCK is
> different than TARGET_SOCK_NONBLOCK.
> 
> I noticed this wrong behaviour with the testcase of the debian lwt package
> which failed (by timeout while hanging in the read() syscall) in qemu but
> succeeded on real hardware.
> 
> Signed-off-by: Helge Deller <deller@gmx.de>
> ---
>   linux-user/syscall.c | 13 ++++++++++++-
>   1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 3f1e8e7ad9..9e9317237d 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -3440,7 +3440,18 @@ static abi_long do_accept4(int fd, abi_ulong target_addr,
>       abi_long ret;
>       int host_flags;
> 
> -    host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl);
> +    host_flags = 0;
> +#if defined(SOCK_NONBLOCK)
> +    if (flags & ~(TARGET_SOCK_CLOEXEC | TARGET_SOCK_NONBLOCK)) {
> +        return -TARGET_EINVAL;
> +    }
> +    if (flags & TARGET_SOCK_NONBLOCK) {
> +        host_flags |= SOCK_NONBLOCK;
> +    }
> +#endif

Can we avoid the ifdef?  Anyway, surely the TARGET bit check should not be protected by 
the #ifdef.


r~
Helge Deller July 7, 2023, 8:46 p.m. UTC | #2
On 7/7/23 22:15, Richard Henderson wrote:
> On 7/7/23 14:19, Helge Deller wrote:
>> The accept4() syscall takes two flags only: SOCK_NONBLOCK and
>> SOCK_CLOEXEC.
>> Even the real Linux kernel returns -EINVAL if any other bits
>> have been set.
>>
>> Change the implementation of accept4() to recognize those two values
>> only, instead of using the fcntl_flags_tbl[] bitmask translation.
>>
>> Beside this correction in behaviour, it actually fixes the accept4()
>> emulation for hppa, mips and alpha targets for which SOCK_NONBLOCK is
>> different than TARGET_SOCK_NONBLOCK.
>>
>> I noticed this wrong behaviour with the testcase of the debian lwt package
>> which failed (by timeout while hanging in the read() syscall) in qemu but
>> succeeded on real hardware.
>>
>> Signed-off-by: Helge Deller <deller@gmx.de>
>> ---
>>   linux-user/syscall.c | 13 ++++++++++++-
>>   1 file changed, 12 insertions(+), 1 deletion(-)
>>
>> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
>> index 3f1e8e7ad9..9e9317237d 100644
>> --- a/linux-user/syscall.c
>> +++ b/linux-user/syscall.c
>> @@ -3440,7 +3440,18 @@ static abi_long do_accept4(int fd, abi_ulong target_addr,
>>       abi_long ret;
>>       int host_flags;
>>
>> -    host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl);
>> +    host_flags = 0;
>> +#if defined(SOCK_NONBLOCK)
>> +    if (flags & ~(TARGET_SOCK_CLOEXEC | TARGET_SOCK_NONBLOCK)) {
>> +        return -TARGET_EINVAL;
>> +    }
>> +    if (flags & TARGET_SOCK_NONBLOCK) {
>> +        host_flags |= SOCK_NONBLOCK;
>> +    }
>> +#endif
>
> Can we avoid the ifdef?

I don't know. There are multiple such SOCK_NONBLOCK checks in the code.

> Anyway, surely the TARGET bit check should not be protected by the #ifdef.

Ok.

Helge
diff mbox series

Patch

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 3f1e8e7ad9..9e9317237d 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -3440,7 +3440,18 @@  static abi_long do_accept4(int fd, abi_ulong target_addr,
     abi_long ret;
     int host_flags;

-    host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl);
+    host_flags = 0;
+#if defined(SOCK_NONBLOCK)
+    if (flags & ~(TARGET_SOCK_CLOEXEC | TARGET_SOCK_NONBLOCK)) {
+        return -TARGET_EINVAL;
+    }
+    if (flags & TARGET_SOCK_NONBLOCK) {
+        host_flags |= SOCK_NONBLOCK;
+    }
+#endif
+    if (flags & TARGET_SOCK_CLOEXEC) {
+        host_flags |= SOCK_CLOEXEC;
+    }

     if (target_addr == 0) {
         return get_errno(safe_accept4(fd, NULL, NULL, host_flags));