1/* Smoke test for SCM_RIGHTS.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* This test passes a file descriptor from a subprocess to the parent
20 process, using recvmsg/sendmsg or recvmmsg/sendmmsg. */
21
22#include <fcntl.h>
23#include <signal.h>
24#include <stdbool.h>
25#include <string.h>
26#include <support/check.h>
27#include <support/xunistd.h>
28#include <sys/socket.h>
29#include <sys/wait.h>
30#include <unistd.h>
31
32/* String sent over the socket. */
33static char DATA[] = "descriptor";
34
35/* Path that is to be opened and sent over the socket. */
36#define PATH "/etc"
37
38/* True if sendmmsg/recvmmsg is to be used. */
39static bool use_multi_call;
40
41/* The pair of sockets used for coordination. The subprocess uses
42 sockets[1]. */
43static int sockets[2];
44
45/* Subprocess side of one send/receive test. */
46_Noreturn static void
47subprocess (void)
48{
49 /* The file descriptor to send. */
50 int fd = xopen (PATH, O_RDONLY, 0);
51
52 struct iovec iov = { .iov_base = DATA, .iov_len = sizeof (DATA) };
53 union
54 {
55 struct cmsghdr header;
56 char bytes[CMSG_SPACE (sizeof (int))];
57 } cmsg_storage;
58 struct mmsghdr mmhdr =
59 {
60 .msg_hdr =
61 {
62 .msg_iov = &iov,
63 .msg_iovlen = 1,
64 .msg_control = cmsg_storage.bytes,
65 .msg_controllen = sizeof (cmsg_storage),
66 },
67 };
68
69 /* Configure the file descriptor for sending. */
70 struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
71 cmsg->cmsg_level = SOL_SOCKET;
72 cmsg->cmsg_type = SCM_RIGHTS;
73 cmsg->cmsg_len = CMSG_LEN (sizeof (int));
74 memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
75 mmhdr.msg_hdr.msg_controllen = cmsg->cmsg_len;
76
77 /* Perform the send operation. */
78 int ret;
79 if (use_multi_call)
80 {
81 ret = sendmmsg (fd: sockets[1], vmessages: &mmhdr, vlen: 1, flags: 0);
82 if (ret >= 0)
83 ret = mmhdr.msg_len;
84 }
85 else
86 ret = sendmsg (fd: sockets[1], message: &mmhdr.msg_hdr, flags: 0);
87 TEST_COMPARE (ret, sizeof (DATA));
88
89 xclose (fd);
90
91 /* Stop the process from exiting. */
92 while (true)
93 pause ();
94}
95
96/* Performs one send/receive test. */
97static void
98one_test (void)
99{
100 TEST_COMPARE (socketpair (AF_UNIX, SOCK_STREAM, 0, sockets), 0);
101
102 pid_t pid = xfork ();
103 if (pid == 0)
104 subprocess ();
105
106 char data_storage[sizeof (DATA) + 1];
107 struct iovec iov =
108 {
109 .iov_base = data_storage,
110 .iov_len = sizeof (data_storage)
111 };
112 union
113 {
114 struct cmsghdr header;
115 char bytes[CMSG_SPACE (sizeof (int))];
116 } cmsg_storage;
117 struct mmsghdr mmhdr =
118 {
119 .msg_hdr =
120 {
121 .msg_iov = &iov,
122 .msg_iovlen = 1,
123 .msg_control = cmsg_storage.bytes,
124 .msg_controllen = sizeof (cmsg_storage),
125 },
126 };
127
128 /* Set up the space for receiving the file descriptor. */
129 struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
130 cmsg->cmsg_level = SOL_SOCKET;
131 cmsg->cmsg_type = SCM_RIGHTS;
132 cmsg->cmsg_len = CMSG_LEN (sizeof (int));
133 mmhdr.msg_hdr.msg_controllen = cmsg->cmsg_len;
134
135 /* Perform the receive operation. */
136 int ret;
137 if (use_multi_call)
138 {
139 ret = recvmmsg (fd: sockets[0], vmessages: &mmhdr, vlen: 1, flags: 0, NULL);
140 if (ret >= 0)
141 ret = mmhdr.msg_len;
142 }
143 else
144 ret = recvmsg (fd: sockets[0], message: &mmhdr.msg_hdr, flags: 0);
145 TEST_COMPARE (ret, sizeof (DATA));
146 TEST_COMPARE_BLOB (data_storage, sizeof (DATA), DATA, sizeof (DATA));
147
148 /* Extract the file descriptor. */
149 TEST_VERIFY (CMSG_FIRSTHDR (&mmhdr.msg_hdr) != NULL);
150 TEST_COMPARE (CMSG_FIRSTHDR (&mmhdr.msg_hdr)->cmsg_len,
151 CMSG_LEN (sizeof (int)));
152 TEST_VERIFY (&cmsg_storage.header == CMSG_FIRSTHDR (&mmhdr.msg_hdr));
153 int fd;
154 memcpy (&fd, CMSG_DATA (CMSG_FIRSTHDR (&mmhdr.msg_hdr)), sizeof (fd));
155
156 /* Verify the received file descriptor. */
157 TEST_VERIFY (fd > 2);
158 struct stat64 st_fd;
159 TEST_COMPARE (fstat64 (fd, &st_fd), 0);
160 struct stat64 st_path;
161 TEST_COMPARE (stat64 (PATH, &st_path), 0);
162 TEST_COMPARE (st_fd.st_ino, st_path.st_ino);
163 TEST_COMPARE (st_fd.st_dev, st_path.st_dev);
164 xclose (fd);
165
166 /* Terminate the subprocess. */
167 TEST_COMPARE (kill (pid, SIGUSR1), 0);
168 int status;
169 TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
170 TEST_VERIFY (WIFSIGNALED (status));
171 TEST_COMPARE (WTERMSIG (status), SIGUSR1);
172
173 xclose (sockets[0]);
174 xclose (sockets[1]);
175}
176
177static int
178do_test (void)
179{
180 one_test ();
181 use_multi_call = true;
182 one_test ();
183 return 0;
184}
185
186#include <support/test-driver.c>
187

source code of glibc/sysdeps/unix/sysv/linux/tst-scm_rights.c