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$ 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 57208562Srwatson#define UNIXSTR_PATH "/tmp/mytest.socket" 58208562Srwatson#define USLEEP 100 59208562Srwatson#define LOOPS 100000 60208562Srwatson 61208562Srwatsonint 62208562Srwatsonmain(int argc, char **argv) 63208562Srwatson{ 64208562Srwatson struct sockaddr_un servaddr; 65208562Srwatson int listenfd, connfd, pid; 66208562Srwatson u_int counter, ncpus; 67208562Srwatson size_t len; 68208562Srwatson 69208562Srwatson len = sizeof(ncpus); 70208562Srwatson if (sysctlbyname("kern.smp.cpus", &ncpus, &len, NULL, 0) < 0) 71208562Srwatson err(1, "kern.smp.cpus"); 72208562Srwatson if (len != sizeof(ncpus)) 73208562Srwatson errx(1, "kern.smp.cpus: invalid length"); 74208562Srwatson if (ncpus < 2) 75208562Srwatson warnx("SMP not present, test may be unable to trigger race"); 76208562Srwatson 77208562Srwatson /* 78208602Srwatson * Create a UNIX domain socket that the child will repeatedly 79208602Srwatson * accept() from, and that the parent will repeatedly connect() to. 80208562Srwatson */ 81208562Srwatson if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) 82208562Srwatson err(1, "parent: socket error"); 83208562Srwatson (void)unlink(UNIXSTR_PATH); 84208562Srwatson bzero(&servaddr, sizeof(servaddr)); 85208562Srwatson servaddr.sun_family = AF_LOCAL; 86208562Srwatson strcpy(servaddr.sun_path, UNIXSTR_PATH); 87208562Srwatson if (bind(listenfd, (struct sockaddr *) &servaddr, 88208562Srwatson sizeof(servaddr)) < 0) 89208562Srwatson err(1, "parent: bind error"); 90208562Srwatson if (listen(listenfd, 1024) < 0) 91208562Srwatson err(1, "parent: listen error"); 92208562Srwatson 93208562Srwatson pid = fork(); 94208562Srwatson if (pid == -1) 95208562Srwatson err(1, "fork()"); 96208562Srwatson if (pid != 0) { 97208562Srwatson /* 98208562Srwatson * In the parent, repeatedly connect and disconnect from the 99208562Srwatson * socket, attempting to induce the race. 100208562Srwatson */ 101208562Srwatson close(listenfd); 102208562Srwatson sleep(1); 103208562Srwatson bzero(&servaddr, sizeof(servaddr)); 104208562Srwatson servaddr.sun_family = AF_LOCAL; 105208562Srwatson strcpy(servaddr.sun_path, UNIXSTR_PATH); 106208562Srwatson for (counter = 0; counter < LOOPS; counter++) { 107208602Srwatson if ((connfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { 108208602Srwatson (void)kill(pid, SIGTERM); 109208602Srwatson err(1, "parent: socket error"); 110208602Srwatson } 111208562Srwatson if (connect(connfd, (struct sockaddr *)&servaddr, 112208602Srwatson sizeof(servaddr)) < 0) { 113208602Srwatson (void)kill(pid, SIGTERM); 114208602Srwatson err(1, "parent: connect error"); 115208602Srwatson } 116208602Srwatson if (close(connfd) < 0) { 117208602Srwatson (void)kill(pid, SIGTERM); 118208602Srwatson err(1, "parent: close error"); 119208602Srwatson } 120208562Srwatson usleep(USLEEP); 121208562Srwatson } 122208562Srwatson (void)kill(pid, SIGTERM); 123208562Srwatson } else { 124208562Srwatson /* 125208562Srwatson * In the child, loop accepting and closing. We may pick up 126208562Srwatson * the race here so report errors from close(). 127208562Srwatson */ 128208562Srwatson for ( ; ; ) { 129208562Srwatson if ((connfd = accept(listenfd, 130208562Srwatson (struct sockaddr *)NULL, NULL)) < 0) 131208602Srwatson err(1, "child: accept error"); 132208562Srwatson if (close(connfd) < 0) 133208602Srwatson err(1, "child: close error"); 134208562Srwatson } 135208562Srwatson } 136208562Srwatson printf("OK\n"); 137208562Srwatson exit(0); 138208562Srwatson} 139