mirror of
https://github.com/qemu/qemu.git
synced 2026-04-05 22:00:58 +00:00
linux-user: update select timeout writeback
The Linux kernel writes back the remaining timeout for select-family syscalls in poll_select_finish(). If that writeback fails, it keeps the original return value. However, QEMU only writes back the timeout on success. If the writeback fails, QEMU returns -TARGET_EFAULT. This can lose the remaining timeout and change the return value. Update do_select(), do_pselect6(), and do_ppoll() to always write back the timeout to match the Linux kernel's behavior. If the timeout writeback fails, keep the original return value. Tested with the issue reproducer. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3343 Signed-off-by: Sun Haoyu <shyliuli@aosc.io> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260320111647.138984-1-shyliuli@aosc.io Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -1384,14 +1384,15 @@ static abi_long do_select(int n,
|
||||
return -TARGET_EFAULT;
|
||||
if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n))
|
||||
return -TARGET_EFAULT;
|
||||
|
||||
if (target_tv_addr) {
|
||||
tv.tv_sec = ts.tv_sec;
|
||||
tv.tv_usec = ts.tv_nsec / 1000;
|
||||
if (copy_to_user_timeval(target_tv_addr, &tv)) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_tv_addr) {
|
||||
tv.tv_sec = ts.tv_sec;
|
||||
tv.tv_usec = ts.tv_nsec / 1000;
|
||||
/*
|
||||
* Like the kernel, we deliberately ignore possible
|
||||
* failures writing back to the timeout struct.
|
||||
*/
|
||||
copy_to_user_timeval(target_tv_addr, &tv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1519,14 +1520,16 @@ 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 (ts_addr) {
|
||||
/*
|
||||
* Like the kernel, we deliberately ignore possible
|
||||
* failures writing back to the timeout struct.
|
||||
*/
|
||||
if (time64) {
|
||||
if (ts_addr && host_to_target_timespec64(ts_addr, &ts)) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
host_to_target_timespec64(ts_addr, &ts);
|
||||
} else {
|
||||
if (ts_addr && host_to_target_timespec(ts_addr, &ts)) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
host_to_target_timespec(ts_addr, &ts);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -1596,15 +1599,15 @@ 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 (arg3) {
|
||||
/*
|
||||
* Like the kernel, we deliberately ignore possible
|
||||
* failures writing back to the timeout struct.
|
||||
*/
|
||||
if (time64) {
|
||||
if (host_to_target_timespec64(arg3, timeout_ts)) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
host_to_target_timespec64(arg3, timeout_ts);
|
||||
} else {
|
||||
if (host_to_target_timespec(arg3, timeout_ts)) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
host_to_target_timespec(arg3, timeout_ts);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user