2 * Copyright (c) 2007 Robert N. M. Watson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
30 * A few regression tests for UNIX domain sockets. Run from single-user mode
31 * as it checks the openfiles sysctl to look for leaks, and we don't want that
32 * changing due to other processes doing stuff.
35 #include <sys/types.h>
36 #include <sys/signal.h>
37 #include <sys/socket.h>
38 #include <sys/sysctl.h>
42 #include <netinet/in.h>
53 static int forcegc = 1;
54 static char dpath[PATH_MAX];
55 static const char *test;
64 if (sysctlbyname("kern.openfiles", &i, &len, NULL, 0) < 0)
65 err(-1, "kern.openfiles");
76 if (sysctlbyname("net.local.inflight", &i, &len, NULL, 0) < 0)
77 err(-1, "net.local.inflight");
82 sendfd(int fd, int fdtosend)
85 struct message { struct cmsghdr msg_hdr; int fd; } m;
87 int after_inflight, before_inflight;
89 before_inflight = getinflight();
91 bzero(&mh, sizeof(mh));
94 mh.msg_controllen = sizeof(m);
95 m.msg_hdr.cmsg_len = sizeof(m);
96 m.msg_hdr.cmsg_level = SOL_SOCKET;
97 m.msg_hdr.cmsg_type = SCM_RIGHTS;
99 len = sendmsg(fd, &mh, 0);
101 err(-1, "%s: sendmsg", test);
102 after_inflight = getinflight();
103 if (after_inflight != before_inflight + 1)
104 errx(-1, "%s: sendfd: before %d after %d\n", test,
105 before_inflight, after_inflight);
109 close2(int fd1, int fd2)
117 close3(int fd1, int fd2, int fd3)
125 close4(int fd1, int fd2, int fd3, int fd4)
133 close5(int fd1, int fd2, int fd3, int fd4, int fd5)
136 close3(fd1, fd2, fd3);
141 my_socket(int domain, int type, int proto)
145 sock = socket(domain, type, proto);
147 err(-1, "%s: socket", test);
152 my_bind(int sock, struct sockaddr *sa, socklen_t len)
155 if (bind(sock, sa, len) < 0)
156 err(-1, "%s: bind", test);
160 my_connect(int sock, struct sockaddr *sa, socklen_t len)
163 if (connect(sock, sa, len) < 0)
164 err(-1, "%s: connect", test);
168 my_listen(int sock, int backlog)
171 if (listen(sock, backlog) < 0)
172 err(-1, "%s: listen", test);
176 my_socketpair(int *sv)
179 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
180 err(-1, "%s: socketpair", test);
184 my_getsockname(int s, struct sockaddr *sa, socklen_t *salen)
187 if (getsockname(s, sa, salen) < 0)
188 err(-1, "%s: getsockname", test);
195 if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
196 err(-1, "%s: fcntl(F_SETFL, O_NONBLOCK)", test);
200 alloc3fds(int *s, int *sv)
203 if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
204 err(-1, "%s: socket", test);
205 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
206 err(-1, "%s: socketpair", test);
210 alloc5fds(int *s, int *sva, int *svb)
213 if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
214 err(-1, "%s: socket", test);
215 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sva) < 0)
216 err(-1, "%s: socketpair", test);
217 if (socketpair(PF_UNIX, SOCK_STREAM, 0, svb) < 0)
218 err(-1, "%s: socketpair", test);
222 save_sysctls(int *before_inflight, int *before_openfiles)
225 *before_inflight = getinflight();
226 *before_openfiles = getopenfiles();
230 * Try hard to make sure that the GC does in fact run before we test the
231 * condition of things.
239 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
240 err(-1, "trigger_gc: socket");
247 test_sysctls(int before_inflight, int before_openfiles)
249 int after_inflight, after_openfiles;
252 after_inflight = getinflight();
253 if (after_inflight != before_inflight)
254 warnx("%s: before inflight: %d, after inflight: %d",
255 test, before_inflight, after_inflight);
257 after_openfiles = getopenfiles();
258 if (after_openfiles != before_openfiles)
259 warnx("%s: before: %d, after: %d", test, before_openfiles,
264 twosome_nothing(void)
266 int inflight, openfiles;
270 * Create a pair, close in one order.
272 test = "twosome_nothing1";
273 printf("%s\n", test);
274 save_sysctls(&inflight, &openfiles);
276 close2(sv[0], sv[1]);
277 test_sysctls(inflight, openfiles);
280 * Create a pair, close in the other order.
282 test = "twosome_nothing2";
283 printf("%s\n", test);
284 save_sysctls(&inflight, &openfiles);
286 close2(sv[0], sv[1]);
287 test_sysctls(inflight, openfiles);
291 * Using a socket pair, send various endpoints over the pair and close in
295 twosome_drop_work(const char *testname, int sendvia, int tosend, int closefirst)
297 int inflight, openfiles;
300 printf("%s\n", testname);
302 save_sysctls(&inflight, &openfiles);
304 sendfd(sv[sendvia], sv[tosend]);
306 close2(sv[0], sv[1]);
308 close2(sv[1], sv[0]);
309 test_sysctls(inflight, openfiles);
317 * In various combations, some wastefully symmetric, create socket
318 * pairs and send one or another endpoint over one or another
319 * endpoint, closing the endpoints in various orders.
321 twosome_drop_work("twosome_drop1", 0, 0, 0);
322 twosome_drop_work("twosome_drop2", 0, 0, 1);
323 twosome_drop_work("twosome_drop3", 0, 1, 0);
324 twosome_drop_work("twosome_drop4", 0, 1, 1);
325 twosome_drop_work("twosome_drop5", 1, 0, 0);
326 twosome_drop_work("twosome_drop6", 1, 0, 1);
327 twosome_drop_work("twosome_drop7", 1, 1, 0);
328 twosome_drop_work("twosome_drop8", 1, 1, 1);
332 threesome_nothing(void)
334 int inflight, openfiles;
337 test = "threesome_nothing";
338 printf("%s\n", test);
339 save_sysctls(&inflight, &openfiles);
341 close3(s, sv[0], sv[1]);
342 test_sysctls(inflight, openfiles);
346 * threesome_drop: create a pair and a spare, send the spare over the pair, and
347 * close in various orders and make sure all the fds went away.
352 int inflight, openfiles;
356 * threesome_drop1: close sent send receive
358 test = "threesome_drop1";
359 printf("%s\n", test);
360 save_sysctls(&inflight, &openfiles);
363 close3(s, sv[0], sv[1]);
364 test_sysctls(inflight, openfiles);
367 * threesome_drop2: close sent receive send
369 test = "threesome_drop2";
370 printf("%s\n", test);
371 save_sysctls(&inflight, &openfiles);
374 close3(s, sv[1], sv[0]);
375 test_sysctls(inflight, openfiles);
378 * threesome_drop3: close receive sent send
380 test = "threesome_drop3";
381 printf("%s\n", test);
382 save_sysctls(&inflight, &openfiles);
385 close3(sv[1], s, sv[0]);
386 test_sysctls(inflight, openfiles);
389 * threesome_drop4: close receive send sent
391 test = "threesome_drop4";
392 printf("%s\n", test);
393 save_sysctls(&inflight, &openfiles);
396 close3(sv[1], sv[0], s);
397 test_sysctls(inflight, openfiles);
400 * threesome_drop5: close send receive sent
402 test = "threesome_drop5";
403 printf("%s\n", test);
404 save_sysctls(&inflight, &openfiles);
407 close3(sv[0], sv[1], s);
408 test_sysctls(inflight, openfiles);
411 * threesome_drop6: close send sent receive
413 test = "threesome_drop6";
414 printf("%s\n", test);
415 save_sysctls(&inflight, &openfiles);
417 close3(sv[0], s, sv[1]);
418 test_sysctls(inflight, openfiles);
422 * Fivesome tests: create two socket pairs and a spare, send the spare over
423 * the first socket pair, then send the first socket pair over the second
424 * socket pair, and GC. Do various closes at various points to exercise
428 fivesome_nothing(void)
430 int inflight, openfiles;
431 int spare, sva[2], svb[2];
433 test = "fivesome_nothing";
434 printf("%s\n", test);
435 save_sysctls(&inflight, &openfiles);
436 alloc5fds(&spare, sva, svb);
437 close5(spare, sva[0], sva[1], svb[0], svb[1]);
438 test_sysctls(inflight, openfiles);
442 fivesome_drop_work(const char *testname, int close_spare_after_send,
443 int close_sva_after_send)
445 int inflight, openfiles;
446 int spare, sva[2], svb[2];
448 printf("%s\n", testname);
450 save_sysctls(&inflight, &openfiles);
451 alloc5fds(&spare, sva, svb);
454 * Send spare over sva.
456 sendfd(sva[0], spare);
457 if (close_spare_after_send)
463 sendfd(svb[0], sva[0]);
464 sendfd(svb[0], sva[1]);
465 if (close_sva_after_send)
466 close2(sva[0], sva[1]);
468 close2(svb[0], svb[1]);
470 if (!close_sva_after_send)
471 close2(sva[0], sva[1]);
472 if (!close_spare_after_send)
475 test_sysctls(inflight, openfiles);
482 fivesome_drop_work("fivesome_drop1", 0, 0);
483 fivesome_drop_work("fivesome_drop2", 0, 1);
484 fivesome_drop_work("fivesome_drop3", 1, 0);
485 fivesome_drop_work("fivesome_drop4", 1, 1);
489 * Create a somewhat nasty dual-socket socket intended to upset the garbage
490 * collector if mark-and-sweep is wrong.
495 int inflight, openfiles;
496 int spare, sva[2], svb[2];
498 test = "complex_cycles";
499 printf("%s\n", test);
500 save_sysctls(&inflight, &openfiles);
501 alloc5fds(&spare, sva, svb);
502 sendfd(sva[0], svb[0]);
503 sendfd(sva[0], svb[1]);
504 sendfd(svb[0], sva[0]);
505 sendfd(svb[0], sva[1]);
506 sendfd(svb[0], spare);
507 sendfd(sva[0], spare);
508 close5(spare, sva[0], sva[1], svb[0], svb[1]);
509 test_sysctls(inflight, openfiles);
513 * Listen sockets can also be passed over UNIX domain sockets, so test
514 * various cases, including ones where listen sockets have waiting sockets
515 * hanging off them...
520 struct sockaddr_un sun;
521 struct sockaddr_in sin;
522 int inflight, openfiles;
525 test = "listen_nothing_unp";
526 printf("%s\n", test);
527 bzero(&sun, sizeof(sun));
528 sun.sun_family = AF_LOCAL;
529 sun.sun_len = sizeof(sun);
530 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
531 save_sysctls(&inflight, &openfiles);
532 s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
533 my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
536 (void)unlink(sun.sun_path);
537 test_sysctls(inflight, openfiles);
539 test = "listen_nothing_inet";
540 printf("%s\n", test);
541 bzero(&sin, sizeof(sin));
542 sin.sin_family = AF_INET;
543 sin.sin_len = sizeof(sin);
544 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
545 sin.sin_port = htons(0);
546 save_sysctls(&inflight, &openfiles);
547 s = my_socket(PF_INET, SOCK_STREAM, 0);
548 my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
551 test_sysctls(inflight, openfiles);
555 * Send a listen UDP socket over a UNIX domain socket.
557 * Send a listen TCP socket over a UNIX domain socket.
559 * Do each twice, with closing of the listen socket vs. socketpair in
565 struct sockaddr_un sun;
566 struct sockaddr_in sin;
567 int inflight, openfiles;
570 bzero(&sun, sizeof(sun));
571 sun.sun_family = AF_LOCAL;
572 sun.sun_len = sizeof(sun);
575 * Close listen socket first.
577 test = "listen_drop_unp1";
578 printf("%s\n", test);
579 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
580 save_sysctls(&inflight, &openfiles);
581 s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
582 my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
586 close3(s, sv[0], sv[1]);
587 test_sysctls(inflight, openfiles);
590 * Close socketpair first.
592 test = "listen_drop_unp2";
593 printf("%s\n", test);
594 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
595 save_sysctls(&inflight, &openfiles);
596 s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
597 my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
601 close3(sv[0], sv[1], s);
602 test_sysctls(inflight, openfiles);
604 sin.sin_family = AF_INET;
605 sin.sin_len = sizeof(sin);
606 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
607 sin.sin_port = htons(0);
610 * Close listen socket first.
612 test = "listen_drop_inet1";
613 printf("%s\n", test);
614 bzero(&sun, sizeof(sun));
615 save_sysctls(&inflight, &openfiles);
616 s = my_socket(PF_INET, SOCK_STREAM, 0);
617 my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
621 close3(s, sv[0], sv[1]);
622 test_sysctls(inflight, openfiles);
625 * Close socketpair first.
627 test = "listen_drop_inet2";
628 printf("%s\n", test);
629 bzero(&sun, sizeof(sun));
630 save_sysctls(&inflight, &openfiles);
631 s = my_socket(PF_INET, SOCK_STREAM, 0);
632 my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
636 close3(sv[0], sv[1], s);
637 test_sysctls(inflight, openfiles);
641 * Up things a notch with listen sockets: add connections that can be
642 * accepted to the listen queues.
645 listen_connect_nothing(void)
647 struct sockaddr_in sin;
648 int slisten, sconnect, sv[2];
649 int inflight, openfiles;
652 test = "listen_connect_nothing";
653 printf("%s\n", test);
654 save_sysctls(&inflight, &openfiles);
656 slisten = my_socket(PF_INET, SOCK_STREAM, 0);
657 my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
658 my_listen(slisten, -1);
663 my_getsockname(slisten, (struct sockaddr *)&sin, &len);
665 sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
666 setnonblock(sconnect);
667 my_connect(sconnect, (struct sockaddr *)&sin, len);
671 close4(slisten, sconnect, sv[0], sv[1]);
673 test_sysctls(inflight, openfiles);
677 listen_connect_drop(void)
679 struct sockaddr_in sin;
680 int slisten, sconnect, sv[2];
681 int inflight, openfiles;
684 test = "listen_connect_drop";
685 printf("%s\n", test);
686 save_sysctls(&inflight, &openfiles);
688 slisten = my_socket(PF_INET, SOCK_STREAM, 0);
689 my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
690 my_listen(slisten, -1);
695 my_getsockname(slisten, (struct sockaddr *)&sin, &len);
697 sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
698 setnonblock(sconnect);
699 my_connect(sconnect, (struct sockaddr *)&sin, len);
702 sendfd(sv[0], slisten);
703 close3(slisten, sv[0], sv[1]);
707 test_sysctls(inflight, openfiles);
710 #define RMDIR "rm -Rf "
712 main(int argc, char *argv[])
714 char cmd[sizeof(RMDIR) + PATH_MAX];
718 strlcpy(dpath, "/tmp/unpgc.XXXXXXXX", sizeof(dpath));
719 if (mkdtemp(dpath) == NULL)
723 * Set up a parent process to GC temporary storage when we're done.
733 signal(SIGINT, SIG_IGN);
734 while (waitpid(pid, NULL, 0) != pid);
735 snprintf(cmd, sizeof(cmd), "%s %s", RMDIR, dpath);
740 printf("Start: inflight %d open %d\n", getinflight(),
757 listen_connect_nothing();
758 listen_connect_drop();
760 printf("Finish: inflight %d open %d\n", getinflight(),