In PR 227259, a user is reporting that they have code which is using

shutdown() to wakeup another thread blocked on a stream listen socket.
This code is failing, while it used to work on FreeBSD 10 and still
works on Linux.

It seems reasonable to add another exception to support something users are
actually doing, which used to work on FreeBSD 10, and still works on Linux.
And, it seems like it should be acceptable to POSIX, as we still return
ENOTCONN.

This patch is different to what had been committed to stable/11, since
code around listening sockets is different. Patch in D15019 is written
by jtl@, slightly modified by me.

PR:		227259
Obtained from:	jtl
Approved by:	re (kib)
Differential Revision:  D15019
This commit is contained in:
glebius 2018-10-03 17:40:04 +00:00
parent 9dd86cea33
commit 968d142094

View File

@ -917,12 +917,13 @@ solisten_dequeue(struct socket *head, struct socket **ret, int flags)
if (head->so_error) { if (head->so_error) {
error = head->so_error; error = head->so_error;
head->so_error = 0; head->so_error = 0;
} else if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->sol_comp))
error = EWOULDBLOCK;
else
error = 0;
if (error) {
SOLISTEN_UNLOCK(head); SOLISTEN_UNLOCK(head);
return (error); return (error);
}
if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->sol_comp)) {
SOLISTEN_UNLOCK(head);
return (EWOULDBLOCK);
} }
so = TAILQ_FIRST(&head->sol_comp); so = TAILQ_FIRST(&head->sol_comp);
SOCK_LOCK(so); SOCK_LOCK(so);
@ -2585,11 +2586,20 @@ soshutdown(struct socket *so, int how)
* both backward-compatibility and POSIX requirements by forcing * both backward-compatibility and POSIX requirements by forcing
* ENOTCONN but still asking protocol to perform pru_shutdown(). * ENOTCONN but still asking protocol to perform pru_shutdown().
*/ */
if (so->so_type != SOCK_DGRAM) if (so->so_type != SOCK_DGRAM && !SOLISTENING(so))
return (ENOTCONN); return (ENOTCONN);
soerror_enotconn = 1; soerror_enotconn = 1;
} }
if (SOLISTENING(so)) {
if (how != SHUT_WR) {
SOLISTEN_LOCK(so);
so->so_error = ECONNABORTED;
solisten_wakeup(so); /* unlocks so */
}
goto done;
}
CURVNET_SET(so->so_vnet); CURVNET_SET(so->so_vnet);
if (pr->pr_usrreqs->pru_flush != NULL) if (pr->pr_usrreqs->pru_flush != NULL)
(*pr->pr_usrreqs->pru_flush)(so, how); (*pr->pr_usrreqs->pru_flush)(so, how);
@ -2604,6 +2614,7 @@ soshutdown(struct socket *so, int how)
wakeup(&so->so_timeo); wakeup(&so->so_timeo);
CURVNET_RESTORE(); CURVNET_RESTORE();
done:
return (soerror_enotconn ? ENOTCONN : 0); return (soerror_enotconn ? ENOTCONN : 0);
} }
@ -3279,6 +3290,8 @@ sopoll_generic(struct socket *so, int events, struct ucred *active_cred,
revents = 0; revents = 0;
else if (!TAILQ_EMPTY(&so->sol_comp)) else if (!TAILQ_EMPTY(&so->sol_comp))
revents = events & (POLLIN | POLLRDNORM); revents = events & (POLLIN | POLLRDNORM);
else if ((events & POLLINIGNEOF) == 0 && so->so_error)
revents = (events & (POLLIN | POLLRDNORM)) | POLLHUP;
else { else {
selrecord(td, &so->so_rdsel); selrecord(td, &so->so_rdsel);
revents = 0; revents = 0;
@ -3555,6 +3568,11 @@ filt_soread(struct knote *kn, long hint)
if (SOLISTENING(so)) { if (SOLISTENING(so)) {
SOCK_LOCK_ASSERT(so); SOCK_LOCK_ASSERT(so);
kn->kn_data = so->sol_qlen; kn->kn_data = so->sol_qlen;
if (so->so_error) {
kn->kn_flags |= EV_EOF;
kn->kn_fflags = so->so_error;
return (1);
}
return (!TAILQ_EMPTY(&so->sol_comp)); return (!TAILQ_EMPTY(&so->sol_comp));
} }