1 /* Smoke test for SCM_RIGHTS.
2    Copyright (C) 2021-2022 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.  */
33 static 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.  */
39 static bool use_multi_call;
40 
41 /* The pair of sockets used for coordination.  The subprocess uses
42    sockets[1].  */
43 static int sockets[2];
44 
45 /* Subprocess side of one send/receive test.  */
46 _Noreturn static void
subprocess(void)47 subprocess (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 (sockets[1], &mmhdr, 1, 0);
82       if (ret >= 0)
83         ret = mmhdr.msg_len;
84     }
85   else
86     ret = sendmsg (sockets[1], &mmhdr.msg_hdr, 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.  */
97 static void
one_test(void)98 one_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 (sockets[0], &mmhdr, 1, 0, NULL);
140       if (ret >= 0)
141         ret = mmhdr.msg_len;
142     }
143   else
144     ret = recvmsg (sockets[0], &mmhdr.msg_hdr, 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 
177 static int
do_test(void)178 do_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