Home | History | Annotate | Line # | Download | only in kernel
      1 /*	$NetBSD: t_fdpass.c,v 1.4 2026/10/02 01:31:31 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2026 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __RCSID("$NetBSD: t_fdpass.c,v 1.4 2026/10/02 01:31:31 riastradh Exp $");
     31 
     32 #include <sys/param.h>		/* needed by sys/mbuf.h */
     33 
     34 #include <sys/socket.h>
     35 
     36 #include <atf-c.h>
     37 #include <errno.h>
     38 #include <fcntl.h>
     39 
     40 #include <rump/rump.h>
     41 #include <rump/rump_syscalls.h>
     42 
     43 #include "h_macros.h"
     44 
     45 /*
     46  * XXX We include <sys/mbuf.h> last to avoid conflict with atf-c.h over
     47  * m_type.
     48  */
     49 #include <sys/mbuf.h>		/* MHLEN */
     50 
     51 static unsigned
     52 countfds(void)
     53 {
     54 	int fd, nfds, maxfd;
     55 
     56 	RL(maxfd = rump_sys_fcntl(-1, F_MAXFD));
     57 	for (fd = nfds = 0; fd <= maxfd; fd++) {
     58 		if (rump_sys_fcntl(fd, F_GETFL) != -1)
     59 			nfds++;
     60 	}
     61 
     62 	return nfds;
     63 }
     64 
     65 enum {
     66 	MAXNFDS = 32,
     67 };
     68 
     69 static void
     70 test_pr60832(const int fds[static 0], unsigned nfds)
     71 {
     72 	static char buf[4096];
     73 	int sock[2];
     74 	socklen_t optlen;
     75 	int sndbuf;
     76 	unsigned resid, clen, nfds_before, nfds_after;
     77 	ssize_t nsent, nrcvd;
     78 	size_t total = 0;
     79 
     80 	/*
     81 	 * Create a socket pair, nonblocking so that we fail promptly
     82 	 * when buffers are full instead of hanging until timeout.
     83 	 */
     84 	RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0, sock));
     85 
     86 	/*
     87 	 * Count the number of file descriptors in this process so we
     88 	 * make sure we receive the right total number of them at the
     89 	 * end.
     90 	 */
     91 	nfds_before = countfds();
     92 
     93 	/*
     94 	 * Find how much space is required for the control message.
     95 	 *
     96 	 * Note: We use CMSG_SPACE for struct msghdr::msg_controllen;
     97 	 * for struct cmsghdr::cmsg_len below, we will use CMSG_LEN.
     98 	 * Confused?  Thank the cmsg(3) API designers...
     99 	 */
    100 	ATF_REQUIRE(nfds <= MAXNFDS);
    101 	clen = CMSG_SPACE(nfds * sizeof(int));
    102 
    103 	/*
    104 	 * Find out how much we can write to the socket (SO_SNDBUF)
    105 	 * without blocking.
    106 	 *
    107 	 * XXX Why can we still write after sndbuf - sndlowat bytes?
    108 	 */
    109 	optlen = sizeof(sndbuf);
    110 	RL(rump_sys_getsockopt(sock[0], SOL_SOCKET, SO_SNDBUF, &sndbuf,
    111 		&optlen));
    112 	ATF_REQUIRE_MSG(optlen == sizeof(sndbuf),
    113 	    "optlen=%zu sizeof(sndbuf)=%zu",
    114 	    (size_t)optlen, sizeof(sndbuf));
    115 
    116 	printf("sndbuf=%d\n", sndbuf);
    117 	ATF_REQUIRE(sndbuf > 0);
    118 	ATF_REQUIRE((unsigned)sndbuf <= __type_max(__typeof(resid)));
    119 
    120 	/*
    121 	 * Fill the buffer until we have only MHLEN bytes left.  Count
    122 	 * how many bytes we have sent total as we go.
    123 	 *
    124 	 * XXX Seems like this should really go until we have sndlowat
    125 	 * bytes left.
    126 	 */
    127 	total = 0;
    128 	for (resid = sndbuf;
    129 	     resid > (size_t)MHLEN + clen;
    130 	     resid -= (size_t)nsent) {
    131 		const ssize_t nsend = MIN(resid - ((size_t)MHLEN + clen),
    132 		    sizeof(buf));
    133 		struct iovec iov[] = {
    134 			{ .iov_base = buf, .iov_len = nsend },
    135 		};
    136 		const struct msghdr msg = {
    137 			.msg_name = NULL,
    138 			.msg_namelen = 0,
    139 			.msg_iov = iov,
    140 			.msg_iovlen = __arraycount(iov),
    141 			.msg_control = NULL,
    142 			.msg_controllen = 0,
    143 			.msg_flags = 0,
    144 		};
    145 
    146 		printf("sendmsg, resid=%u nsend=%zd\n",
    147 		    resid, nsend);
    148 		ATF_REQUIRE(nsend > 0);
    149 		RL(nsent = rump_sys_sendmsg(sock[0], &msg, 0));
    150 		printf("sent %zd of %zd\n", nsent, nsend);
    151 		ATF_REQUIRE_MSG(nsent == nsend, "nsent=%zd nsend=%zd",
    152 		    nsent, nsend);
    153 		total += (size_t)nsent;
    154 	}
    155 	ATF_REQUIRE_MSG(resid == (size_t)MHLEN + clen, "resid=%u", resid);
    156 
    157 	/*
    158 	 * Try to fill the remainder of the buffer, but with file
    159 	 * descriptors as well.  The control message length should
    160 	 * count toward sndbuf as well as the data length.
    161 	 */
    162     {
    163 	union {
    164 		struct cmsghdr	hdr;
    165 		unsigned char	buf[CMSG_SPACE(MAXNFDS * sizeof(int))];
    166 	} cmsgbuf;
    167 	const ssize_t nsend = resid - clen;
    168 	struct iovec iov[] = {
    169 		{ .iov_base = buf, .iov_len = nsend }
    170 	};
    171 	const struct msghdr msg = {
    172 		.msg_name = NULL,
    173 		.msg_namelen = 0,
    174 		.msg_iov = iov,
    175 		.msg_iovlen = __arraycount(iov),
    176 		.msg_control = &cmsgbuf,
    177 		.msg_controllen = clen,
    178 		.msg_flags = 0,
    179 	};
    180 	struct cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
    181 
    182 	cmsg->cmsg_len = CMSG_LEN(nfds * sizeof(int));
    183 	cmsg->cmsg_level = SOL_SOCKET;
    184 	cmsg->cmsg_type = SCM_RIGHTS;
    185 	memcpy(CMSG_DATA(cmsg), fds, nfds * sizeof(int));
    186 
    187 	printf("send %zd data bytes with %u control bytes\n", nsend, clen);
    188 	RL(nsent = rump_sys_sendmsg(sock[0], &msg, 0));
    189 	total += (size_t)nsent;
    190     }
    191 
    192 	/*
    193 	 * Receive the data and file descriptors.  In some chunk
    194 	 * (though it may not be the last chunk, because the last chunk
    195 	 * may be split into smaller chunks), we should receive the
    196 	 * fds.
    197 	 */
    198 	for (resid = total; resid > 0; resid -= (size_t)nrcvd) {
    199 		union {
    200 			struct cmsghdr	hdr;
    201 			unsigned char	buf[CMSG_SPACE(MAXNFDS * sizeof(int))];
    202 		} cmsgbuf;
    203 		const ssize_t nrecv = sizeof(buf);
    204 		struct iovec iov[] = {
    205 			{ .iov_base = buf, .iov_len = nrecv }
    206 		};
    207 		struct msghdr msg = {
    208 			.msg_name = NULL,
    209 			.msg_namelen = 0,
    210 			.msg_iov = iov,
    211 			.msg_iovlen = __arraycount(iov),
    212 			.msg_control = &cmsgbuf,
    213 			.msg_controllen = sizeof(cmsgbuf),
    214 			.msg_flags = 0,
    215 		};
    216 		struct cmsghdr *cmsg;
    217 
    218 		printf("recvmsg, resid=%u nrecv=%zu\n", resid, nrecv);
    219 		nrcvd = rump_sys_recvmsg(sock[1], &msg, 0);
    220 		if (nrcvd == -1) {
    221 			int error = errno;
    222 
    223 			atf_tc_fail_nonfatal("recvmsg: %d (%s)", error,
    224 			    strerror(error));
    225 			break;
    226 		}
    227 		printf("nrcvd=%zu\n", nrcvd);
    228 		ATF_REQUIRE_MSG((size_t)nrcvd <= resid, "nrcvd=%zd resid=%u",
    229 		    nrcvd, resid);
    230 		for (cmsg = CMSG_FIRSTHDR(&msg);
    231 		     cmsg != NULL;
    232 		     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
    233 			unsigned i, n;
    234 			const int *fdptr;
    235 
    236 			if (cmsg->cmsg_level != SOL_SOCKET) {
    237 				atf_tc_fail_nonfatal("cmsg_level=%d,"
    238 				    " expected %d\n",
    239 				    cmsg->cmsg_level, SOL_SOCKET);
    240 				continue;
    241 			}
    242 			if (cmsg->cmsg_type != SCM_RIGHTS) {
    243 				atf_tc_fail_nonfatal("cmsg_type=%d,"
    244 				    " expected %d\n",
    245 				    cmsg->cmsg_type, SCM_RIGHTS);
    246 				continue;
    247 			}
    248 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(int))) {
    249 				atf_tc_fail_nonfatal("cmsg_len=%zu,"
    250 				    " expected >=%zu\n",
    251 				    (size_t)cmsg->cmsg_len,
    252 				    (size_t)CMSG_LEN(sizeof(int)));
    253 				continue;
    254 			}
    255 			if ((cmsg->cmsg_len - CMSG_LEN(0)) % sizeof(int)) {
    256 				atf_tc_fail_nonfatal("cmsg_len=%zu,"
    257 				    " expected 0 mod %zu after %zu\n",
    258 				    (size_t)cmsg->cmsg_len,
    259 				    (size_t)sizeof(int),
    260 				    (size_t)CMSG_LEN(0));
    261 				continue;
    262 			}
    263 			n = (cmsg->cmsg_len - CMSG_LEN(0))/sizeof(int);
    264 			ATF_CHECK(n > 0);
    265 			fdptr = (const int *)CMSG_DATA(cmsg);
    266 			for (i = 0; i < n; i++)
    267 				RL(rump_sys_close(fdptr[i]));
    268 		}
    269 	}
    270 
    271 	/*
    272 	 * For one final recvmsg, we should block.
    273 	 */
    274     {
    275 	union {
    276 		struct cmsghdr	hdr;
    277 		unsigned char	buf[CMSG_SPACE(MAXNFDS * sizeof(int))];
    278 	} cmsgbuf;
    279 	struct iovec iov[] = {
    280 		{ .iov_base = buf, .iov_len = sizeof(buf) }
    281 	};
    282 	struct msghdr msg = {
    283 		.msg_name = NULL,
    284 		.msg_namelen = 0,
    285 		.msg_iov = iov,
    286 		.msg_iovlen = __arraycount(iov),
    287 		.msg_control = &cmsgbuf,
    288 		.msg_controllen = sizeof(cmsgbuf),
    289 		.msg_flags = 0,
    290 	};
    291 
    292 	ATF_CHECK_ERRNO(EAGAIN, rump_sys_recvmsg(sock[1], &msg, 0) == -1);
    293     }
    294 
    295 	/*
    296 	 * Verify the total number of file descriptors has not changed.
    297 	 * (We closed all the copies we sent ourself in the recvmsg
    298 	 * loop above.)
    299 	 */
    300 	nfds_after = countfds();
    301 	ATF_CHECK_MSG(nfds_before == nfds_after,
    302 	    "nfds_before=%u nfds_after=%u", nfds_before, nfds_after);
    303 
    304 	RL(rump_sys_close(sock[0]));
    305 	RL(rump_sys_close(sock[1]));
    306 }
    307 
    308 ATF_TC(pr60832);
    309 ATF_TC_HEAD(pr60832, tc)
    310 {
    311 	atf_tc_set_md_var(tc, "descr", "Test sendmsg fails on partial data");
    312 }
    313 ATF_TC_BODY(pr60832, tc)
    314 {
    315 	int fds[MAXNFDS];
    316 	unsigned i;
    317 
    318 	REQUIRE_LIBC(setlinebuf(stdout), EOF);
    319 	printf("MHLEN=%d\n", MHLEN);
    320 
    321 	/*
    322 	 * Initialize rump before we try using it.
    323 	 */
    324 	RL(rump_init());
    325 
    326 	/*
    327 	 * Create some spare file descriptors.
    328 	 */
    329 	for (i = 0; i < __arraycount(fds); i++)
    330 		RL(fds[i] = rump_sys_socket(AF_LOCAL, SOCK_STREAM, 0));
    331 
    332 	/*
    333 	 * Run the test with different numbers of file descriptors.
    334 	 */
    335 	for (i = 0; i < __arraycount(fds); i++) {
    336 		const unsigned nfds = i + 1;
    337 
    338 		printf("test %u fd%s\n", nfds, nfds == 1 ? "" : "s");
    339 		test_pr60832(fds, nfds);
    340 	}
    341 }
    342 
    343 ATF_TP_ADD_TCS(tp)
    344 {
    345 
    346 	ATF_TP_ADD_TC(tp, pr60832);
    347 
    348 	return atf_no_error();
    349 }
    350