1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2001 Wayne Boyer International Business Machines
4 * Copyright (c) Linux Test Project, 2002-2022
5 * Copyright (c) 2023 Wei Gao <wegao@suse.com>
6 */
7
8/*\
9 * [Description]
10 *
11 * Verify that recvmsg() returns the proper errno for various failure cases.
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <sys/wait.h>
17#include "tst_test.h"
18
19#define MSG "from recvmsg01 server"
20#define BUF_SIZE 1024
21#define CONTROL_LEN (128 * 1024)
22
23static char recv_buf[BUF_SIZE], cbuf[BUF_SIZE];
24static int sock;
25static struct sockaddr_in sin1, from;
26static struct sockaddr_un sun1;
27static struct msghdr msgdat;
28static struct cmsghdr *control;
29static int controllen;
30static struct iovec iov[1];
31static int sfd;			/* shared between do_child and start_server */
32static int ufd;			/* shared between do_child and start_server */
33static pid_t pid;
34static char tmpsunpath[BUF_SIZE];
35
36static void setup_all(void);
37static void setup_invalid_sock(int);
38static void setup_valid_sock(int);
39static void setup_valid_msg_control(int);
40static void setup_large_msg_control(int);
41static void cleanup_all(void);
42static void cleanup_invalid_sock(int);
43static void cleanup_close_sock(int);
44static void cleanup_reset_all(int);
45static void do_child(void);
46static pid_t start_server(struct sockaddr_in *, struct sockaddr_un *);
47
48static struct tcase {
49	int domain;
50	int type;
51	int protocol;
52	struct iovec *iov;
53	int iovcnt;
54	void *recv_buf;
55	int buflen;
56	struct msghdr *msg;
57	unsigned int flags;
58	struct sockaddr *from;
59	int fromlen;
60	int exp_errno;
61	void (*setup)(int n);
62	void (*cleanup)(int n);
63	char *desc;
64} tcases[] = {
65	{
66		.domain = PF_INET,
67		.type = SOCK_STREAM,
68		.iov = iov,
69		.iovcnt = 1,
70		.recv_buf = recv_buf,
71		.buflen = sizeof(recv_buf),
72		.msg = &msgdat,
73		.from = (struct sockaddr *)&from,
74		.fromlen = sizeof(from),
75		.exp_errno = EBADF,
76		.setup = setup_invalid_sock,
77		.cleanup = cleanup_invalid_sock,
78		.desc = "bad file descriptor",
79	},
80	{
81		.domain = PF_INET,
82		.type = SOCK_STREAM,
83		.iov = iov,
84		.iovcnt = 1,
85		.recv_buf = (void *)recv_buf,
86		.buflen = sizeof(recv_buf),
87		.msg = &msgdat,
88		.from = (struct sockaddr *)&from,
89		.fromlen = sizeof(from),
90		.exp_errno = ENOTSOCK,
91		.setup = setup_invalid_sock,
92		.cleanup = cleanup_invalid_sock,
93		.desc = "invalid socket",
94	},
95	{
96		.domain = PF_INET,
97		.type = SOCK_STREAM,
98		.iov = iov,
99		.iovcnt = 1,
100		.recv_buf = (void *)recv_buf,
101		.buflen = sizeof(recv_buf),
102		.msg = &msgdat,
103		.flags = -1,
104		.from = (struct sockaddr *)&from,
105		.fromlen = -1,
106		.exp_errno = EINVAL,
107		.setup = setup_valid_sock,
108		.cleanup = cleanup_close_sock,
109		.desc = "invalid socket length",
110	},
111	{
112		.domain = PF_INET,
113		.type = SOCK_STREAM,
114		.iov = iov,
115		.iovcnt = 1,
116		.recv_buf = (void *)-1,
117		.buflen = sizeof(recv_buf),
118		.msg = &msgdat,
119		.from = (struct sockaddr *)&from,
120		.fromlen = sizeof(from),
121		.exp_errno = EFAULT,
122		.setup = setup_valid_sock,
123		.cleanup = cleanup_close_sock,
124		.desc = "invalid recv buffer",
125	},
126	{
127		.domain = PF_INET,
128		.type = SOCK_STREAM,
129		.iovcnt = 1,
130		.recv_buf = recv_buf,
131		.buflen = sizeof(recv_buf),
132		.msg = &msgdat,
133		.from = (struct sockaddr *)&from,
134		.fromlen = sizeof(from),
135		.exp_errno = EFAULT,
136		.setup = setup_valid_sock,
137		.cleanup = cleanup_close_sock,
138		.desc = "invalid iovec buffer",
139	},
140	{
141		.domain = PF_INET,
142		.type = SOCK_STREAM,
143		.iov = iov,
144		.iovcnt = -1,
145		.recv_buf = recv_buf,
146		.buflen = sizeof(recv_buf),
147		.msg = &msgdat,
148		.from = (struct sockaddr *)&from,
149		.fromlen = sizeof(from),
150		.exp_errno = EMSGSIZE,
151		.setup = setup_valid_sock,
152		.cleanup = cleanup_close_sock,
153		.desc = "invalid iovec count",
154	},
155	{
156		.domain = PF_INET,
157		.type = SOCK_STREAM,
158		.iov = iov,
159		.iovcnt = 1,
160		.recv_buf = recv_buf,
161		.buflen = sizeof(recv_buf),
162		.msg = &msgdat,
163		.from = (struct sockaddr *)&from,
164		.fromlen = sizeof(from),
165		.setup = setup_valid_msg_control,
166		.cleanup = cleanup_reset_all,
167		.desc = "permission reception",
168	},
169	{
170		.domain = PF_INET,
171		.type = SOCK_STREAM,
172		.iov = iov,
173		.iovcnt = 1,
174		.recv_buf = recv_buf,
175		.buflen = sizeof(recv_buf),
176		.msg = &msgdat,
177		.flags = MSG_OOB,
178		.from = (struct sockaddr *)&from,
179		.fromlen = sizeof(from),
180		.exp_errno = EINVAL,
181		.setup = setup_valid_sock,
182		.cleanup = cleanup_close_sock,
183		.desc = "invalid MSG_OOB flag set",
184	},
185	{
186		.domain = PF_INET,
187		.type = SOCK_STREAM,
188		.iov = iov,
189		.iovcnt = 1,
190		.recv_buf = recv_buf,
191		.buflen = sizeof(recv_buf),
192		.msg = &msgdat,
193		.flags = MSG_ERRQUEUE,
194		.from = (struct sockaddr *)&from,
195		.fromlen = sizeof(from),
196		.exp_errno = EAGAIN,
197		.setup = setup_valid_sock,
198		.cleanup = cleanup_close_sock,
199		.desc = "invalid MSG_ERRQUEUE flag set",
200	},
201	{
202		.domain = PF_INET,
203		.type = SOCK_STREAM,
204		.iov = iov,
205		.iovcnt = 1,
206		.recv_buf = recv_buf,
207		.buflen = sizeof(recv_buf),
208		.msg = &msgdat,
209		.from = (struct sockaddr *)&from,
210		.fromlen = sizeof(from),
211		.setup = setup_large_msg_control,
212		.cleanup = cleanup_reset_all,
213		.desc = "large cmesg length",
214	},
215
216};
217
218static void run(unsigned int n)
219{
220	struct tcase *tc = &tcases[n];
221	int ret = tc->exp_errno ? -1 : 0;
222
223	if ((tst_kvercmp(3, 17, 0) < 0)
224			&& (tc->flags & MSG_ERRQUEUE)
225			&& (tc->type & SOCK_STREAM)) {
226		tst_res(TCONF, "MSG_ERRQUEUE requires kernel >= 3.17");
227		return;
228	}
229
230	setup_all();
231	tc->setup(n);
232
233	iov[0].iov_base = tc->recv_buf;
234	iov[0].iov_len = tc->buflen;
235	msgdat.msg_name = tc->from;
236	msgdat.msg_namelen = tc->fromlen;
237	msgdat.msg_iov = tc->iov;
238	msgdat.msg_iovlen = tc->iovcnt;
239	msgdat.msg_control = control;
240	msgdat.msg_controllen = controllen;
241	msgdat.msg_flags = 0;
242
243	TEST(recvmsg(sock, tc->msg, tc->flags));
244	if (TST_RET >= 0)
245		TST_RET = 0;
246
247	if (TST_RET != ret) {
248		tst_res(TFAIL | TTERRNO, "%s: expected %d, returned %ld",
249			tc->desc, ret, TST_RET);
250	} else if (TST_ERR != tc->exp_errno) {
251		tst_res(TFAIL | TTERRNO,
252			"%s: expected %s",
253			tc->desc, tst_strerrno(tc->exp_errno));
254	} else {
255		tst_res(TPASS, "%s passed", tc->desc);
256	}
257
258	tc->cleanup(n);
259	cleanup_all();
260}
261
262
263static void setup_all(void)
264{
265	int tfd;
266
267	sun1.sun_family = AF_UNIX;
268
269	(void)strcpy(tmpsunpath, "udsockXXXXXX");
270	tfd = mkstemp(tmpsunpath);
271	SAFE_CLOSE(tfd);
272	SAFE_UNLINK(tmpsunpath);
273	(void)strcpy(sun1.sun_path, tmpsunpath);
274	SAFE_SIGNAL(SIGPIPE, SIG_IGN);
275	pid = start_server(&sin1, &sun1);
276}
277
278static void cleanup_all(void)
279{
280	if (pid > 0) {
281		(void)kill(pid, SIGKILL);	/* kill server */
282		wait(NULL);
283	}
284
285	if (tmpsunpath[0] != '\0')
286		(void)SAFE_UNLINK(tmpsunpath);
287}
288
289static void setup_invalid_sock(int n)
290{
291	if (tcases[n].exp_errno == EBADF)
292		sock = 400;	/* anything not an open file */
293	else
294		sock = SAFE_OPEN("/dev/null", O_WRONLY);
295}
296
297static void cleanup_invalid_sock(int n)
298{
299	if (tcases[n].exp_errno == EBADF)
300		sock = -1;
301	else
302		SAFE_CLOSE(sock);
303}
304
305static void setup_valid_sock(int n)
306{
307	fd_set rdfds;
308	struct timeval timeout;
309
310	sock = SAFE_SOCKET(tcases[n].domain, tcases[n].type, tcases[n].protocol);
311
312	if (tcases[n].type == SOCK_STREAM) {
313		if (tcases[n].domain == PF_INET) {
314			SAFE_CONNECT(sock, (struct sockaddr *)&sin1, sizeof(sin1));
315			/* Wait for something to be readable, else we won't detect EFAULT on recv */
316			FD_ZERO(&rdfds);
317			FD_SET(sock, &rdfds);
318			timeout.tv_sec = 2;
319			timeout.tv_usec = 0;
320			n = select(sock + 1, &rdfds, 0, 0, &timeout);
321
322			if (n != 1 || !FD_ISSET(sock, &rdfds))
323				tst_brk(TBROK, "no message ready in %d sec", (int)timeout.tv_sec);
324
325		} else if (tcases[n].domain == PF_UNIX) {
326			SAFE_CONNECT(sock, (struct sockaddr *)&sun1, sizeof(sun1));
327		}
328	}
329}
330
331static void setup_valid_msg_control(int n)
332{
333	setup_valid_sock(n);
334	SAFE_SEND(1, sock, "R", 1, 0);
335	control = (struct cmsghdr *)cbuf;
336	controllen = control->cmsg_len = sizeof(cbuf);
337}
338
339static void setup_large_msg_control(int n)
340{
341	setup_valid_msg_control(n);
342	controllen = CONTROL_LEN;
343}
344
345static void cleanup_close_sock(int n LTP_ATTRIBUTE_UNUSED)
346{
347	SAFE_CLOSE(sock);
348}
349
350static void cleanup_reset_all(int n LTP_ATTRIBUTE_UNUSED)
351{
352	SAFE_CLOSE(sock);
353
354	control = 0;
355	controllen = 0;
356}
357
358pid_t start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun)
359{
360	pid_t pid;
361	socklen_t slen = sizeof(*ssin);
362
363	ssin->sin_family = AF_INET;
364	ssin->sin_port = 0; /* pick random free port */
365	ssin->sin_addr.s_addr = INADDR_ANY;
366
367	/* set up inet socket */
368	sfd = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0);
369	SAFE_BIND(sfd, (struct sockaddr *)ssin, sizeof(*ssin));
370	SAFE_LISTEN(sfd, 10);
371	SAFE_GETSOCKNAME(sfd, (struct sockaddr *)ssin, &slen);
372
373	/* set up UNIX-domain socket */
374	ufd = SAFE_SOCKET(PF_UNIX, SOCK_STREAM, 0);
375	SAFE_BIND(ufd, (struct sockaddr *)ssun, sizeof(*ssun));
376	SAFE_LISTEN(ufd, 10);
377
378	pid = SAFE_FORK();
379	if (!pid) {
380		do_child();
381		exit(1);
382	}
383
384	SAFE_CLOSE(sfd);
385	SAFE_CLOSE(ufd);
386
387	return pid;
388}
389
390/* for permission test */
391static void sender(int fd)
392{
393	struct msghdr mh = {};
394	struct cmsghdr *control;
395	char tmpfn[BUF_SIZE] = "";
396	char snd_cbuf[BUF_SIZE] = "";
397	int tfd;
398
399	(void)strcpy(tmpfn, "smtXXXXXX");
400	tfd = mkstemp(tmpfn);
401	if (tfd < 0)
402		return;
403
404	/* set up cmsghdr */
405	control = (struct cmsghdr *)snd_cbuf;
406	control->cmsg_len = sizeof(struct cmsghdr) + 4;
407	control->cmsg_level = SOL_SOCKET;
408	control->cmsg_type = SCM_RIGHTS;
409	*(int *)CMSG_DATA(control) = tfd;
410
411	/* set up msghdr */
412	iov[0].iov_base = MSG;
413	iov[0].iov_len = sizeof(MSG);
414	mh.msg_iov = iov;
415	mh.msg_iovlen = 1;
416	mh.msg_flags = 0;
417	mh.msg_control = control;
418	mh.msg_controllen = control->cmsg_len;
419
420	/* do it */
421	SAFE_SENDMSG(sizeof(MSG), fd, &mh, 0);
422	SAFE_CLOSE(tfd);
423	(void)SAFE_UNLINK(tmpfn);
424}
425
426static void do_child(void)
427{
428	struct sockaddr_in fsin;
429	struct sockaddr_un fsun;
430	fd_set afds, rfds;
431	int nfds, fd;
432
433	FD_ZERO(&afds);
434	FD_SET(sfd, &afds);
435	FD_SET(ufd, &afds);
436
437	nfds = MAX(sfd + 1, ufd + 1);
438
439	/* accept connections until killed */
440	while (1) {
441		socklen_t fromlen;
442
443		memcpy(&rfds, &afds, sizeof(rfds));
444
445		if (select(nfds, &rfds, NULL, NULL,
446			   NULL) < 0) {
447			if (errno != EINTR) {
448				perror("server select");
449				exit(1);
450			}
451			continue;
452		}
453		if (FD_ISSET(sfd, &rfds)) {
454			int newfd;
455
456			fromlen = sizeof(fsin);
457			newfd = SAFE_ACCEPT(sfd, (struct sockaddr *)&fsin, &fromlen);
458			if (newfd >= 0) {
459				FD_SET(newfd, &afds);
460				nfds = MAX(nfds, newfd + 1);
461				/* send something back */
462				SAFE_SEND(1, newfd, "hi", 2, 0);
463			}
464		}
465		if (FD_ISSET(ufd, &rfds)) {
466			int newfd;
467
468			fromlen = sizeof(fsun);
469			newfd = SAFE_ACCEPT(ufd, (struct sockaddr *)&fsun, &fromlen);
470			if (newfd >= 0) {
471				FD_SET(newfd, &afds);
472				nfds = MAX(nfds, newfd + 1);
473			}
474		}
475		for (fd = 0; fd < nfds; ++fd)
476			if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) {
477				char rbuf[BUF_SIZE];
478
479				TEST(read(fd, rbuf, sizeof(rbuf)));
480				if (TST_RET > 0 && rbuf[0] == 'R')
481					sender(fd);
482				if (TST_RET == 0 || (TST_RET < 0 && TST_ERR != EINTR)) {
483					close(fd);
484					FD_CLR(fd, &afds);
485				}
486			}
487	}
488}
489
490static struct tst_test test = {
491	.test = run,
492	.tcnt = ARRAY_SIZE(tcases),
493	.forks_child = 1,
494	.needs_tmpdir = 1,
495};
496