Path: blob/main/tools/regression/sockets/unix_gc/unix_gc.c
48254 views
/*-1* Copyright (c) 2007 Robert N. M. Watson2* 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* A few regression tests for UNIX domain sockets. Run from single-user mode28* as it checks the openfiles sysctl to look for leaks, and we don't want that29* changing due to other processes doing stuff.30*/3132#include <sys/types.h>33#include <sys/signal.h>34#include <sys/socket.h>35#include <sys/sysctl.h>36#include <sys/un.h>37#include <sys/wait.h>3839#include <netinet/in.h>4041#include <err.h>42#include <errno.h>43#include <fcntl.h>44#include <limits.h>45#include <stdio.h>46#include <stdlib.h>47#include <string.h>48#include <unistd.h>4950static int forcegc = 1;51static char dpath[PATH_MAX];52static const char *test;5354static int55getsysctl(const char *name)56{57size_t len;58int i;5960len = sizeof(i);61if (sysctlbyname(name, &i, &len, NULL, 0) < 0)62err(-1, "%s", name);63return (i);64}6566static int67getopenfiles(void)68{6970return (getsysctl("kern.openfiles"));71}7273static int74getinflight(void)75{7677return (getsysctl("net.local.inflight"));78}7980static int81getdeferred(void)82{8384return (getsysctl("net.local.deferred"));85}8687static void88sendfd(int fd, int fdtosend)89{90struct msghdr mh;91struct message { struct cmsghdr msg_hdr; int fd; } m;92ssize_t len;93int after_inflight, before_inflight;9495before_inflight = getinflight();9697bzero(&mh, sizeof(mh));98bzero(&m, sizeof(m));99mh.msg_control = &m;100mh.msg_controllen = sizeof(m);101m.msg_hdr.cmsg_len = sizeof(m);102m.msg_hdr.cmsg_level = SOL_SOCKET;103m.msg_hdr.cmsg_type = SCM_RIGHTS;104m.fd = fdtosend;105len = sendmsg(fd, &mh, 0);106if (len < 0)107err(-1, "%s: sendmsg", test);108after_inflight = getinflight();109if (after_inflight != before_inflight + 1)110errx(-1, "%s: sendfd: before %d after %d\n", test,111before_inflight, after_inflight);112}113114static void115close2(int fd1, int fd2)116{117118close(fd1);119close(fd2);120}121122static void123close3(int fd1, int fd2, int fd3)124{125126close2(fd1, fd2);127close(fd3);128}129130static void131close4(int fd1, int fd2, int fd3, int fd4)132{133134close2(fd1, fd2);135close2(fd3, fd4);136}137138static void139close5(int fd1, int fd2, int fd3, int fd4, int fd5)140{141142close3(fd1, fd2, fd3);143close2(fd4, fd5);144}145146static int147my_socket(int domain, int type, int proto)148{149int sock;150151sock = socket(domain, type, proto);152if (sock < 0)153err(-1, "%s: socket", test);154return (sock);155}156157static void158my_bind(int sock, struct sockaddr *sa, socklen_t len)159{160161if (bind(sock, sa, len) < 0)162err(-1, "%s: bind", test);163}164165static void166my_connect(int sock, struct sockaddr *sa, socklen_t len)167{168169if (connect(sock, sa, len) < 0 && errno != EINPROGRESS)170err(-1, "%s: connect", test);171}172173static void174my_listen(int sock, int backlog)175{176177if (listen(sock, backlog) < 0)178err(-1, "%s: listen", test);179}180181static void182my_socketpair(int *sv)183{184185if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)186err(-1, "%s: socketpair", test);187}188189static void190my_getsockname(int s, struct sockaddr *sa, socklen_t *salen)191{192193if (getsockname(s, sa, salen) < 0)194err(-1, "%s: getsockname", test);195}196197static void198setnonblock(int s)199{200201if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)202err(-1, "%s: fcntl(F_SETFL, O_NONBLOCK)", test);203}204205static void206alloc3fds(int *s, int *sv)207{208209if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)210err(-1, "%s: socket", test);211if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)212err(-1, "%s: socketpair", test);213}214215static void216alloc5fds(int *s, int *sva, int *svb)217{218219if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)220err(-1, "%s: socket", test);221if (socketpair(PF_UNIX, SOCK_STREAM, 0, sva) < 0)222err(-1, "%s: socketpair", test);223if (socketpair(PF_UNIX, SOCK_STREAM, 0, svb) < 0)224err(-1, "%s: socketpair", test);225}226227static void228save_sysctls(int *before_inflight, int *before_openfiles)229{230231*before_inflight = getinflight();232*before_openfiles = getopenfiles();233}234235/*236* Try hard to make sure that the GC does in fact run before we test the237* condition of things.238*/239static void240trigger_gc(void)241{242int s;243244if (forcegc) {245if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)246err(-1, "trigger_gc: socket");247close(s);248}249sleep(1);250}251252static void253test_sysctls(int before_inflight, int before_openfiles)254{255int after_inflight, after_openfiles;256257trigger_gc();258after_inflight = getinflight();259if (after_inflight != before_inflight)260warnx("%s: before inflight: %d, after inflight: %d",261test, before_inflight, after_inflight);262263after_openfiles = getopenfiles();264if (after_openfiles != before_openfiles)265warnx("%s: before: %d, after: %d", test, before_openfiles,266after_openfiles);267}268269static void270twosome_nothing(void)271{272int inflight, openfiles;273int sv[2];274275/*276* Create a pair, close in one order.277*/278test = "twosome_nothing1";279printf("%s\n", test);280save_sysctls(&inflight, &openfiles);281my_socketpair(sv);282close2(sv[0], sv[1]);283test_sysctls(inflight, openfiles);284285/*286* Create a pair, close in the other order.287*/288test = "twosome_nothing2";289printf("%s\n", test);290save_sysctls(&inflight, &openfiles);291my_socketpair(sv);292close2(sv[0], sv[1]);293test_sysctls(inflight, openfiles);294}295296/*297* Using a socket pair, send various endpoints over the pair and close in298* various orders.299*/300static void301twosome_drop_work(const char *testname, int sendvia, int tosend, int closefirst)302{303int inflight, openfiles;304int sv[2];305306printf("%s\n", testname);307test = testname;308save_sysctls(&inflight, &openfiles);309my_socketpair(sv);310sendfd(sv[sendvia], sv[tosend]);311if (closefirst == 0)312close2(sv[0], sv[1]);313else314close2(sv[1], sv[0]);315test_sysctls(inflight, openfiles);316}317318static void319twosome_drop(void)320{321322/*323* In various combations, some wastefully symmetric, create socket324* pairs and send one or another endpoint over one or another325* endpoint, closing the endpoints in various orders.326*/327twosome_drop_work("twosome_drop1", 0, 0, 0);328twosome_drop_work("twosome_drop2", 0, 0, 1);329twosome_drop_work("twosome_drop3", 0, 1, 0);330twosome_drop_work("twosome_drop4", 0, 1, 1);331twosome_drop_work("twosome_drop5", 1, 0, 0);332twosome_drop_work("twosome_drop6", 1, 0, 1);333twosome_drop_work("twosome_drop7", 1, 1, 0);334twosome_drop_work("twosome_drop8", 1, 1, 1);335}336337static void338threesome_nothing(void)339{340int inflight, openfiles;341int s, sv[2];342343test = "threesome_nothing";344printf("%s\n", test);345save_sysctls(&inflight, &openfiles);346alloc3fds(&s, sv);347close3(s, sv[0], sv[1]);348test_sysctls(inflight, openfiles);349}350351/*352* threesome_drop: create a pair and a spare, send the spare over the pair, and353* close in various orders and make sure all the fds went away.354*/355static void356threesome_drop(void)357{358int inflight, openfiles;359int s, sv[2];360361/*362* threesome_drop1: close sent send receive363*/364test = "threesome_drop1";365printf("%s\n", test);366save_sysctls(&inflight, &openfiles);367alloc3fds(&s, sv);368sendfd(sv[0], s);369close3(s, sv[0], sv[1]);370test_sysctls(inflight, openfiles);371372/*373* threesome_drop2: close sent receive send374*/375test = "threesome_drop2";376printf("%s\n", test);377save_sysctls(&inflight, &openfiles);378alloc3fds(&s, sv);379sendfd(sv[0], s);380close3(s, sv[1], sv[0]);381test_sysctls(inflight, openfiles);382383/*384* threesome_drop3: close receive sent send385*/386test = "threesome_drop3";387printf("%s\n", test);388save_sysctls(&inflight, &openfiles);389alloc3fds(&s, sv);390sendfd(sv[0], s);391close3(sv[1], s, sv[0]);392test_sysctls(inflight, openfiles);393394/*395* threesome_drop4: close receive send sent396*/397test = "threesome_drop4";398printf("%s\n", test);399save_sysctls(&inflight, &openfiles);400alloc3fds(&s, sv);401sendfd(sv[0], s);402close3(sv[1], sv[0], s);403test_sysctls(inflight, openfiles);404405/*406* threesome_drop5: close send receive sent407*/408test = "threesome_drop5";409printf("%s\n", test);410save_sysctls(&inflight, &openfiles);411alloc3fds(&s, sv);412sendfd(sv[0], s);413close3(sv[0], sv[1], s);414test_sysctls(inflight, openfiles);415416/*417* threesome_drop6: close send sent receive418*/419test = "threesome_drop6";420printf("%s\n", test);421save_sysctls(&inflight, &openfiles);422alloc3fds(&s, sv);423close3(sv[0], s, sv[1]);424test_sysctls(inflight, openfiles);425}426427/*428* Fivesome tests: create two socket pairs and a spare, send the spare over429* the first socket pair, then send the first socket pair over the second430* socket pair, and GC. Do various closes at various points to exercise431* various cases.432*/433static void434fivesome_nothing(void)435{436int inflight, openfiles;437int spare, sva[2], svb[2];438439test = "fivesome_nothing";440printf("%s\n", test);441save_sysctls(&inflight, &openfiles);442alloc5fds(&spare, sva, svb);443close5(spare, sva[0], sva[1], svb[0], svb[1]);444test_sysctls(inflight, openfiles);445}446447static void448fivesome_drop_work(const char *testname, int close_spare_after_send,449int close_sva_after_send)450{451int inflight, openfiles;452int spare, sva[2], svb[2];453454printf("%s\n", testname);455test = testname;456save_sysctls(&inflight, &openfiles);457alloc5fds(&spare, sva, svb);458459/*460* Send spare over sva.461*/462sendfd(sva[0], spare);463if (close_spare_after_send)464close(spare);465466/*467* Send sva over svb.468*/469sendfd(svb[0], sva[0]);470sendfd(svb[0], sva[1]);471if (close_sva_after_send)472close2(sva[0], sva[1]);473474close2(svb[0], svb[1]);475476if (!close_sva_after_send)477close2(sva[0], sva[1]);478if (!close_spare_after_send)479close(spare);480481test_sysctls(inflight, openfiles);482}483484static void485fivesome_drop(void)486{487488fivesome_drop_work("fivesome_drop1", 0, 0);489fivesome_drop_work("fivesome_drop2", 0, 1);490fivesome_drop_work("fivesome_drop3", 1, 0);491fivesome_drop_work("fivesome_drop4", 1, 1);492}493494/*495* Create a somewhat nasty dual-socket socket intended to upset the garbage496* collector if mark-and-sweep is wrong.497*/498static void499complex_cycles(void)500{501int inflight, openfiles;502int spare, sva[2], svb[2];503504test = "complex_cycles";505printf("%s\n", test);506save_sysctls(&inflight, &openfiles);507alloc5fds(&spare, sva, svb);508sendfd(sva[0], svb[0]);509sendfd(sva[0], svb[1]);510sendfd(svb[0], sva[0]);511sendfd(svb[0], sva[1]);512sendfd(svb[0], spare);513sendfd(sva[0], spare);514close5(spare, sva[0], sva[1], svb[0], svb[1]);515test_sysctls(inflight, openfiles);516}517518/*519* Listen sockets can also be passed over UNIX domain sockets, so test520* various cases, including ones where listen sockets have waiting sockets521* hanging off them...522*/523static void524listen_nothing(void)525{526struct sockaddr_un sun;527struct sockaddr_in sin;528int inflight, openfiles;529int s;530531test = "listen_nothing_unp";532printf("%s\n", test);533bzero(&sun, sizeof(sun));534sun.sun_family = AF_LOCAL;535sun.sun_len = sizeof(sun);536snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);537save_sysctls(&inflight, &openfiles);538s = my_socket(PF_LOCAL, SOCK_STREAM, 0);539my_bind(s, (struct sockaddr *)&sun, sizeof(sun));540my_listen(s, -1);541close(s);542(void)unlink(sun.sun_path);543test_sysctls(inflight, openfiles);544545test = "listen_nothing_inet";546printf("%s\n", test);547bzero(&sin, sizeof(sin));548sin.sin_family = AF_INET;549sin.sin_len = sizeof(sin);550sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);551sin.sin_port = htons(0);552save_sysctls(&inflight, &openfiles);553s = my_socket(PF_INET, SOCK_STREAM, 0);554my_bind(s, (struct sockaddr *)&sin, sizeof(sin));555my_listen(s, -1);556close(s);557test_sysctls(inflight, openfiles);558}559560/*561* Send a listen UDP socket over a UNIX domain socket.562*563* Send a listen TCP socket over a UNIX domain socket.564*565* Do each twice, with closing of the listen socket vs. socketpair in566* different orders.567*/568static void569listen_drop(void)570{571struct sockaddr_un sun;572struct sockaddr_in sin;573int inflight, openfiles;574int s, sv[2];575576bzero(&sun, sizeof(sun));577sun.sun_family = AF_LOCAL;578sun.sun_len = sizeof(sun);579580/*581* Close listen socket first.582*/583test = "listen_drop_unp1";584printf("%s\n", test);585snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);586save_sysctls(&inflight, &openfiles);587s = my_socket(PF_LOCAL, SOCK_STREAM, 0);588my_bind(s, (struct sockaddr *)&sun, sizeof(sun));589my_listen(s, -1);590my_socketpair(sv);591sendfd(sv[0], s);592close3(s, sv[0], sv[1]);593test_sysctls(inflight, openfiles);594595/*596* Close socketpair first.597*/598test = "listen_drop_unp2";599printf("%s\n", test);600snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);601save_sysctls(&inflight, &openfiles);602s = my_socket(PF_LOCAL, SOCK_STREAM, 0);603my_bind(s, (struct sockaddr *)&sun, sizeof(sun));604my_listen(s, -1);605my_socketpair(sv);606sendfd(sv[0], s);607close3(sv[0], sv[1], s);608test_sysctls(inflight, openfiles);609610sin.sin_family = AF_INET;611sin.sin_len = sizeof(sin);612sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);613sin.sin_port = htons(0);614615/*616* Close listen socket first.617*/618test = "listen_drop_inet1";619printf("%s\n", test);620bzero(&sun, sizeof(sun));621save_sysctls(&inflight, &openfiles);622s = my_socket(PF_INET, SOCK_STREAM, 0);623my_bind(s, (struct sockaddr *)&sin, sizeof(sin));624my_listen(s, -1);625my_socketpair(sv);626sendfd(sv[0], s);627close3(s, sv[0], sv[1]);628test_sysctls(inflight, openfiles);629630/*631* Close socketpair first.632*/633test = "listen_drop_inet2";634printf("%s\n", test);635bzero(&sun, sizeof(sun));636save_sysctls(&inflight, &openfiles);637s = my_socket(PF_INET, SOCK_STREAM, 0);638my_bind(s, (struct sockaddr *)&sin, sizeof(sin));639my_listen(s, -1);640my_socketpair(sv);641sendfd(sv[0], s);642close3(sv[0], sv[1], s);643test_sysctls(inflight, openfiles);644}645646/*647* Up things a notch with listen sockets: add connections that can be648* accepted to the listen queues.649*/650static void651listen_connect_nothing(void)652{653struct sockaddr_in sin;654int slisten, sconnect, sv[2];655int inflight, openfiles;656socklen_t len;657658test = "listen_connect_nothing";659printf("%s\n", test);660save_sysctls(&inflight, &openfiles);661662slisten = my_socket(PF_INET, SOCK_STREAM, 0);663my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));664my_listen(slisten, -1);665666my_socketpair(sv);667668len = sizeof(sin);669my_getsockname(slisten, (struct sockaddr *)&sin, &len);670671sconnect = my_socket(PF_INET, SOCK_STREAM, 0);672setnonblock(sconnect);673my_connect(sconnect, (struct sockaddr *)&sin, len);674675sleep(1);676677close4(slisten, sconnect, sv[0], sv[1]);678679test_sysctls(inflight, openfiles);680}681682static void683listen_connect_drop(void)684{685struct sockaddr_in sin;686int slisten, sconnect, sv[2];687int inflight, openfiles;688socklen_t len;689690test = "listen_connect_drop";691printf("%s\n", test);692save_sysctls(&inflight, &openfiles);693694slisten = my_socket(PF_INET, SOCK_STREAM, 0);695my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));696my_listen(slisten, -1);697698my_socketpair(sv);699700len = sizeof(sin);701my_getsockname(slisten, (struct sockaddr *)&sin, &len);702703sconnect = my_socket(PF_INET, SOCK_STREAM, 0);704setnonblock(sconnect);705my_connect(sconnect, (struct sockaddr *)&sin, len);706707sleep(1);708sendfd(sv[0], slisten);709close3(slisten, sv[0], sv[1]);710sleep(1);711close(sconnect);712713test_sysctls(inflight, openfiles);714}715716static void717recursion(void)718{719int fd[2], ff[2];720int inflight, openfiles, deferred, deferred1;721722test = "recursion";723printf("%s\n", test);724save_sysctls(&inflight, &openfiles);725deferred = getdeferred();726727my_socketpair(fd);728729for (;;) {730if (socketpair(PF_UNIX, SOCK_STREAM, 0, ff) == -1) {731if (errno == EMFILE || errno == ENFILE)732break;733err(-1, "socketpair");734}735sendfd(ff[0], fd[0]);736sendfd(ff[0], fd[1]);737close2(fd[1], fd[0]);738fd[0] = ff[0];739fd[1] = ff[1];740}741close2(fd[0], fd[1]);742sleep(1);743test_sysctls(inflight, openfiles);744deferred1 = getdeferred();745if (deferred != deferred1)746errx(-1, "recursion: deferred before %d after %d", deferred,747deferred1);748}749750#define RMDIR "rm -Rf "751int752main(void)753{754char cmd[sizeof(RMDIR) + PATH_MAX];755int serrno;756pid_t pid;757758strlcpy(dpath, "/tmp/unpgc.XXXXXXXX", sizeof(dpath));759if (mkdtemp(dpath) == NULL)760err(-1, "mkdtemp");761762/*763* Set up a parent process to GC temporary storage when we're done.764*/765pid = fork();766if (pid < 0) {767serrno = errno;768(void)rmdir(dpath);769errno = serrno;770err(-1, "fork");771}772if (pid > 0) {773signal(SIGINT, SIG_IGN);774while (waitpid(pid, NULL, 0) != pid);775snprintf(cmd, sizeof(cmd), "%s %s", RMDIR, dpath);776(void)system(cmd);777exit(0);778}779780printf("Start: inflight %d open %d\n", getinflight(),781getopenfiles());782783twosome_nothing();784twosome_drop();785786threesome_nothing();787threesome_drop();788789fivesome_nothing();790fivesome_drop();791792complex_cycles();793794listen_nothing();795listen_drop();796797listen_connect_nothing();798listen_connect_drop();799800recursion();801802printf("Finish: inflight %d open %d\n", getinflight(),803getopenfiles());804return (0);805}806807808