Cygwin: select: fix overwriting fd sets if poll returns no fd

There's a long-standing bug in select.  If we have poll-only
descriptors in the fd set, select overwrites the incoming
fd sets with the polling result.  If none of the fds is ready,
select has to loop again.  But now the fd sets are set to all
zero and select hangs.

Fix this by utilizing the local fd sets r, w, e as storage for
the incoming fd sets and use them to initialize select_stuff.

If we have to loop, overwritung the incoming fd sets doesn't matter.

While at it, rename r, w, e to readfds_in, writefds_in, exceptfds_in.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2019-01-13 22:43:52 +01:00
parent d31f9f9c13
commit b6694df619
2 changed files with 11 additions and 6 deletions

View File

@ -65,3 +65,5 @@ Bug Fixes
even if file has been deleted. even if file has been deleted.
Addresses: https://cygwin.com/ml/cygwin/2018-12/msg00125.html Addresses: https://cygwin.com/ml/cygwin/2018-12/msg00125.html
https://cygwin.com/ml/cygwin/2018-12/msg00028.html https://cygwin.com/ml/cygwin/2018-12/msg00028.html
- Fix a bug in select(2) when polling HANDLE-less descriptors.

View File

@ -164,17 +164,20 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
select_stuff sel; select_stuff sel;
sel.return_on_signal = 0; sel.return_on_signal = 0;
/* Allocate some fd_set structures using the number of fds as a guide. */ /* Allocate fd_set structures to store incoming fd sets. */
fd_set *r = allocfd_set (maxfds); fd_set *readfds_in = allocfd_set (maxfds);
fd_set *w = allocfd_set (maxfds); fd_set *writefds_in = allocfd_set (maxfds);
fd_set *e = allocfd_set (maxfds); fd_set *exceptfds_in = allocfd_set (maxfds);
memcpy (readfds_in, readfds, sizeof_fd_set (maxfds));
memcpy (writefds_in, writefds, sizeof_fd_set (maxfds));
memcpy (exceptfds_in, exceptfds, sizeof_fd_set (maxfds));
do do
{ {
/* Build the select record per fd linked list and set state as /* Build the select record per fd linked list and set state as
needed. */ needed. */
for (int i = 0; i < maxfds; i++) for (int i = 0; i < maxfds; i++)
if (!sel.test_and_set (i, readfds, writefds, exceptfds)) if (!sel.test_and_set (i, readfds_in, writefds_in, exceptfds_in))
{ {
select_printf ("aborting due to test_and_set error"); select_printf ("aborting due to test_and_set error");
return -1; /* Invalid fd, maybe? */ return -1; /* Invalid fd, maybe? */
@ -186,7 +189,7 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
wait_state = select_stuff::select_ok; wait_state = select_stuff::select_ok;
else else
/* wait for an fd to become active or time out */ /* wait for an fd to become active or time out */
wait_state = sel.wait (r, w, e, us); wait_state = sel.wait (readfds, writefds, exceptfds, us);
select_printf ("sel.wait returns %d", wait_state); select_printf ("sel.wait returns %d", wait_state);