Path: blob/main/tools/regression/sockets/unix_close_race/unix_close_race.c
48254 views
/*-1* Copyright (c) 2010 Mikolaj Golub2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526/*27* This regression test attempts to trigger a race that occurs when both28* endpoints of a connected UNIX domain socket are closed at once. The two29* close paths may run concurrently leading to a call to sodisconnect() on an30* already-closed socket in kernel. Before it was fixed, this might lead to31* ENOTCONN being returned improperly from close().32*33* This race is fairly timing-dependent, so it effectively requires SMP, and34* may not even trigger then.35*/3637#include <sys/types.h>38#include <sys/select.h>39#include <sys/socket.h>40#include <sys/sysctl.h>41#include <sys/un.h>42#include <netinet/in.h>43#include <arpa/inet.h>44#include <errno.h>45#include <fcntl.h>46#include <signal.h>47#include <stdlib.h>48#include <stdio.h>49#include <strings.h>50#include <string.h>51#include <unistd.h>52#include <err.h>5354static char socket_path[] = "tmp.XXXXXXXX";5556#define USLEEP 10057#define LOOPS 1000005859int60main(void)61{62struct sockaddr_un servaddr;63int listenfd, connfd, pid;64u_int counter, ncpus;65size_t len;6667len = sizeof(ncpus);68if (sysctlbyname("kern.smp.cpus", &ncpus, &len, NULL, 0) < 0)69err(1, "kern.smp.cpus");70if (len != sizeof(ncpus))71errx(1, "kern.smp.cpus: invalid length");72if (ncpus < 2)73warnx("SMP not present, test may be unable to trigger race");7475if (mkstemp(socket_path) == -1)76err(1, "mkstemp failed");77unlink(socket_path);7879/*80* Create a UNIX domain socket that the child will repeatedly81* accept() from, and that the parent will repeatedly connect() to.82*/83if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)84err(1, "parent: socket error");85(void)unlink(socket_path);86bzero(&servaddr, sizeof(servaddr));87servaddr.sun_family = AF_LOCAL;88strcpy(servaddr.sun_path, socket_path);89if (bind(listenfd, (struct sockaddr *) &servaddr,90sizeof(servaddr)) < 0)91err(1, "parent: bind error");92if (listen(listenfd, 1024) < 0)93err(1, "parent: listen error");9495pid = fork();96if (pid == -1)97err(1, "fork()");98if (pid != 0) {99/*100* In the parent, repeatedly connect and disconnect from the101* socket, attempting to induce the race.102*/103close(listenfd);104sleep(1);105bzero(&servaddr, sizeof(servaddr));106servaddr.sun_family = AF_LOCAL;107strcpy(servaddr.sun_path, socket_path);108for (counter = 0; counter < LOOPS; counter++) {109if ((connfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {110(void)kill(pid, SIGTERM);111err(1, "parent: socket error");112}113if (connect(connfd, (struct sockaddr *)&servaddr,114sizeof(servaddr)) < 0) {115(void)kill(pid, SIGTERM);116err(1, "parent: connect error");117}118if (close(connfd) < 0) {119(void)kill(pid, SIGTERM);120err(1, "parent: close error");121}122usleep(USLEEP);123}124(void)kill(pid, SIGTERM);125} else {126/*127* In the child, loop accepting and closing. We may pick up128* the race here so report errors from close().129*/130for ( ; ; ) {131if ((connfd = accept(listenfd,132(struct sockaddr *)NULL, NULL)) < 0)133err(1, "child: accept error");134if (close(connfd) < 0)135err(1, "child: close error");136}137}138printf("OK\n");139exit(0);140}141142143