1/* Test svc_register/svc_unregister rpcbind interaction (bug 5010).
2 Copyright (C) 2017-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 uses a stub rpcbind server (implemented in a child
20 process using rpcbind_dispatch/run_rpcbind) to check how RPC
21 services are registered and unregistered using the rpcbind
22 protocol. For each subtest, a separate rpcbind test server is
23 spawned and terminated. */
24
25#include <errno.h>
26#include <netinet/in.h>
27#include <rpc/clnt.h>
28#include <rpc/pmap_prot.h>
29#include <rpc/svc.h>
30#include <signal.h>
31#include <support/check.h>
32#include <support/namespace.h>
33#include <support/test-driver.h>
34#include <support/xsocket.h>
35#include <support/xthread.h>
36#include <support/xunistd.h>
37#include <sys/socket.h>
38#include <sys/wait.h>
39#include <unistd.h>
40
41#include <libc-symbols.h>
42#include <shlib-compat.h>
43
44/* These functions are only available as compat symbols. */
45compat_symbol_reference (libc, xdr_pmap, xdr_pmap, GLIBC_2_0);
46compat_symbol_reference (libc, svc_unregister, svc_unregister, GLIBC_2_0);
47
48/* Server callback for the unused RPC service which is registered and
49 unregistered. */
50static void
51server_dispatch (struct svc_req *request, SVCXPRT *transport)
52{
53 FAIL_EXIT1 ("server_dispatch called");
54}
55
56/* The port on which rpcbind listens for incoming requests. */
57static inline struct sockaddr_in
58rpcbind_address (void)
59{
60 return (struct sockaddr_in)
61 {
62 .sin_family = AF_INET,
63 .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
64 .sin_port = htons (PMAPPORT)
65 };
66}
67
68/* Data provided by the test server after running the test, to see
69 that the expected calls (and only those) happened. */
70struct test_state
71{
72 bool_t set_called;
73 bool_t unset_called;
74};
75
76static bool_t
77xdr_test_state (XDR *xdrs, void *data, ...)
78{
79 struct test_state *p = data;
80 return xdr_bool (xdrs, &p->set_called)
81 && xdr_bool (xdrs, &p->unset_called);
82}
83
84enum
85{
86 /* Coordinates of our test service. These numbers are
87 arbitrary. */
88 PROGNUM = 123,
89 VERSNUM = 456,
90
91 /* Extension for this test. */
92 PROC_GET_STATE_AND_EXIT = 10760
93};
94
95/* Dummy implementation of the rpcbind service, with the
96 PROC_GET_STATE_AND_EXIT extension. */
97static void
98rpcbind_dispatch (struct svc_req *request, SVCXPRT *transport)
99{
100 static struct test_state state = { 0, };
101
102 if (test_verbose)
103 printf (format: "info: rpcbind request %lu\n", request->rq_proc);
104
105 switch (request->rq_proc)
106 {
107 case PMAPPROC_SET:
108 case PMAPPROC_UNSET:
109 TEST_VERIFY (state.set_called == (request->rq_proc == PMAPPROC_UNSET));
110 TEST_VERIFY (!state.unset_called);
111
112 struct pmap query = { 0, };
113 TEST_VERIFY
114 (svc_getargs (transport, (xdrproc_t) xdr_pmap, (void *) &query));
115 if (test_verbose)
116 printf (format: " pm_prog=%lu pm_vers=%lu pm_prot=%lu pm_port=%lu\n",
117 query.pm_prog, query.pm_vers, query.pm_prot, query.pm_port);
118 TEST_VERIFY (query.pm_prog == PROGNUM);
119 TEST_VERIFY (query.pm_vers == VERSNUM);
120
121 if (request->rq_proc == PMAPPROC_SET)
122 state.set_called = TRUE;
123 else
124 state.unset_called = TRUE;
125
126 bool_t result = TRUE;
127 TEST_VERIFY (svc_sendreply (transport,
128 (xdrproc_t) xdr_bool, (void *) &result));
129 break;
130
131 case PROC_GET_STATE_AND_EXIT:
132 TEST_VERIFY (svc_sendreply (transport,
133 xdr_test_state, (void *) &state));
134 _exit (0);
135 break;
136
137 default:
138 FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
139 }
140}
141
142/* Run the rpcbind test server. */
143static void
144run_rpcbind (int rpcbind_sock)
145{
146 SVCXPRT *rpcbind_transport = svcudp_create (rpcbind_sock);
147 TEST_VERIFY (svc_register (rpcbind_transport, PMAPPROG, PMAPVERS,
148 rpcbind_dispatch,
149 /* Do not register with rpcbind. */
150 0));
151 svc_run ();
152}
153
154/* Call out to the rpcbind test server to retrieve the test status
155 information. */
156static struct test_state
157get_test_state (void)
158{
159 int socket = RPC_ANYSOCK;
160 struct sockaddr_in address = rpcbind_address ();
161 CLIENT *client = clntudp_create
162 (&address, PMAPPROG, PMAPVERS, (struct timeval) { 1, 0}, &socket);
163 struct test_state result = { 0 };
164 TEST_VERIFY (clnt_call (client, PROC_GET_STATE_AND_EXIT,
165 (xdrproc_t) xdr_void, NULL,
166 xdr_test_state, (void *) &result,
167 ((struct timeval) { 3, 0}))
168 == RPC_SUCCESS);
169 clnt_destroy (client);
170 return result;
171}
172
173/* Used by test_server_thread to receive test parameters. */
174struct test_server_args
175{
176 bool use_rpcbind;
177 bool use_unregister;
178};
179
180/* RPC test server. Used to verify the svc_unregister behavior during
181 thread cleanup. */
182static void *
183test_server_thread (void *closure)
184{
185 struct test_server_args *args = closure;
186 SVCXPRT *transport = svcudp_create (RPC_ANYSOCK);
187 int protocol;
188 if (args->use_rpcbind)
189 protocol = IPPROTO_UDP;
190 else
191 /* Do not register with rpcbind. */
192 protocol = 0;
193 TEST_VERIFY (svc_register (transport, PROGNUM, VERSNUM,
194 server_dispatch, protocol));
195 if (args->use_unregister)
196 svc_unregister (PROGNUM, VERSNUM);
197 SVC_DESTROY (transport);
198 return NULL;
199}
200
201static int
202do_test (void)
203{
204 support_become_root ();
205 support_enter_network_namespace ();
206
207 /* Try to bind to the rpcbind port. */
208 int rpcbind_sock = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
209 {
210 struct sockaddr_in sin = rpcbind_address ();
211 if (bind (fd: rpcbind_sock, addr: (struct sockaddr *) &sin, len: sizeof (sin)) != 0)
212 {
213 /* If the port is not available, we cannot run this test. */
214 printf (format: "warning: could not bind to rpcbind port %d: %m\n",
215 (int) PMAPPORT);
216 return EXIT_UNSUPPORTED;
217 }
218 }
219
220 for (int use_thread = 0; use_thread < 2; ++use_thread)
221 for (int use_rpcbind = 0; use_rpcbind < 2; ++use_rpcbind)
222 for (int use_unregister = 0; use_unregister < 2; ++use_unregister)
223 {
224 if (test_verbose)
225 printf (format: "info: * use_thread=%d use_rpcbind=%d use_unregister=%d\n",
226 use_thread, use_rpcbind, use_unregister);
227
228 /* Create the subprocess which runs the actual test. The
229 kernel will queue the UDP packets to the rpcbind
230 process. */
231 pid_t svc_pid = xfork ();
232 if (svc_pid == 0)
233 {
234 struct test_server_args args =
235 {
236 .use_rpcbind = use_rpcbind,
237 .use_unregister = use_unregister,
238 };
239 if (use_thread)
240 xpthread_join (thr: xpthread_create
241 (NULL, thread_func: test_server_thread, closure: &args));
242 else
243 test_server_thread (closure: &args);
244 /* We cannnot use _exit here because we want to test the
245 process cleanup. */
246 exit (0);
247 }
248
249 /* Create the subprocess for the rpcbind test server. */
250 pid_t rpcbind_pid = xfork ();
251 if (rpcbind_pid == 0)
252 run_rpcbind (rpcbind_sock);
253
254 int status;
255 xwaitpid (svc_pid, status: &status, flags: 0);
256 TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == 0);
257
258 if (!use_rpcbind)
259 /* Wait a bit, to see if the packet arrives on the rpcbind
260 port. The choice is of the timeout is arbitrary, but
261 should be long enough even for slow/busy systems. For
262 the use_rpcbind case, waiting on svc_pid above makes
263 sure that the test server has responded because
264 svc_register/svc_unregister are supposed to wait for a
265 reply. */
266 usleep (useconds: 300 * 1000);
267
268 struct test_state state = get_test_state ();
269 if (use_rpcbind)
270 {
271 TEST_VERIFY (state.set_called);
272 if (use_thread || use_unregister)
273 /* Thread cleanup or explicit svc_unregister will
274 result in a rpcbind unset RPC call. */
275 TEST_VERIFY (state.unset_called);
276 else
277 /* This is arguably a bug: Regular process termination
278 does not unregister the service with rpcbind. The
279 unset rpcbind call happens from a __libc_subfreeres
280 callback, and this only happens when running under
281 memory debuggers such as valgrind. */
282 TEST_VERIFY (!state.unset_called);
283 }
284 else
285 {
286 /* If rpcbind registration is not requested, we do not
287 expect any rpcbind calls. */
288 TEST_VERIFY (!state.set_called);
289 TEST_VERIFY (!state.unset_called);
290 }
291
292 xwaitpid (rpcbind_pid, status: &status, flags: 0);
293 TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == 0);
294 }
295
296 return 0;
297}
298
299#include <support/test-driver.c>
300

source code of glibc/sunrpc/tst-svc_register.c