1 /*-
2  * Copyright (c) 2006 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * Attempts to exercise UNIX domain socket races relating to the non-atomic
29  * connect-and-send properties of sendto().  As the result of such a race is
30  * a kernel panic, this test simply completes or doesn't.
31  *
32  * XXX: Despite implementing support for sendto() on stream sockets with
33  * implied connect, the appropriate flag isn't set in the FreeBSD kernel so
34  * it does not work.  For now, don't call the stream test.
35  */
36 
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 
41 #include <err.h>
42 #include <signal.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #define	ITERATIONS	1000000
47 
48 static char socket_path[] = "tmp.XXXXXX";
49 
50 static void
51 stream_server(int listenfd)
52 {
53 	int acceptfd;
54 
55 	while (1) {
56 		acceptfd = accept(listenfd, NULL, NULL);
57 		if (acceptfd < 0) {
58 			warn("stream_server: accept");
59 			continue;
60 		}
61 		sleep(1);
62 		close(acceptfd);
63 	}
64 }
65 
66 static void
67 stream_client(void)
68 {
69 	struct sockaddr_un sun;
70 	ssize_t len;
71 	char c = 0;
72 	int fd, i;
73 
74 	bzero(&sun, sizeof(sun));
75 	sun.sun_len = sizeof(sun);
76 	sun.sun_family = AF_UNIX;
77 	strcpy(sun.sun_path, socket_path);
78 	for (i = 0; i < ITERATIONS; i++) {
79 		fd = socket(PF_UNIX, SOCK_STREAM, 0);
80 		if (fd < 0) {
81 			warn("stream_client: socket");
82 			return;
83 		}
84 		len = sendto(fd, &c, sizeof(c), 0, (struct sockaddr *)&sun,
85 		    sizeof(sun));
86 		if (len < 0)
87 			warn("stream_client: sendto");
88 		close(fd);
89 	}
90 }
91 
92 static void
93 stream_test(void)
94 {
95 	struct sockaddr_un sun;
96 	pid_t childpid;
97 	int listenfd;
98 
99 	listenfd = socket(PF_UNIX, SOCK_STREAM, 0);
100 	if (listenfd < 0)
101 		err(-1, "stream_test: socket");
102 
103 	bzero(&sun, sizeof(sun));
104 	sun.sun_len = sizeof(sun);
105 	sun.sun_family = AF_UNIX;
106 	strcpy(sun.sun_path, socket_path);
107 
108 	if (bind(listenfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
109 		err(-1, "stream_test: bind");
110 
111 	if (listen(listenfd, -1) < 0)
112 		err(-1, "stream_test: listen");
113 
114 	childpid = fork();
115 	if (childpid < 0)
116 		err(-1, "stream_test: fork");
117 
118 	if (childpid != 0) {
119 		sleep(1);
120 		stream_client();
121 		kill(childpid, SIGTERM);
122 		sleep(1);
123 	} else
124 		stream_server(listenfd);
125 
126 	(void)unlink(socket_path);
127 }
128 
129 static void
130 datagram_server(int serverfd)
131 {
132 	ssize_t len;
133 	char c;
134 
135 	while (1) {
136 		len = recv(serverfd, &c, sizeof(c), 0);
137 		if (len < 0)
138 			warn("datagram_server: recv");
139 	}
140 }
141 
142 static void
143 datagram_client(void)
144 {
145 	struct sockaddr_un sun;
146 	ssize_t len;
147 	char c = 0;
148 	int fd, i;
149 
150 	bzero(&sun, sizeof(sun));
151 	sun.sun_len = sizeof(sun);
152 	sun.sun_family = AF_UNIX;
153 	strcpy(sun.sun_path, socket_path);
154 	for (i = 0; i < ITERATIONS; i++) {
155 		fd = socket(PF_UNIX, SOCK_DGRAM, 0);
156 		if (fd < 0) {
157 			warn("datagram_client: socket");
158 			return;
159 		}
160 		len = sendto(fd, &c, sizeof(c), 0, (struct sockaddr *)&sun,
161 		    sizeof(sun));
162 		if (len < 0)
163 			warn("datagram_client: sendto");
164 		close(fd);
165 	}
166 }
167 
168 static void
169 datagram_test(void)
170 {
171 	struct sockaddr_un sun;
172 	pid_t childpid;
173 	int serverfd;
174 
175 	serverfd = socket(PF_UNIX, SOCK_DGRAM, 0);
176 	if (serverfd < 0)
177 		err(-1, "datagram_test: socket");
178 
179 	bzero(&sun, sizeof(sun));
180 	sun.sun_len = sizeof(sun);
181 	sun.sun_family = AF_UNIX;
182 	strcpy(sun.sun_path, socket_path);
183 
184 	if (bind(serverfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
185 		err(-1, "datagram_test: bind");
186 
187 	childpid = fork();
188 	if (childpid < 0)
189 		err(-1, "datagram_test: fork");
190 
191 	if (childpid != 0) {
192 		sleep(1);
193 		datagram_client();
194 		kill(childpid, SIGTERM);
195 		sleep(1);
196 	} else
197 		datagram_server(serverfd);
198 
199 	(void)unlink(socket_path);
200 }
201 
202 int
203 main(void)
204 {
205 
206 	if (mkstemp(socket_path) == -1)
207 		err(1, "mkstemp failed");
208 	(void)unlink(socket_path);
209 	datagram_test();
210 	if (0)
211 		stream_test();
212 	return (0);
213 }
214