kern/78478: Writing to closed socket does not generate SIGPIPE

Mikko Työläjärvi mbsd at pacbell.net
Sun Mar 6 02:50:18 GMT 2005


>Number:         78478
>Category:       kern
>Synopsis:       writing to closed socket does not generate SIGPIPE
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Mar 06 02:50:17 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Mikko Tyolajarvi
>Release:        FreeBSD 6.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD sotec.home 6.0-CURRENT FreeBSD 6.0-CURRENT #28: Sat Mar 5 16:58:15 PST 2005 mikko at sotec.home:/usr/obj/usr/src/sys/SOTEC i386

5.3-RELEASE, 5-STABLE, 6-CURRENT all behave the same.

>Description:

Using write(2) to write to a connected socket, IP or unix domain,
where the other end has closed the connection should result in a
SIGPIPE signal being generated, and does so on FreeBSD 4.11 (as well
as Solaris 2.6, 8 & 10, HPUX 11.00 & 11.22, Linux 2.4 & 2.6 etc).

Using send(2) instead of write(2) does produce the signal, as does
write(2) to a pipe.  It is only the combination of write(2) and
a stream socket that does not work.


>How-To-Repeat:


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void
gotpipe(int sig)
{
     printf("SIGPIPE\n");
     exit(0);
}

int
main(int argc, char *argv[])
{
     char data[32 * 1024];
     int sock, lsock, asock;
     socklen_t len;
     int i, use_send, set_nosigpipe;
     struct sockaddr_in ia;

     use_send = (argc > 1 && strchr(argv[1], 's'));
     set_nosigpipe = (argc > 1 && strchr(argv[1], 'n'));

     signal(SIGPIPE, gotpipe);

     lsock = socket(AF_INET, SOCK_STREAM, 0);
     memset(&ia, 0, sizeof(ia));
     ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
     bind(lsock, (struct sockaddr *)&ia, sizeof(ia));
     listen(lsock, 5);
     len = sizeof(ia);
     if (getsockname(lsock, (struct sockaddr *)&ia, &len) < 0) {
 	perror("getsockname");
 	return 1;
     }

     sock = socket(AF_INET, SOCK_STREAM, 0);
     if (connect(sock, (struct sockaddr *)&ia, sizeof(ia)) < 0) {
 	perror("connect");
 	return 1;
     }
     asock = accept(lsock, NULL, NULL);
     close(lsock);
     close(asock);

#ifdef SO_NOSIGPIPE
     if (set_nosigpipe) {
 	i = 1;
 	if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) {
 	    perror("setsockopt");
 	}
     }
     len = sizeof(i);
     if (getsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &i, &len) < 0) {
 	perror("getsockopt");
 	return 1;
     }
     printf("SO_NOSIGPIPE is %d\n", i);
#else
     printf("SO_NOSIGPIPE not supported\n");
#endif

     for (i = 0; i < 4; i++) {
 	if (use_send) {
 	    if (send(sock, data, sizeof(data), 0) < 0) {
 		perror("send");
 	    }
 	} else {
 	    if (write(sock, data, sizeof(data)) < 0) {
 		perror("write");
 	    }
 	}
     }

     printf("No sigpipe\n");

     return 1;
}


>Fix:

This seems to be one way to solve the problem:

Index: sys_socket.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/sys_socket.c,v
retrieving revision 1.67
diff -b -u -r1.67 sys_socket.c
--- sys_socket.c	6 Jan 2005 23:35:39 -0000	1.67
+++ sys_socket.c	6 Mar 2005 02:44:58 -0000
@@ -39,8 +39,11 @@
  #include <sys/file.h>
  #include <sys/filedesc.h>
  #include <sys/mac.h>
+#include <sys/proc.h>
  #include <sys/protosw.h>
  #include <sys/sigio.h>
+#include <sys/signal.h>
+#include <sys/signalvar.h>
  #include <sys/socket.h>
  #include <sys/socketvar.h>
  #include <sys/filio.h>			/* XXX */
@@ -115,6 +118,13 @@
  	error = so->so_proto->pr_usrreqs->pru_sosend(so, 0, uio, 0, 0, 0,
  						    uio->uio_td);
  	NET_UNLOCK_GIANT();
+
+	/* Generation of SIGPIPE can be controlled per socket */
+	if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE)) {
+		PROC_LOCK(td->td_proc);
+		psignal(td->td_proc, SIGPIPE);
+		PROC_UNLOCK(td->td_proc);
+	}
  	return (error);
  }
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list