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