unix socket: race on close?
Robert Watson
rwatson at FreeBSD.org
Thu Feb 18 11:59:40 UTC 2010
On Thu, 18 Feb 2010, Mikolaj Golub wrote:
> Below is a simple test code with unix sockets: the client does
> connect()/close() in loop and the server -- accept()/close().
>
> Sometimes close() fails with 'Socket is not connected' error:
Hi Mikolaj:
Thanks for this report, and sorry about not spotting your earlier post to
freebsd-net. I've been fairly preoccupied the last month and not keeping up
with the mailing lists. Could I ask you to file a PR on this, and forward me
the PR number so I can claim ownership? This should prevent it from getting
lost while I catch up.
In short, your evaluation seems reasonable to me -- have you tried tweaking
soclose() to ignore ENOTCONN from sodisconnect() to confirm this diagnosis
fixes all the instances you've been seeing?
Robert N M Watson
Computer Laboratory
University of Cambridge
>
> a.out: parent: close error: 57
>
> or
>
> a.out: child: close error: 57
>
> It looks for me like some race in close(). Looking at uipc_socket.c:soclose():
>
> int
> soclose(struct socket *so)
> {
> int error = 0;
>
> KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter"));
>
> CURVNET_SET(so->so_vnet);
> funsetown(&so->so_sigio);
> if (so->so_state & SS_ISCONNECTED) {
> if ((so->so_state & SS_ISDISCONNECTING) == 0) {
> error = sodisconnect(so);
> if (error)
> goto drop;
> }
>
> Isn't the problem here? so_state is checked for SS_ISCONNECTED and
> SS_ISDISCONNECTING without locking and then sodisconnect() is called, which
> closes both sockets of the connection. So it looks for me that if the close()
> is called for both ends simultaneously it is possible that sodisconnect() will
> be called for both ends and for one ENOTCONN will be returned. Or may I have
> missed something?
>
> We have been observing periodically ENOTCONN errors on unix socket close in
> our applications, so it is not just curiosity :-) (I posted about our problem
> to freebsd-net@ some time ago but then did not attract any attention
> http://lists.freebsd.org/pipermail/freebsd-net/2009-December/024047.html).
>
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <sys/un.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <stdio.h>
> #include <strings.h>
> #include <string.h>
> #include <unistd.h>
> #include <sys/select.h>
> #include <err.h>
>
> #define UNIXSTR_PATH "/tmp/mytest.socket"
> #define USLEEP 100
>
> int main(int argc, char **argv)
> {
> int listenfd, connfd, pid;
> struct sockaddr_un servaddr;
>
> pid = fork();
> if (-1 == pid)
> errx(1, "fork(): %d", errno);
>
> if (0 != pid) { /* parent */
>
> if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
> errx(1, "parent: socket error: %d", errno);
>
> unlink(UNIXSTR_PATH);
> bzero(&servaddr, sizeof(servaddr));
> servaddr.sun_family = AF_LOCAL;
> strcpy(servaddr.sun_path, UNIXSTR_PATH);
>
> if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
> errx(1, "parent: bind error: %d", errno);
>
> if (listen(listenfd, 1024) < 0)
> errx(1, "parent: listen error: %d", errno);
>
> for ( ; ; ) {
> if ((connfd = accept(listenfd, (struct sockaddr *) NULL, NULL)) < 0)
> errx(1, "parent: accept error: %d", errno);
>
> //usleep(USLEEP / 2); // (I) uncomment this or (II) below to avoid the race
>
> if (close(connfd) < 0)
> errx(1, "parent: close error: %d", errno);
> }
>
> } else { /* child */
>
> sleep(1); /* give the parent some time to create the socket */
>
> for ( ; ; ) {
>
> if ((connfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
> errx(1, "child: socket error: %d", errno);
>
> bzero(&servaddr, sizeof(servaddr));
> servaddr.sun_family = AF_LOCAL;
> strcpy(servaddr.sun_path, UNIXSTR_PATH);
>
> if (connect(connfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
> errx(1, "child: connect error %d", errno);
>
> // usleep(USLEEP); // (II) uncomment this or (I) above to avoid the race
>
> if (close(connfd) != 0)
> errx(1, "child: close error: %d", errno);
>
> usleep(USLEEP);
> }
> }
>
> return 0;
> }
>
> --
> Mikolaj Golub
> _______________________________________________
> freebsd-hackers at freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
> To unsubscribe, send any mail to "freebsd-hackers-unsubscribe at freebsd.org"
>
More information about the freebsd-hackers
mailing list