Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3343

Linux kernel writes back the remaining timeout when raw ppoll/pselect6
syscalls are interrupted by signals (-EINTR).

However, QEMU keeps the original timeout and loses the remaining time.

Both do_ppoll() and do_pselect6() now write back the timeout on -EINTR
to match the kernel.

Tested with the issue reproducer.

Signed-off-by: Sun Haoyu <[email protected]>
---
 linux-user/syscall.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 064bc604c9..f049223772 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1529,16 +1529,18 @@ static abi_long do_pselect6(abi_long arg1, abi_long 
arg2, abi_long arg3,
         if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) {
             return -TARGET_EFAULT;
         }
+    }
+    if (((ret == -TARGET_EINTR) || !is_error(ret)) && ts_addr) {
         if (time64) {
-            if (ts_addr && host_to_target_timespec64(ts_addr, &ts)) {
+            if (host_to_target_timespec64(ts_addr, &ts)) {
                 return -TARGET_EFAULT;
             }
         } else {
-            if (ts_addr && host_to_target_timespec(ts_addr, &ts)) {
+            if (host_to_target_timespec(ts_addr, &ts)) {
                 return -TARGET_EFAULT;
             }
         }
-    }
+     }
     return ret;
 }
 #endif
@@ -1606,7 +1608,7 @@ static abi_long do_ppoll(abi_long arg1, abi_long arg2, 
abi_long arg3,
         if (set) {
             finish_sigsuspend_mask(ret);
         }
-        if (!is_error(ret) && arg3) {
+        if ((ret == -TARGET_EINTR || !is_error(ret)) && arg3) {
             if (time64) {
                 if (host_to_target_timespec64(arg3, timeout_ts)) {
                     return -TARGET_EFAULT;
-- 
2.53.0


Reply via email to