From eccd4bf781e99f57902a6189e4acb572479d0c49 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 6 Feb 2020 23:48:36 +0000 Subject: [PATCH] pollwrap: stop returning unasked-for rwx statuses. The sets of poll(2) events that we check in order to return SELECT_R and SELECT_W overlap: to be precise, they have POLLERR in common. So if an fd signals POLLERR, then pollwrap_get_fd_rwx will respond by saying that it has both SELECT_R and SELECT_W available on it - even if the caller had only asked for one of those. In other words, you can get a spurious SELECT_W notification on an fd that you never asked for SELECT_W on in the first place. This definitely isn't what I'd meant that API to do. In particular, if a socket in the middle of an asynchronous connect() signals POLLERR, then Unix Plink will call select_result for it with SELECT_R and then SELECT_W respectively. The former will notice that it's got an error condition and call plug_closing - and _then_ the latter will decide that it's writable and set s->connected! The plan was to only select it for write until it was connected, but this bug in pollwrap was defeating that plan. Now pollwrap_get_fd_rwx should only ever return a set of rwx flags that's a subset of the one that the client asked for via pollwrap_add_fd_rwx. (cherry picked from commit 78974fce896068b099cfc99ac7be27d5fe3bb075) --- unix/uxpoll.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/unix/uxpoll.c b/unix/uxpoll.c index da74ebf9..474926bb 100644 --- a/unix/uxpoll.c +++ b/unix/uxpoll.c @@ -126,29 +126,44 @@ int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds) return poll(pw->fds, pw->nfd, milliseconds); } -int pollwrap_get_fd_events(pollwrapper *pw, int fd) +static void pollwrap_get_fd_events_revents(pollwrapper *pw, int fd, + int *events_p, int *revents_p) { pollwrap_fdtopos *f2p, f2p_find; + int events = 0, revents = 0; assert(fd >= 0); f2p_find.fd = fd; f2p = find234(pw->fdtopos, &f2p_find, NULL); - if (!f2p) - return 0; + if (f2p) { + events = pw->fds[f2p->pos].events; + revents = pw->fds[f2p->pos].revents; + } - return pw->fds[f2p->pos].revents; + if (events_p) + *events_p = events; + if (revents_p) + *revents_p = revents; +} + +int pollwrap_get_fd_events(pollwrapper *pw, int fd) +{ + int revents; + pollwrap_get_fd_events_revents(pw, fd, NULL, &revents); + return revents; } int pollwrap_get_fd_rwx(pollwrapper *pw, int fd) { - int revents = pollwrap_get_fd_events(pw, fd); + int events, revents; + pollwrap_get_fd_events_revents(pw, fd, &events, &revents); int rwx = 0; - if (revents & SELECT_R_OUT) + if ((events & POLLIN) && (revents & SELECT_R_OUT)) rwx |= SELECT_R; - if (revents & SELECT_W_OUT) + if ((events & POLLOUT) && (revents & SELECT_W_OUT)) rwx |= SELECT_W; - if (revents & SELECT_X_OUT) + if ((events & POLLPRI) && (revents & SELECT_X_OUT)) rwx |= SELECT_X; return rwx; }