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
