From 63649db04205480b160d38124000f43cbbb16b15 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Fri, 14 Apr 2017 17:23:28 +0000 Subject: [PATCH] Restore ability to shutdown DGRAM sockets, still forcing ENOTCONN to be returned by the shutdown(2) system call. This ability has been lost as part of the svn revision 285910. Reviewed by: ed, rwatson, glebius, hiren MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D10351 --- sys/kern/uipc_socket.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index c34e917b6bcb..4ff301a9540f 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -2343,13 +2343,27 @@ int soshutdown(struct socket *so, int how) { struct protosw *pr = so->so_proto; - int error; + int error, soerror_enotconn; if (!(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR)) return (EINVAL); + + soerror_enotconn = 0; if ((so->so_state & - (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) - return (ENOTCONN); + (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) { + /* + * POSIX mandates us to return ENOTCONN when shutdown(2) is + * invoked on a datagram sockets, however historically we would + * actually tear socket down. This is known to be leveraged by + * some applications to unblock process waiting in recvXXX(2) + * by other process that it shares that socket with. Try to meet + * both backward-compatibility and POSIX requirements by forcing + * ENOTCONN but still asking protocol to perform pru_shutdown(). + */ + if (so->so_type != SOCK_DGRAM) + return (ENOTCONN); + soerror_enotconn = 1; + } CURVNET_SET(so->so_vnet); if (pr->pr_usrreqs->pru_flush != NULL) @@ -2360,11 +2374,12 @@ soshutdown(struct socket *so, int how) error = (*pr->pr_usrreqs->pru_shutdown)(so); wakeup(&so->so_timeo); CURVNET_RESTORE(); - return (error); + return ((error == 0 && soerror_enotconn) ? ENOTCONN : error); } wakeup(&so->so_timeo); CURVNET_RESTORE(); - return (0); + + return (soerror_enotconn ? ENOTCONN : 0); } void