unix_close_race.c revision 281400
1208562Srwatson/*- 2208562Srwatson * Copyright (c) 2010 Mikolaj Golub 3208562Srwatson * All rights reserved. 4208562Srwatson * 5208562Srwatson * Redistribution and use in source and binary forms, with or without 6208562Srwatson * modification, are permitted provided that the following conditions 7208562Srwatson * are met: 8208562Srwatson * 1. Redistributions of source code must retain the above copyright 9208562Srwatson * notice, this list of conditions and the following disclaimer. 10208562Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11208562Srwatson * notice, this list of conditions and the following disclaimer in the 12208562Srwatson * documentation and/or other materials provided with the distribution. 13208562Srwatson * 14208562Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15208562Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16208562Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17208562Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18208562Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19208562Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20208562Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21208562Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22208562Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23208562Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24208562Srwatson * SUCH DAMAGE. 25208562Srwatson * 26208562Srwatson * $FreeBSD: head/tools/regression/sockets/unix_close_race/unix_close_race.c 281400 2015-04-11 03:43:43Z ngie $ 27208562Srwatson */ 28208562Srwatson 29208562Srwatson/* 30208562Srwatson * This regression test attempts to trigger a race that occurs when both 31208562Srwatson * endpoints of a connected UNIX domain socket are closed at once. The two 32208562Srwatson * close paths may run concurrently leading to a call to sodisconnect() on an 33208562Srwatson * already-closed socket in kernel. Before it was fixed, this might lead to 34208562Srwatson * ENOTCONN being returned improperly from close(). 35208562Srwatson * 36208562Srwatson * This race is fairly timing-dependent, so it effectively requires SMP, and 37208562Srwatson * may not even trigger then. 38208562Srwatson */ 39208562Srwatson 40208562Srwatson#include <sys/types.h> 41208562Srwatson#include <sys/select.h> 42208562Srwatson#include <sys/socket.h> 43208562Srwatson#include <sys/sysctl.h> 44208562Srwatson#include <sys/un.h> 45208562Srwatson#include <netinet/in.h> 46208562Srwatson#include <arpa/inet.h> 47208562Srwatson#include <errno.h> 48208562Srwatson#include <fcntl.h> 49208562Srwatson#include <signal.h> 50208562Srwatson#include <stdlib.h> 51208562Srwatson#include <stdio.h> 52208562Srwatson#include <strings.h> 53208562Srwatson#include <string.h> 54208562Srwatson#include <unistd.h> 55208562Srwatson#include <err.h> 56208562Srwatson 57281400Sngiestatic char socket_path[] = "tmp.XXXXXXXX"; 58281400Sngie 59208562Srwatson#define USLEEP 100 60208562Srwatson#define LOOPS 100000 61208562Srwatson 62208562Srwatsonint 63281400Sngiemain(void) 64208562Srwatson{ 65208562Srwatson struct sockaddr_un servaddr; 66208562Srwatson int listenfd, connfd, pid; 67208562Srwatson u_int counter, ncpus; 68208562Srwatson size_t len; 69208562Srwatson 70208562Srwatson len = sizeof(ncpus); 71208562Srwatson if (sysctlbyname("kern.smp.cpus", &ncpus, &len, NULL, 0) < 0) 72208562Srwatson err(1, "kern.smp.cpus"); 73208562Srwatson if (len != sizeof(ncpus)) 74208562Srwatson errx(1, "kern.smp.cpus: invalid length"); 75208562Srwatson if (ncpus < 2) 76208562Srwatson warnx("SMP not present, test may be unable to trigger race"); 77208562Srwatson 78281400Sngie if (mkstemp(socket_path) == -1) 79281400Sngie err(1, "mkstemp failed"); 80281400Sngie unlink(socket_path); 81281400Sngie 82208562Srwatson /* 83208602Srwatson * Create a UNIX domain socket that the child will repeatedly 84208602Srwatson * accept() from, and that the parent will repeatedly connect() to. 85208562Srwatson */ 86208562Srwatson if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) 87208562Srwatson err(1, "parent: socket error"); 88281400Sngie (void)unlink(socket_path); 89208562Srwatson bzero(&servaddr, sizeof(servaddr)); 90208562Srwatson servaddr.sun_family = AF_LOCAL; 91281400Sngie strcpy(servaddr.sun_path, socket_path); 92208562Srwatson if (bind(listenfd, (struct sockaddr *) &servaddr, 93208562Srwatson sizeof(servaddr)) < 0) 94208562Srwatson err(1, "parent: bind error"); 95208562Srwatson if (listen(listenfd, 1024) < 0) 96208562Srwatson err(1, "parent: listen error"); 97208562Srwatson 98208562Srwatson pid = fork(); 99208562Srwatson if (pid == -1) 100208562Srwatson err(1, "fork()"); 101208562Srwatson if (pid != 0) { 102208562Srwatson /* 103208562Srwatson * In the parent, repeatedly connect and disconnect from the 104208562Srwatson * socket, attempting to induce the race. 105208562Srwatson */ 106208562Srwatson close(listenfd); 107208562Srwatson sleep(1); 108208562Srwatson bzero(&servaddr, sizeof(servaddr)); 109208562Srwatson servaddr.sun_family = AF_LOCAL; 110281400Sngie strcpy(servaddr.sun_path, socket_path); 111208562Srwatson for (counter = 0; counter < LOOPS; counter++) { 112208602Srwatson if ((connfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { 113208602Srwatson (void)kill(pid, SIGTERM); 114208602Srwatson err(1, "parent: socket error"); 115208602Srwatson } 116208562Srwatson if (connect(connfd, (struct sockaddr *)&servaddr, 117208602Srwatson sizeof(servaddr)) < 0) { 118208602Srwatson (void)kill(pid, SIGTERM); 119208602Srwatson err(1, "parent: connect error"); 120208602Srwatson } 121208602Srwatson if (close(connfd) < 0) { 122208602Srwatson (void)kill(pid, SIGTERM); 123208602Srwatson err(1, "parent: close error"); 124208602Srwatson } 125208562Srwatson usleep(USLEEP); 126208562Srwatson } 127208562Srwatson (void)kill(pid, SIGTERM); 128208562Srwatson } else { 129208562Srwatson /* 130208562Srwatson * In the child, loop accepting and closing. We may pick up 131208562Srwatson * the race here so report errors from close(). 132208562Srwatson */ 133208562Srwatson for ( ; ; ) { 134208562Srwatson if ((connfd = accept(listenfd, 135208562Srwatson (struct sockaddr *)NULL, NULL)) < 0) 136208602Srwatson err(1, "child: accept error"); 137208562Srwatson if (close(connfd) < 0) 138208602Srwatson err(1, "child: close error"); 139208562Srwatson } 140208562Srwatson } 141208562Srwatson printf("OK\n"); 142208562Srwatson exit(0); 143208562Srwatson} 144