Produce alive in-flight sockets with positive "f_count == unp_msgcount"
authormvs <mvs@openbsd.org>
Tue, 11 Jan 2022 08:03:25 +0000 (08:03 +0000)
committermvs <mvs@openbsd.org>
Tue, 11 Jan 2022 08:03:25 +0000 (08:03 +0000)
equation. Such sockets should not be killed by unp_gc() otherwise system
will panic.

tested by anton@; ok bluhm@

regress/sys/kern/unixsockets/ungc.c

index 6f08c56..6cbc9e0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ungc.c,v 1.2 2021/12/15 21:25:55 bluhm Exp $ */
+/* $OpenBSD: ungc.c,v 1.3 2022/01/11 08:03:25 mvs Exp $ */
 
 /*
  * Copyright (c) 2021 Vitaliy Makkoveev <mvs@openbsd.org>
@@ -41,7 +41,7 @@ int main(int argc, char *argv[])
        struct iovec iov;
        struct msghdr msgh;
        struct cmsghdr *cmsgh;
-       int s[2];
+       int sp[2], sl[2], ts;
        int infinite = 0;
 
        if (argc > 1 && !strcmp(argv[1], "--infinite"))
@@ -52,7 +52,60 @@ int main(int argc, char *argv[])
                        err(1, "clock_gettime");
 
        while (1) {
-               if (socketpair(AF_UNIX, SOCK_STREAM|O_NONBLOCK, 0, s) < 0)
+               if (socketpair(AF_UNIX, SOCK_STREAM|O_NONBLOCK, 0, sp) < 0)
+                       err(1, "socketpair");
+
+               iov_buf = 0;
+               iov.iov_base = &iov_buf;
+               iov.iov_len = sizeof(iov_buf);
+               msgh.msg_control = msg_control.control;
+               msgh.msg_controllen = sizeof(msg_control.control);
+               msgh.msg_iov = &iov;
+               msgh.msg_iovlen = 1;
+               msgh.msg_name = NULL;
+               msgh.msg_namelen = 0;
+               cmsgh = CMSG_FIRSTHDR(&msgh);
+               cmsgh->cmsg_len = CMSG_LEN(sizeof(int));
+               cmsgh->cmsg_level = SOL_SOCKET;
+               cmsgh->cmsg_type = SCM_RIGHTS;
+
+               *((int *)CMSG_DATA(cmsgh)) = sp[0];
+
+               if (sendmsg(sp[0], &msgh, 0) < 0) {
+                       if (errno == EMFILE) {
+                               /* Too may sockets in flight */
+                               close(sp[0]);
+                               goto skip;
+                       }
+
+                       err(1, "sendmsg");
+               }
+
+               *((int *)CMSG_DATA(cmsgh)) = sp[1];
+
+               if (sendmsg(sp[1], &msgh, 0) < 0) {
+                       if (errno == EMFILE) {
+                               /* Too may sockets in flight */
+                               close(sp[0]);
+                               goto skip;
+                       }
+
+                       err(1, "sendmsg");
+               }
+
+               /*
+                * After following close(2), the sp[0] socket has
+                * f_count equal to unp_msgcount. This socket is not
+                * in the loop and should not be killed by unp_gc().
+                * This sockets should be successfully received.
+                * The sp[1] socket is stored within sp[0] receive
+                * buffer. This socket should be also successfully
+                * received.
+                */
+
+               close(sp[0]);
+
+               if (socketpair(AF_UNIX, SOCK_STREAM|O_NONBLOCK, 0, sl) < 0)
                        err(1, "socketpair");
 
                iov_buf = 0;
@@ -68,16 +121,63 @@ int main(int argc, char *argv[])
                cmsgh->cmsg_len = CMSG_LEN(sizeof(int) * 2);
                cmsgh->cmsg_level = SOL_SOCKET;
                cmsgh->cmsg_type = SCM_RIGHTS;
-               *((int *)CMSG_DATA(cmsgh) + 0) = s[0];
-               *((int *)CMSG_DATA(cmsgh) + 1) = s[1];
+               *((int *)CMSG_DATA(cmsgh) + 0) = sl[0];
+               *((int *)CMSG_DATA(cmsgh) + 1) = sl[1];
 
-               if (sendmsg(s[0], &msgh, 0) < 0) {
+               if (sendmsg(sl[0], &msgh, 0) < 0) {
                        if (errno != EMFILE)
                                err(1, "sendmsg");
                }
 
-               close(s[0]);
-               close(s[1]);
+               /*
+                * After following close(2), the sl[0] socket is not
+                * in the loop and should be disposed by sorflush().
+                * The sl[1] socket is in the loop and should be
+                * killed by unp_gc().
+                */
+
+               close(sl[0]);
+               close(sl[1]);
+
+               if (recvmsg(sp[1], &msgh, 0) < 0) {
+                       if (errno == EMSGSIZE)
+                               goto skip;
+                       err(1, "recvmsg");
+               }
+
+               if (!(cmsgh = CMSG_FIRSTHDR(&msgh)))
+                       errx(1, "bad cmsg header");
+               if (cmsgh->cmsg_level != SOL_SOCKET)
+                       errx(1, "bad cmsg level");
+               if (cmsgh->cmsg_type != SCM_RIGHTS)
+                       errx(1, "bad cmsg type");
+               if (cmsgh->cmsg_len != CMSG_LEN(sizeof(ts)))
+                       errx(1, "bad cmsg length");
+
+               ts = *((int *)CMSG_DATA(cmsgh));
+
+               if (recvmsg(ts, &msgh, 0) < 0) {
+                       if (errno == EMSGSIZE)
+                               goto skip;
+                       err(1, "recvmsg");
+               }
+
+               close(ts);
+
+               if (!(cmsgh = CMSG_FIRSTHDR(&msgh)))
+                       errx(1, "bad cmsg header");
+               if (cmsgh->cmsg_level != SOL_SOCKET)
+                       errx(1, "bad cmsg level");
+               if (cmsgh->cmsg_type != SCM_RIGHTS)
+                       errx(1, "bad cmsg type");
+               if (cmsgh->cmsg_len != CMSG_LEN(sizeof(ts)))
+                       errx(1, "bad cmsg length");
+
+               ts = *((int *)CMSG_DATA(cmsgh));
+               close(ts);
+
+skip:
+               close(sp[1]);
 
                if (!infinite) {
                        if (clock_gettime(CLOCK_BOOTTIME, &ts_now) <0)