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
23 static char recv_buf[BUF_SIZE], cbuf[BUF_SIZE];
24 static int sock;
25 static struct sockaddr_in sin1, from;
26 static struct sockaddr_un sun1;
27 static struct msghdr msgdat;
28 static struct cmsghdr *control;
29 static int controllen;
30 static struct iovec iov[1];
31 static int sfd; /* shared between do_child and start_server */
32 static int ufd; /* shared between do_child and start_server */
33 static pid_t pid;
34 static char tmpsunpath[BUF_SIZE];
35
36 static void setup_all(void);
37 static void setup_invalid_sock(int);
38 static void setup_valid_sock(int);
39 static void setup_valid_msg_control(int);
40 static void setup_large_msg_control(int);
41 static void cleanup_all(void);
42 static void cleanup_invalid_sock(int);
43 static void cleanup_close_sock(int);
44 static void cleanup_reset_all(int);
45 static void do_child(void);
46 static pid_t start_server(struct sockaddr_in *, struct sockaddr_un *);
47
48 static 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
run(unsigned int n)218 static void run(unsigned int n)
219 {
220 struct tcase *tc = &tcases[n];
221 int ret = tc->exp_errno ? -1 : 0;
222
223 setup_all();
224 tc->setup(n);
225
226 iov[0].iov_base = tc->recv_buf;
227 iov[0].iov_len = tc->buflen;
228 msgdat.msg_name = tc->from;
229 msgdat.msg_namelen = tc->fromlen;
230 msgdat.msg_iov = tc->iov;
231 msgdat.msg_iovlen = tc->iovcnt;
232 msgdat.msg_control = control;
233 msgdat.msg_controllen = controllen;
234 msgdat.msg_flags = 0;
235
236 TEST(recvmsg(sock, tc->msg, tc->flags));
237 if (TST_RET >= 0)
238 TST_RET = 0;
239
240 if (TST_RET != ret) {
241 tst_res(TFAIL | TTERRNO, "%s: expected %d, returned %ld",
242 tc->desc, ret, TST_RET);
243 } else if (TST_ERR != tc->exp_errno) {
244 tst_res(TFAIL | TTERRNO,
245 "%s: expected %s",
246 tc->desc, tst_strerrno(tc->exp_errno));
247 } else {
248 tst_res(TPASS, "%s passed", tc->desc);
249 }
250
251 tc->cleanup(n);
252 cleanup_all();
253 }
254
255
setup_all(void)256 static void setup_all(void)
257 {
258 int tfd;
259
260 sun1.sun_family = AF_UNIX;
261
262 (void)strcpy(tmpsunpath, "udsockXXXXXX");
263 tfd = mkstemp(tmpsunpath);
264 SAFE_CLOSE(tfd);
265 SAFE_UNLINK(tmpsunpath);
266 (void)strcpy(sun1.sun_path, tmpsunpath);
267 SAFE_SIGNAL(SIGPIPE, SIG_IGN);
268 pid = start_server(&sin1, &sun1);
269 }
270
cleanup_all(void)271 static void cleanup_all(void)
272 {
273 if (pid > 0) {
274 (void)kill(pid, SIGKILL); /* kill server */
275 wait(NULL);
276 }
277
278 if (tmpsunpath[0] != '\0')
279 (void)SAFE_UNLINK(tmpsunpath);
280 }
281
setup_invalid_sock(int n)282 static void setup_invalid_sock(int n)
283 {
284 if (tcases[n].exp_errno == EBADF)
285 sock = 400; /* anything not an open file */
286 else
287 sock = SAFE_OPEN("/dev/null", O_WRONLY);
288 }
289
cleanup_invalid_sock(int n)290 static void cleanup_invalid_sock(int n)
291 {
292 if (tcases[n].exp_errno == EBADF)
293 sock = -1;
294 else
295 SAFE_CLOSE(sock);
296 }
297
setup_valid_sock(int n)298 static void setup_valid_sock(int n)
299 {
300 fd_set rdfds;
301 struct timeval timeout;
302
303 sock = SAFE_SOCKET(tcases[n].domain, tcases[n].type, tcases[n].protocol);
304
305 if (tcases[n].type == SOCK_STREAM) {
306 if (tcases[n].domain == PF_INET) {
307 SAFE_CONNECT(sock, (struct sockaddr *)&sin1, sizeof(sin1));
308 /* Wait for something to be readable, else we won't detect EFAULT on recv */
309 FD_ZERO(&rdfds);
310 FD_SET(sock, &rdfds);
311 timeout.tv_sec = 2;
312 timeout.tv_usec = 0;
313 n = select(sock + 1, &rdfds, 0, 0, &timeout);
314
315 if (n != 1 || !FD_ISSET(sock, &rdfds))
316 tst_brk(TBROK, "no message ready in %d sec", (int)timeout.tv_sec);
317
318 } else if (tcases[n].domain == PF_UNIX) {
319 SAFE_CONNECT(sock, (struct sockaddr *)&sun1, sizeof(sun1));
320 }
321 }
322 }
323
setup_valid_msg_control(int n)324 static void setup_valid_msg_control(int n)
325 {
326 setup_valid_sock(n);
327 SAFE_SEND(1, sock, "R", 1, 0);
328 control = (struct cmsghdr *)cbuf;
329 controllen = control->cmsg_len = sizeof(cbuf);
330 }
331
setup_large_msg_control(int n)332 static void setup_large_msg_control(int n)
333 {
334 setup_valid_msg_control(n);
335 controllen = CONTROL_LEN;
336 }
337
cleanup_close_sock(int n LTP_ATTRIBUTE_UNUSED)338 static void cleanup_close_sock(int n LTP_ATTRIBUTE_UNUSED)
339 {
340 SAFE_CLOSE(sock);
341 }
342
cleanup_reset_all(int n LTP_ATTRIBUTE_UNUSED)343 static void cleanup_reset_all(int n LTP_ATTRIBUTE_UNUSED)
344 {
345 SAFE_CLOSE(sock);
346
347 control = 0;
348 controllen = 0;
349 }
350
start_server(struct sockaddr_in * ssin,struct sockaddr_un * ssun)351 pid_t start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun)
352 {
353 pid_t pid;
354 socklen_t slen = sizeof(*ssin);
355
356 ssin->sin_family = AF_INET;
357 ssin->sin_port = 0; /* pick random free port */
358 ssin->sin_addr.s_addr = INADDR_ANY;
359
360 /* set up inet socket */
361 sfd = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0);
362 SAFE_BIND(sfd, (struct sockaddr *)ssin, sizeof(*ssin));
363 SAFE_LISTEN(sfd, 10);
364 SAFE_GETSOCKNAME(sfd, (struct sockaddr *)ssin, &slen);
365
366 /* set up UNIX-domain socket */
367 ufd = SAFE_SOCKET(PF_UNIX, SOCK_STREAM, 0);
368 SAFE_BIND(ufd, (struct sockaddr *)ssun, sizeof(*ssun));
369 SAFE_LISTEN(ufd, 10);
370
371 pid = SAFE_FORK();
372 if (!pid) {
373 do_child();
374 exit(1);
375 }
376
377 SAFE_CLOSE(sfd);
378 SAFE_CLOSE(ufd);
379
380 return pid;
381 }
382
383 /* for permission test */
sender(int fd)384 static void sender(int fd)
385 {
386 struct msghdr mh = {};
387 struct cmsghdr *control;
388 char tmpfn[BUF_SIZE] = "";
389 char snd_cbuf[BUF_SIZE] = "";
390 int tfd;
391
392 (void)strcpy(tmpfn, "smtXXXXXX");
393 tfd = mkstemp(tmpfn);
394 if (tfd < 0)
395 return;
396
397 /* set up cmsghdr */
398 control = (struct cmsghdr *)snd_cbuf;
399 control->cmsg_len = sizeof(struct cmsghdr) + 4;
400 control->cmsg_level = SOL_SOCKET;
401 control->cmsg_type = SCM_RIGHTS;
402 *(int *)CMSG_DATA(control) = tfd;
403
404 /* set up msghdr */
405 iov[0].iov_base = MSG;
406 iov[0].iov_len = sizeof(MSG);
407 mh.msg_iov = iov;
408 mh.msg_iovlen = 1;
409 mh.msg_flags = 0;
410 mh.msg_control = control;
411 mh.msg_controllen = control->cmsg_len;
412
413 /* do it */
414 SAFE_SENDMSG(sizeof(MSG), fd, &mh, 0);
415 SAFE_CLOSE(tfd);
416 (void)SAFE_UNLINK(tmpfn);
417 }
418
do_child(void)419 static void do_child(void)
420 {
421 struct sockaddr_in fsin;
422 struct sockaddr_un fsun;
423 fd_set afds, rfds;
424 int nfds, fd;
425
426 FD_ZERO(&afds);
427 FD_SET(sfd, &afds);
428 FD_SET(ufd, &afds);
429
430 nfds = MAX(sfd + 1, ufd + 1);
431
432 /* accept connections until killed */
433 while (1) {
434 socklen_t fromlen;
435
436 memcpy(&rfds, &afds, sizeof(rfds));
437
438 if (select(nfds, &rfds, NULL, NULL,
439 NULL) < 0) {
440 if (errno != EINTR) {
441 perror("server select");
442 exit(1);
443 }
444 continue;
445 }
446 if (FD_ISSET(sfd, &rfds)) {
447 int newfd;
448
449 fromlen = sizeof(fsin);
450 newfd = SAFE_ACCEPT(sfd, (struct sockaddr *)&fsin, &fromlen);
451 if (newfd >= 0) {
452 FD_SET(newfd, &afds);
453 nfds = MAX(nfds, newfd + 1);
454 /* send something back */
455 SAFE_SEND(1, newfd, "hi", 2, 0);
456 }
457 }
458 if (FD_ISSET(ufd, &rfds)) {
459 int newfd;
460
461 fromlen = sizeof(fsun);
462 newfd = SAFE_ACCEPT(ufd, (struct sockaddr *)&fsun, &fromlen);
463 if (newfd >= 0) {
464 FD_SET(newfd, &afds);
465 nfds = MAX(nfds, newfd + 1);
466 }
467 }
468 for (fd = 0; fd < nfds; ++fd)
469 if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) {
470 char rbuf[BUF_SIZE];
471
472 TEST(read(fd, rbuf, sizeof(rbuf)));
473 if (TST_RET > 0 && rbuf[0] == 'R')
474 sender(fd);
475 if (TST_RET == 0 || (TST_RET < 0 && TST_ERR != EINTR)) {
476 close(fd);
477 FD_CLR(fd, &afds);
478 }
479 }
480 }
481 }
482
483 static struct tst_test test = {
484 .test = run,
485 .tcnt = ARRAY_SIZE(tcases),
486 .forks_child = 1,
487 .needs_tmpdir = 1,
488 };
489