From d1c7dc7411f0de2e228526b73f83ed2f0a1df5e8 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Sun, 19 Apr 2026 18:30:12 -0300 Subject: [PATCH] =?utf8?q?fix=20select()=20timeout=20conversion=20?= =?utf8?q?=E2=80=94=20was=20passing=20raw=20pointer=20as=20kernel=20timeou?= =?utf8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The select() wrapper in both newlib/posix_stubs.c and ulibc/unistd.c was casting the struct timeval* pointer directly to int and passing it as the 5th syscall argument. The kernel expects an int32_t timeout: -1 = infinite wait, 0 = poll (return immediately), >0 = ticks. When bash calls select() with timeout=NULL (infinite wait), the raw pointer value was interpreted as a large positive number (poll with timeout), not as -1 (infinite). Worse, on some code paths the pointer could be 0 (NULL), which the kernel treats as timeout=0 (poll only), causing select() to return 0 immediately with no fds ready — which bash interprets as EOF on stdin, causing it to exit immediately. Fix: convert struct timeval to int32_t ticks before passing to the kernel. NULL timeout → -1 (infinite). tv_sec=0, tv_usec=0 → 0 (poll). Otherwise convert ms → ticks (TIMER_HZ=100, 10ms/tick). --- newlib/libgloss/adros/posix_stubs.c | 15 ++++++++++++++- user/ulibc/src/unistd.c | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/newlib/libgloss/adros/posix_stubs.c b/newlib/libgloss/adros/posix_stubs.c index db32cadd..108e8260 100644 --- a/newlib/libgloss/adros/posix_stubs.c +++ b/newlib/libgloss/adros/posix_stubs.c @@ -451,8 +451,21 @@ int ioctl(int fd, unsigned long request, ...) { int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { + /* Kernel expects int32_t timeout: -1 = infinite, 0 = poll, >0 = ticks. + * TIMER_HZ = 100, so 1 tick = 10 ms. */ + int32_t tmo = -1; /* default: infinite wait */ + if (timeout) { + if (timeout->tv_sec == 0 && timeout->tv_usec == 0) { + tmo = 0; /* poll only */ + } else { + uint32_t ms = (uint32_t)timeout->tv_sec * 1000 + + (uint32_t)timeout->tv_usec / 1000; + tmo = (int32_t)(ms / 10); /* ms → ticks (10 ms/tick) */ + if (tmo < 1) tmo = 1; + } + } return _check(_sc5(SYS_SELECT, nfds, (int)readfds, (int)writefds, - (int)exceptfds, (int)timeout)); + (int)exceptfds, (int)tmo)); } int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c index 748b78a4..5993e2c8 100644 --- a/user/ulibc/src/unistd.c +++ b/user/ulibc/src/unistd.c @@ -335,8 +335,21 @@ void* sbrk(int increment) { int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout) { + /* Kernel expects int32_t timeout: -1 = infinite, 0 = poll, >0 = ticks. + * TIMER_HZ = 100, so 1 tick = 10 ms. */ + int32_t tmo = -1; + if (timeout) { + if (timeout->tv_sec == 0 && timeout->tv_usec == 0) { + tmo = 0; + } else { + uint32_t ms = (uint32_t)timeout->tv_sec * 1000 + + (uint32_t)timeout->tv_usec / 1000; + tmo = (int32_t)(ms / 10); + if (tmo < 1) tmo = 1; + } + } return __syscall_ret(_syscall5(SYS_SELECT, nfds, (int)readfds, (int)writefds, - (int)exceptfds, (int)timeout)); + (int)exceptfds, (int)tmo)); } int fcntl(int fd, int cmd, ...) { -- 2.43.0