xref: /freebsd/lib/libnv/msgio.c (revision 5f4c09dd85bff675e0ca63c55ea3c517e0fddfcc)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
6  * All rights reserved.
7  *
8  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #ifdef HAVE_PJDLOG
47 #include <pjdlog.h>
48 #endif
49 
50 #include "common_impl.h"
51 #include "msgio.h"
52 
53 #ifndef	HAVE_PJDLOG
54 #include <assert.h>
55 #define	PJDLOG_ASSERT(...)		assert(__VA_ARGS__)
56 #define	PJDLOG_RASSERT(expr, ...)	assert(expr)
57 #define	PJDLOG_ABORT(...)		abort()
58 #endif
59 
60 #ifdef __linux__
61 /* Linux: arbitrary size, but must be lower than SCM_MAX_FD. */
62 #define	PKG_MAX_SIZE	((64U - 1) * CMSG_SPACE(sizeof(int)))
63 #else
64 /*
65  * To work around limitations in 32-bit emulation on 64-bit kernels, use a
66  * machine-independent limit on the number of FDs per message.  Each control
67  * message contains 1 FD and requires 12 bytes for the header, 4 pad bytes,
68  * 4 bytes for the descriptor, and another 4 pad bytes.
69  */
70 #define	PKG_MAX_SIZE	(MCLBYTES / 24)
71 #endif
72 
73 static int
74 msghdr_add_fd(struct cmsghdr *cmsg, int fd)
75 {
76 
77 	PJDLOG_ASSERT(fd >= 0);
78 
79 	cmsg->cmsg_level = SOL_SOCKET;
80 	cmsg->cmsg_type = SCM_RIGHTS;
81 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
82 	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
83 
84 	return (0);
85 }
86 
87 static void
88 fd_wait(int fd, bool doread)
89 {
90 	fd_set fds;
91 
92 	PJDLOG_ASSERT(fd >= 0);
93 
94 	FD_ZERO(&fds);
95 	FD_SET(fd, &fds);
96 	(void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
97 	    NULL, NULL);
98 }
99 
100 static int
101 msg_recv(int sock, struct msghdr *msg)
102 {
103 	int flags;
104 
105 	PJDLOG_ASSERT(sock >= 0);
106 
107 #ifdef MSG_CMSG_CLOEXEC
108 	flags = MSG_CMSG_CLOEXEC;
109 #else
110 	flags = 0;
111 #endif
112 
113 	for (;;) {
114 		fd_wait(sock, true);
115 		if (recvmsg(sock, msg, flags) == -1) {
116 			if (errno == EINTR)
117 				continue;
118 			return (-1);
119 		}
120 		break;
121 	}
122 
123 	return (0);
124 }
125 
126 static int
127 msg_send(int sock, const struct msghdr *msg)
128 {
129 
130 	PJDLOG_ASSERT(sock >= 0);
131 
132 	for (;;) {
133 		fd_wait(sock, false);
134 		if (sendmsg(sock, msg, 0) == -1) {
135 			if (errno == EINTR)
136 				continue;
137 			return (-1);
138 		}
139 		break;
140 	}
141 
142 	return (0);
143 }
144 
145 #ifdef __FreeBSD__
146 int
147 cred_send(int sock)
148 {
149 	unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
150 	struct msghdr msg;
151 	struct cmsghdr *cmsg;
152 	struct iovec iov;
153 	uint8_t dummy;
154 
155 	bzero(credbuf, sizeof(credbuf));
156 	bzero(&msg, sizeof(msg));
157 	bzero(&iov, sizeof(iov));
158 
159 	/*
160 	 * XXX: We send one byte along with the control message, because
161 	 *      setting msg_iov to NULL only works if this is the first
162 	 *      packet send over the socket. Once we send some data we
163 	 *      won't be able to send credentials anymore. This is most
164 	 *      likely a kernel bug.
165 	 */
166 	dummy = 0;
167 	iov.iov_base = &dummy;
168 	iov.iov_len = sizeof(dummy);
169 
170 	msg.msg_iov = &iov;
171 	msg.msg_iovlen = 1;
172 	msg.msg_control = credbuf;
173 	msg.msg_controllen = sizeof(credbuf);
174 
175 	cmsg = CMSG_FIRSTHDR(&msg);
176 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
177 	cmsg->cmsg_level = SOL_SOCKET;
178 	cmsg->cmsg_type = SCM_CREDS;
179 
180 	if (msg_send(sock, &msg) == -1)
181 		return (-1);
182 
183 	return (0);
184 }
185 
186 int
187 cred_recv(int sock, struct cmsgcred *cred)
188 {
189 	unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
190 	struct msghdr msg;
191 	struct cmsghdr *cmsg;
192 	struct iovec iov;
193 	uint8_t dummy;
194 
195 	bzero(credbuf, sizeof(credbuf));
196 	bzero(&msg, sizeof(msg));
197 	bzero(&iov, sizeof(iov));
198 
199 	iov.iov_base = &dummy;
200 	iov.iov_len = sizeof(dummy);
201 
202 	msg.msg_iov = &iov;
203 	msg.msg_iovlen = 1;
204 	msg.msg_control = credbuf;
205 	msg.msg_controllen = sizeof(credbuf);
206 
207 	if (msg_recv(sock, &msg) == -1)
208 		return (-1);
209 
210 	cmsg = CMSG_FIRSTHDR(&msg);
211 	if (cmsg == NULL ||
212 	    cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) ||
213 	    cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) {
214 		errno = EINVAL;
215 		return (-1);
216 	}
217 	bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred));
218 
219 	return (0);
220 }
221 #endif
222 
223 static int
224 fd_package_send(int sock, const int *fds, size_t nfds)
225 {
226 	struct msghdr msg;
227 	struct cmsghdr *cmsg;
228 	struct iovec iov;
229 	unsigned int i;
230 	int serrno, ret;
231 	uint8_t dummy;
232 
233 	PJDLOG_ASSERT(sock >= 0);
234 	PJDLOG_ASSERT(fds != NULL);
235 	PJDLOG_ASSERT(nfds > 0);
236 
237 	bzero(&msg, sizeof(msg));
238 
239 	/*
240 	 * XXX: Look into cred_send function for more details.
241 	 */
242 	dummy = 0;
243 	iov.iov_base = &dummy;
244 	iov.iov_len = sizeof(dummy);
245 
246 	msg.msg_iov = &iov;
247 	msg.msg_iovlen = 1;
248 	msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
249 	msg.msg_control = calloc(1, msg.msg_controllen);
250 	if (msg.msg_control == NULL)
251 		return (-1);
252 
253 	ret = -1;
254 
255 	for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
256 	    i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
257 		if (msghdr_add_fd(cmsg, fds[i]) == -1)
258 			goto end;
259 	}
260 
261 	if (msg_send(sock, &msg) == -1)
262 		goto end;
263 
264 	ret = 0;
265 end:
266 	serrno = errno;
267 	free(msg.msg_control);
268 	errno = serrno;
269 	return (ret);
270 }
271 
272 static int
273 fd_package_recv(int sock, int *fds, size_t nfds)
274 {
275 	struct msghdr msg;
276 	struct cmsghdr *cmsg;
277 	unsigned int i;
278 	int serrno, ret;
279 	struct iovec iov;
280 	uint8_t dummy;
281 
282 	PJDLOG_ASSERT(sock >= 0);
283 	PJDLOG_ASSERT(nfds > 0);
284 	PJDLOG_ASSERT(fds != NULL);
285 
286 	bzero(&msg, sizeof(msg));
287 	bzero(&iov, sizeof(iov));
288 
289 	/*
290 	 * XXX: Look into cred_send function for more details.
291 	 */
292 	iov.iov_base = &dummy;
293 	iov.iov_len = sizeof(dummy);
294 
295 	msg.msg_iov = &iov;
296 	msg.msg_iovlen = 1;
297 	msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
298 	msg.msg_control = calloc(1, msg.msg_controllen);
299 	if (msg.msg_control == NULL)
300 		return (-1);
301 
302 	ret = -1;
303 
304 	if (msg_recv(sock, &msg) == -1)
305 		goto end;
306 
307 	i = 0;
308 	cmsg = CMSG_FIRSTHDR(&msg);
309 	while (cmsg && i < nfds) {
310 		unsigned int n;
311 
312 		if (cmsg->cmsg_level != SOL_SOCKET ||
313 		    cmsg->cmsg_type != SCM_RIGHTS) {
314 			errno = EINVAL;
315 			break;
316 		}
317 		n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
318 		if (i + n > nfds) {
319 			errno = EINVAL;
320 			break;
321 		}
322 		bcopy(CMSG_DATA(cmsg), fds + i, sizeof(int) * n);
323 		cmsg = CMSG_NXTHDR(&msg, cmsg);
324 		i += n;
325 	}
326 
327 	if (cmsg != NULL || i < nfds) {
328 		unsigned int last;
329 
330 		/*
331 		 * We need to close all received descriptors, even if we have
332 		 * different control message (eg. SCM_CREDS) in between.
333 		 */
334 		last = i;
335 		for (i = 0; i < last; i++) {
336 			if (fds[i] >= 0) {
337 				close(fds[i]);
338 			}
339 		}
340 		errno = EINVAL;
341 		goto end;
342 	}
343 
344 #ifndef MSG_CMSG_CLOEXEC
345 	/*
346 	 * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the
347 	 * close-on-exec flag atomically, but we still want to set it for
348 	 * consistency.
349 	 */
350 	for (i = 0; i < nfds; i++) {
351 		(void) fcntl(fds[i], F_SETFD, FD_CLOEXEC);
352 	}
353 #endif
354 
355 	ret = 0;
356 end:
357 	serrno = errno;
358 	free(msg.msg_control);
359 	errno = serrno;
360 	return (ret);
361 }
362 
363 int
364 fd_recv(int sock, int *fds, size_t nfds)
365 {
366 	unsigned int i, step, j;
367 	int ret, serrno;
368 
369 	if (nfds == 0 || fds == NULL) {
370 		errno = EINVAL;
371 		return (-1);
372 	}
373 
374 	ret = i = step = 0;
375 	while (i < nfds) {
376 		if (PKG_MAX_SIZE < nfds - i)
377 			step = PKG_MAX_SIZE;
378 		else
379 			step = nfds - i;
380 		ret = fd_package_recv(sock, fds + i, step);
381 		if (ret != 0) {
382 			/* Close all received descriptors. */
383 			serrno = errno;
384 			for (j = 0; j < i; j++)
385 				close(fds[j]);
386 			errno = serrno;
387 			break;
388 		}
389 		i += step;
390 	}
391 
392 	return (ret);
393 }
394 
395 int
396 fd_send(int sock, const int *fds, size_t nfds)
397 {
398 	unsigned int i, step;
399 	int ret;
400 
401 	if (nfds == 0 || fds == NULL) {
402 		errno = EINVAL;
403 		return (-1);
404 	}
405 
406 	ret = i = step = 0;
407 	while (i < nfds) {
408 		if (PKG_MAX_SIZE < nfds - i)
409 			step = PKG_MAX_SIZE;
410 		else
411 			step = nfds - i;
412 		ret = fd_package_send(sock, fds + i, step);
413 		if (ret != 0)
414 			break;
415 		i += step;
416 	}
417 
418 	return (ret);
419 }
420 
421 int
422 buf_send(int sock, void *buf, size_t size)
423 {
424 	ssize_t done;
425 	unsigned char *ptr;
426 
427 	PJDLOG_ASSERT(sock >= 0);
428 	PJDLOG_ASSERT(size > 0);
429 	PJDLOG_ASSERT(buf != NULL);
430 
431 	ptr = buf;
432 	do {
433 		fd_wait(sock, false);
434 		done = send(sock, ptr, size, 0);
435 		if (done == -1) {
436 			if (errno == EINTR)
437 				continue;
438 			return (-1);
439 		} else if (done == 0) {
440 			errno = ENOTCONN;
441 			return (-1);
442 		}
443 		size -= done;
444 		ptr += done;
445 	} while (size > 0);
446 
447 	return (0);
448 }
449 
450 int
451 buf_recv(int sock, void *buf, size_t size, int flags)
452 {
453 	ssize_t done;
454 	unsigned char *ptr;
455 
456 	PJDLOG_ASSERT(sock >= 0);
457 	PJDLOG_ASSERT(buf != NULL);
458 
459 	ptr = buf;
460 	while (size > 0) {
461 		fd_wait(sock, true);
462 		done = recv(sock, ptr, size, flags);
463 		if (done == -1) {
464 			if (errno == EINTR)
465 				continue;
466 			return (-1);
467 		} else if (done == 0) {
468 			errno = ENOTCONN;
469 			return (-1);
470 		}
471 		size -= done;
472 		ptr += done;
473 	}
474 
475 	return (0);
476 }
477