linux-user: raw ppoll() and pselect6() lose timeout writeback on EINTR
## Host environment - Operating system: EndeavourOS x86_64 - OS/kernel version: Linux ArchLinux-desktop 6.19.6-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 04 Mar 202618:25:08 +0000 x86_64 GNU/Linux - Architecture: ALL - QEMU flavor: qemu-user(I tested qemu-x86_64 and riscv64) - QEMU version: 10.2.1 - QEMU command line: - qemu-x86_64 /tmp/test-04 ppoll - qemu-x86_64 /tmp/test-04 pselect6 ## Emulated/Virtualized environment QEMU-USER - Architecture: ALL ## Description of problem I can reproduce this locally with qemu-x86_64 10.2.1, and the source locations below match the 10.2.50 code layout. The linux-user implementations of raw ppoll() and pselect6() only copy the timeout back to guest memory on non-error returns. Relevant source locations: - linux-user/syscall.c:1522 - linux-user/syscall.c:1609 At those locations, timeout writeback is guarded by !is_error(ret). This is incorrect for raw syscall semantics. On Linux, raw ppoll() and pselect6() may update the timeout object even when the syscall returns -1 with errno = EINTR. Observed output on native Linux: ppoll_ret=-1 errno=4 remaining=0.999985506 got_sigalrm=1 pselect6_ret=-1 errno=4 remaining=0.999966902 got_sigalrm=1 Observed output under qemu-x86_64: ppoll_ret=-1 errno=4 remaining=2.000000000 got_sigalrm=1 pselect6_ret=-1 errno=4 remaining=2.000000000 got_sigalrm=1 Expected behavior: the remaining timeout should still be written back on EINTR, matching Linux raw syscall behavior. Actual behavior: QEMU preserves the original timeout and drops the remaining-time information. This is a direct guest-visible raw-syscall semantic mismatch. ## Steps to reproduce 1. The minimal reproducer file `tests/04_ppoll_pselect6_timeout.c`. [04_ppoll_pselect6_timeout.c](/uploads/042f2e5cab86fed0dcd428dac5ccb7b1/04_ppoll_pselect6_timeout.c) 2. Build it with `gcc -O2 -Wall -Wextra -static -o /tmp/test-04 tests/04_ppoll_pselect6_timeout.c`. 3. Run on native Linux with `/tmp/test-04 ppoll` and `/tmp/test-04 pselect6`. 4. Run under QEMU with `qemu-x86_64 /tmp/test-04 ppoll` and `qemu-x86_64 /tmp/test-04 pselect6`. 5. Compare the timeout value after the syscall returns. The test arms alarm(1), passes a 2-second timeout, calls the raw syscall directly, and prints the timeout value after return. ## Additional information I am a student from Xidian University. I quickly discovered some bugs through Codex. I know that QEMU does not accept content directly generated by AI, so I have verified them and confirm that this bug is **valid**. ```c #define _GNU_SOURCE #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/select.h> #include <sys/syscall.h> #include <time.h> #include <unistd.h> static volatile sig_atomic_t got_sigalrm; static void on_sigalrm(int signo) { (void)signo; got_sigalrm = 1; } static int run_ppoll(void) { struct timespec timeout = { .tv_sec = 2, .tv_nsec = 0 }; long ret; int saved_errno; got_sigalrm = 0; alarm(1); ret = syscall(SYS_ppoll, NULL, 0UL, &timeout, NULL, 0UL); saved_errno = errno; alarm(0); printf("ppoll_ret=%ld errno=%d remaining=%lld.%09ld got_sigalrm=%d\n", ret, saved_errno, (long long)timeout.tv_sec, timeout.tv_nsec, got_sigalrm ? 1 : 0); return 0; } static int run_pselect6(void) { struct timespec timeout = { .tv_sec = 2, .tv_nsec = 0 }; long ret; int saved_errno; got_sigalrm = 0; alarm(1); ret = syscall(SYS_pselect6, 0, NULL, NULL, NULL, &timeout, NULL); saved_errno = errno; alarm(0); printf("pselect6_ret=%ld errno=%d remaining=%lld.%09ld got_sigalrm=%d\n", ret, saved_errno, (long long)timeout.tv_sec, timeout.tv_nsec, got_sigalrm ? 1 : 0); return 0; } int main(int argc, char **argv) { struct sigaction sa; if (argc != 2) { fprintf(stderr, "usage: %s ppoll|pselect6\n", argv[0]); return 2; } memset(&sa, 0, sizeof(sa)); sa.sa_handler = on_sigalrm; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, NULL) < 0) { perror("sigaction"); return 1; } if (strcmp(argv[1], "ppoll") == 0) { return run_ppoll(); } if (strcmp(argv[1], "pselect6") == 0) { return run_pselect6(); } fprintf(stderr, "usage: %s ppoll|pselect6\n", argv[0]); return 2; } ```
issue