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;
58 getsysctl(const char *name)
64 if (sysctlbyname(name, &i, &len, NULL, 0) < 0)
73 return (getsysctl("kern.openfiles"));
80 return (getsysctl("net.local.inflight"));
87 return (getsysctl("net.local.deferred"));
91 sendfd(int fd, int fdtosend)
94 struct message { struct cmsghdr msg_hdr; int fd; } m;
96 int after_inflight, before_inflight;
98 before_inflight = getinflight();
100 bzero(&mh, sizeof(mh));
101 bzero(&m, sizeof(m));
103 mh.msg_controllen = sizeof(m);
104 m.msg_hdr.cmsg_len = sizeof(m);
105 m.msg_hdr.cmsg_level = SOL_SOCKET;
106 m.msg_hdr.cmsg_type = SCM_RIGHTS;
108 len = sendmsg(fd, &mh, 0);
110 err(-1, "%s: sendmsg", test);
111 after_inflight = getinflight();
112 if (after_inflight != before_inflight + 1)
113 errx(-1, "%s: sendfd: before %d after %d\n", test,
114 before_inflight, after_inflight);
118 close2(int fd1, int fd2)
126 close3(int fd1, int fd2, int fd3)
134 close4(int fd1, int fd2, int fd3, int fd4)
142 close5(int fd1, int fd2, int fd3, int fd4, int fd5)
145 close3(fd1, fd2, fd3);
150 my_socket(int domain, int type, int proto)
154 sock = socket(domain, type, proto);
156 err(-1, "%s: socket", test);
161 my_bind(int sock, struct sockaddr *sa, socklen_t len)
164 if (bind(sock, sa, len) < 0)
165 err(-1, "%s: bind", test);
169 my_connect(int sock, struct sockaddr *sa, socklen_t len)
172 if (connect(sock, sa, len) < 0 && errno != EINPROGRESS)
173 err(-1, "%s: connect", test);
177 my_listen(int sock, int backlog)
180 if (listen(sock, backlog) < 0)
181 err(-1, "%s: listen", test);
185 my_socketpair(int *sv)
188 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
189 err(-1, "%s: socketpair", test);
193 my_getsockname(int s, struct sockaddr *sa, socklen_t *salen)
196 if (getsockname(s, sa, salen) < 0)
197 err(-1, "%s: getsockname", test);
204 if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
205 err(-1, "%s: fcntl(F_SETFL, O_NONBLOCK)", test);
209 alloc3fds(int *s, int *sv)
212 if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
213 err(-1, "%s: socket", test);
214 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
215 err(-1, "%s: socketpair", test);
219 alloc5fds(int *s, int *sva, int *svb)
222 if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
223 err(-1, "%s: socket", test);
224 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sva) < 0)
225 err(-1, "%s: socketpair", test);
226 if (socketpair(PF_UNIX, SOCK_STREAM, 0, svb) < 0)
227 err(-1, "%s: socketpair", test);
231 save_sysctls(int *before_inflight, int *before_openfiles)
234 *before_inflight = getinflight();
235 *before_openfiles = getopenfiles();
239 * Try hard to make sure that the GC does in fact run before we test the
240 * condition of things.
248 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
249 err(-1, "trigger_gc: socket");
256 test_sysctls(int before_inflight, int before_openfiles)
258 int after_inflight, after_openfiles;
261 after_inflight = getinflight();
262 if (after_inflight != before_inflight)
263 warnx("%s: before inflight: %d, after inflight: %d",
264 test, before_inflight, after_inflight);
266 after_openfiles = getopenfiles();
267 if (after_openfiles != before_openfiles)
268 warnx("%s: before: %d, after: %d", test, before_openfiles,
273 twosome_nothing(void)
275 int inflight, openfiles;
279 * Create a pair, close in one order.
281 test = "twosome_nothing1";
282 printf("%s\n", test);
283 save_sysctls(&inflight, &openfiles);
285 close2(sv[0], sv[1]);
286 test_sysctls(inflight, openfiles);
289 * Create a pair, close in the other order.
291 test = "twosome_nothing2";
292 printf("%s\n", test);
293 save_sysctls(&inflight, &openfiles);
295 close2(sv[0], sv[1]);
296 test_sysctls(inflight, openfiles);
300 * Using a socket pair, send various endpoints over the pair and close in
304 twosome_drop_work(const char *testname, int sendvia, int tosend, int closefirst)
306 int inflight, openfiles;
309 printf("%s\n", testname);
311 save_sysctls(&inflight, &openfiles);
313 sendfd(sv[sendvia], sv[tosend]);
315 close2(sv[0], sv[1]);
317 close2(sv[1], sv[0]);
318 test_sysctls(inflight, openfiles);
326 * In various combations, some wastefully symmetric, create socket
327 * pairs and send one or another endpoint over one or another
328 * endpoint, closing the endpoints in various orders.
330 twosome_drop_work("twosome_drop1", 0, 0, 0);
331 twosome_drop_work("twosome_drop2", 0, 0, 1);
332 twosome_drop_work("twosome_drop3", 0, 1, 0);
333 twosome_drop_work("twosome_drop4", 0, 1, 1);
334 twosome_drop_work("twosome_drop5", 1, 0, 0);
335 twosome_drop_work("twosome_drop6", 1, 0, 1);
336 twosome_drop_work("twosome_drop7", 1, 1, 0);
337 twosome_drop_work("twosome_drop8", 1, 1, 1);
341 threesome_nothing(void)
343 int inflight, openfiles;
346 test = "threesome_nothing";
347 printf("%s\n", test);
348 save_sysctls(&inflight, &openfiles);
350 close3(s, sv[0], sv[1]);
351 test_sysctls(inflight, openfiles);
355 * threesome_drop: create a pair and a spare, send the spare over the pair, and
356 * close in various orders and make sure all the fds went away.
361 int inflight, openfiles;
365 * threesome_drop1: close sent send receive
367 test = "threesome_drop1";
368 printf("%s\n", test);
369 save_sysctls(&inflight, &openfiles);
372 close3(s, sv[0], sv[1]);
373 test_sysctls(inflight, openfiles);
376 * threesome_drop2: close sent receive send
378 test = "threesome_drop2";
379 printf("%s\n", test);
380 save_sysctls(&inflight, &openfiles);
383 close3(s, sv[1], sv[0]);
384 test_sysctls(inflight, openfiles);
387 * threesome_drop3: close receive sent send
389 test = "threesome_drop3";
390 printf("%s\n", test);
391 save_sysctls(&inflight, &openfiles);
394 close3(sv[1], s, sv[0]);
395 test_sysctls(inflight, openfiles);
398 * threesome_drop4: close receive send sent
400 test = "threesome_drop4";
401 printf("%s\n", test);
402 save_sysctls(&inflight, &openfiles);
405 close3(sv[1], sv[0], s);
406 test_sysctls(inflight, openfiles);
409 * threesome_drop5: close send receive sent
411 test = "threesome_drop5";
412 printf("%s\n", test);
413 save_sysctls(&inflight, &openfiles);
416 close3(sv[0], sv[1], s);
417 test_sysctls(inflight, openfiles);
420 * threesome_drop6: close send sent receive
422 test = "threesome_drop6";
423 printf("%s\n", test);
424 save_sysctls(&inflight, &openfiles);
426 close3(sv[0], s, sv[1]);
427 test_sysctls(inflight, openfiles);
431 * Fivesome tests: create two socket pairs and a spare, send the spare over
432 * the first socket pair, then send the first socket pair over the second
433 * socket pair, and GC. Do various closes at various points to exercise
437 fivesome_nothing(void)
439 int inflight, openfiles;
440 int spare, sva[2], svb[2];
442 test = "fivesome_nothing";
443 printf("%s\n", test);
444 save_sysctls(&inflight, &openfiles);
445 alloc5fds(&spare, sva, svb);
446 close5(spare, sva[0], sva[1], svb[0], svb[1]);
447 test_sysctls(inflight, openfiles);
451 fivesome_drop_work(const char *testname, int close_spare_after_send,
452 int close_sva_after_send)
454 int inflight, openfiles;
455 int spare, sva[2], svb[2];
457 printf("%s\n", testname);
459 save_sysctls(&inflight, &openfiles);
460 alloc5fds(&spare, sva, svb);
463 * Send spare over sva.
465 sendfd(sva[0], spare);
466 if (close_spare_after_send)
472 sendfd(svb[0], sva[0]);
473 sendfd(svb[0], sva[1]);
474 if (close_sva_after_send)
475 close2(sva[0], sva[1]);
477 close2(svb[0], svb[1]);
479 if (!close_sva_after_send)
480 close2(sva[0], sva[1]);
481 if (!close_spare_after_send)
484 test_sysctls(inflight, openfiles);
491 fivesome_drop_work("fivesome_drop1", 0, 0);
492 fivesome_drop_work("fivesome_drop2", 0, 1);
493 fivesome_drop_work("fivesome_drop3", 1, 0);
494 fivesome_drop_work("fivesome_drop4", 1, 1);
498 * Create a somewhat nasty dual-socket socket intended to upset the garbage
499 * collector if mark-and-sweep is wrong.
504 int inflight, openfiles;
505 int spare, sva[2], svb[2];
507 test = "complex_cycles";
508 printf("%s\n", test);
509 save_sysctls(&inflight, &openfiles);
510 alloc5fds(&spare, sva, svb);
511 sendfd(sva[0], svb[0]);
512 sendfd(sva[0], svb[1]);
513 sendfd(svb[0], sva[0]);
514 sendfd(svb[0], sva[1]);
515 sendfd(svb[0], spare);
516 sendfd(sva[0], spare);
517 close5(spare, sva[0], sva[1], svb[0], svb[1]);
518 test_sysctls(inflight, openfiles);
522 * Listen sockets can also be passed over UNIX domain sockets, so test
523 * various cases, including ones where listen sockets have waiting sockets
524 * hanging off them...
529 struct sockaddr_un sun;
530 struct sockaddr_in sin;
531 int inflight, openfiles;
534 test = "listen_nothing_unp";
535 printf("%s\n", test);
536 bzero(&sun, sizeof(sun));
537 sun.sun_family = AF_LOCAL;
538 sun.sun_len = sizeof(sun);
539 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
540 save_sysctls(&inflight, &openfiles);
541 s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
542 my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
545 (void)unlink(sun.sun_path);
546 test_sysctls(inflight, openfiles);
548 test = "listen_nothing_inet";
549 printf("%s\n", test);
550 bzero(&sin, sizeof(sin));
551 sin.sin_family = AF_INET;
552 sin.sin_len = sizeof(sin);
553 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
554 sin.sin_port = htons(0);
555 save_sysctls(&inflight, &openfiles);
556 s = my_socket(PF_INET, SOCK_STREAM, 0);
557 my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
560 test_sysctls(inflight, openfiles);
564 * Send a listen UDP socket over a UNIX domain socket.
566 * Send a listen TCP socket over a UNIX domain socket.
568 * Do each twice, with closing of the listen socket vs. socketpair in
574 struct sockaddr_un sun;
575 struct sockaddr_in sin;
576 int inflight, openfiles;
579 bzero(&sun, sizeof(sun));
580 sun.sun_family = AF_LOCAL;
581 sun.sun_len = sizeof(sun);
584 * Close listen socket first.
586 test = "listen_drop_unp1";
587 printf("%s\n", test);
588 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
589 save_sysctls(&inflight, &openfiles);
590 s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
591 my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
595 close3(s, sv[0], sv[1]);
596 test_sysctls(inflight, openfiles);
599 * Close socketpair first.
601 test = "listen_drop_unp2";
602 printf("%s\n", test);
603 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
604 save_sysctls(&inflight, &openfiles);
605 s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
606 my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
610 close3(sv[0], sv[1], s);
611 test_sysctls(inflight, openfiles);
613 sin.sin_family = AF_INET;
614 sin.sin_len = sizeof(sin);
615 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
616 sin.sin_port = htons(0);
619 * Close listen socket first.
621 test = "listen_drop_inet1";
622 printf("%s\n", test);
623 bzero(&sun, sizeof(sun));
624 save_sysctls(&inflight, &openfiles);
625 s = my_socket(PF_INET, SOCK_STREAM, 0);
626 my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
630 close3(s, sv[0], sv[1]);
631 test_sysctls(inflight, openfiles);
634 * Close socketpair first.
636 test = "listen_drop_inet2";
637 printf("%s\n", test);
638 bzero(&sun, sizeof(sun));
639 save_sysctls(&inflight, &openfiles);
640 s = my_socket(PF_INET, SOCK_STREAM, 0);
641 my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
645 close3(sv[0], sv[1], s);
646 test_sysctls(inflight, openfiles);
650 * Up things a notch with listen sockets: add connections that can be
651 * accepted to the listen queues.
654 listen_connect_nothing(void)
656 struct sockaddr_in sin;
657 int slisten, sconnect, sv[2];
658 int inflight, openfiles;
661 test = "listen_connect_nothing";
662 printf("%s\n", test);
663 save_sysctls(&inflight, &openfiles);
665 slisten = my_socket(PF_INET, SOCK_STREAM, 0);
666 my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
667 my_listen(slisten, -1);
672 my_getsockname(slisten, (struct sockaddr *)&sin, &len);
674 sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
675 setnonblock(sconnect);
676 my_connect(sconnect, (struct sockaddr *)&sin, len);
680 close4(slisten, sconnect, sv[0], sv[1]);
682 test_sysctls(inflight, openfiles);
686 listen_connect_drop(void)
688 struct sockaddr_in sin;
689 int slisten, sconnect, sv[2];
690 int inflight, openfiles;
693 test = "listen_connect_drop";
694 printf("%s\n", test);
695 save_sysctls(&inflight, &openfiles);
697 slisten = my_socket(PF_INET, SOCK_STREAM, 0);
698 my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
699 my_listen(slisten, -1);
704 my_getsockname(slisten, (struct sockaddr *)&sin, &len);
706 sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
707 setnonblock(sconnect);
708 my_connect(sconnect, (struct sockaddr *)&sin, len);
711 sendfd(sv[0], slisten);
712 close3(slisten, sv[0], sv[1]);
716 test_sysctls(inflight, openfiles);
723 int inflight, openfiles, deferred, deferred1;
726 printf("%s\n", test);
727 save_sysctls(&inflight, &openfiles);
728 deferred = getdeferred();
733 if (socketpair(PF_UNIX, SOCK_STREAM, 0, ff) == -1) {
734 if (errno == EMFILE || errno == ENFILE)
736 err(-1, "socketpair");
738 sendfd(ff[0], fd[0]);
739 sendfd(ff[0], fd[1]);
740 close2(fd[1], fd[0]);
744 close2(fd[0], fd[1]);
746 test_sysctls(inflight, openfiles);
747 deferred1 = getdeferred();
748 if (deferred != deferred1)
749 errx(-1, "recursion: deferred before %d after %d", deferred,
753 #define RMDIR "rm -Rf "
757 char cmd[sizeof(RMDIR) + PATH_MAX];
761 strlcpy(dpath, "/tmp/unpgc.XXXXXXXX", sizeof(dpath));
762 if (mkdtemp(dpath) == NULL)
766 * Set up a parent process to GC temporary storage when we're done.
776 signal(SIGINT, SIG_IGN);
777 while (waitpid(pid, NULL, 0) != pid);
778 snprintf(cmd, sizeof(cmd), "%s %s", RMDIR, dpath);
783 printf("Start: inflight %d open %d\n", getinflight(),
800 listen_connect_nothing();
801 listen_connect_drop();
805 printf("Finish: inflight %d open %d\n", getinflight(),