2 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
8 #include <common/compat/fcntl.h>
9 #include <common/sessiond-comm/sessiond-comm.h>
10 #include <common/payload.h>
11 #include <common/payload-view.h>
12 #include <common/unix.h>
13 #include <common/utils.h>
14 #include <common/defaults.h>
17 #include <common/error.h>
18 #include <lttng/constant.h>
25 #define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS
26 #define MESSAGE_COUNT 4
27 #define LARGE_PAYLOAD_SIZE 4 * 1024
28 #define LARGE_PAYLOAD_RECV_SIZE 100
30 static const int TEST_COUNT
= 37;
34 int lttng_opt_verbose
;
38 * Validate that a large number of file descriptors can be received in one shot.
40 static void test_high_fd_count(unsigned int fd_count
)
42 int sockets
[2] = {-1, -1};
45 const unsigned int payload_content
= 42;
46 struct lttng_payload sent_payload
;
47 struct lttng_payload received_payload
;
49 diag("Send and receive high FD count atomically (%u FDs)", fd_count
);
50 lttng_payload_init(&sent_payload
);
51 lttng_payload_init(&received_payload
);
53 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
54 ok(ret
== 0, "Created anonymous unix socket pair");
56 PERROR("Failed to create an anonymous pair of unix sockets");
60 /* Add dummy content to payload. */
61 ret
= lttng_dynamic_buffer_append(&sent_payload
.buffer
,
62 &payload_content
, sizeof(payload_content
));
64 PERROR("Failed to initialize test payload");
68 for (i
= 0; i
< fd_count
; i
++) {
69 struct fd_handle
*handle
;
70 int fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
73 PERROR("Failed to create fd while creating test payload");
77 handle
= fd_handle_create(fd
);
80 PERROR("Failed to close fd while preparing test payload");
85 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
86 fd_handle_put(handle
);
88 PERROR("Failed to add fd handle to test payload");
96 struct lttng_payload_view pv
= lttng_payload_view_from_payload(
97 &sent_payload
, 0, -1);
99 /* Not expected to block considering the size of the payload. */
100 sock_ret
= lttcomm_send_unix_sock(
101 sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
102 ok(sock_ret
== pv
.buffer
.size
, "Sent complete test payload");
103 if (sock_ret
!= pv
.buffer
.size
) {
104 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
105 sock_ret
, pv
.buffer
.size
);
109 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(
111 ok(sock_ret
== 1, "Sent test payload file descriptors");
114 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
117 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
125 /* Receive payload */
129 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
,
130 sent_payload
.buffer
.size
);
132 PERROR("Failed to pre-allocate reception buffer");
136 sock_ret
= lttcomm_recv_unix_sock(sockets
[1],
137 received_payload
.buffer
.data
,
138 received_payload
.buffer
.size
);
139 ok(sock_ret
== received_payload
.buffer
.size
,
140 "Received payload bytes");
141 if (sock_ret
!= received_payload
.buffer
.size
) {
142 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
143 sock_ret
, received_payload
.buffer
.size
);
147 sock_ret
= lttcomm_recv_payload_fds_unix_sock(
148 sockets
[1], fd_count
, &received_payload
);
149 ok(sock_ret
== (int) (sizeof(int) * fd_count
),
150 "FD reception return value is number of fd * sizeof(int)");
151 if (sock_ret
!= (int) (sizeof(int) * fd_count
)) {
152 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
154 (int) (fd_count
* sizeof(int)));
159 const struct lttng_payload_view pv
=
160 lttng_payload_view_from_payload(
161 &received_payload
, 0,
163 const int fd_handle_count
=
164 lttng_payload_view_get_fd_handle_count(
167 ok(fd_handle_count
== fd_count
,
168 "Received all test payload file descriptors in one invocation");
173 for (i
= 0; i
< 2; i
++) {
174 if (sockets
[i
] < 0) {
178 if (close(sockets
[i
])) {
179 PERROR("Failed to close unix socket");
183 lttng_payload_reset(&sent_payload
);
184 lttng_payload_reset(&received_payload
);
188 * Validate that if the sender sent multiple messages, each containing 1 fd,
189 * the receiver can receive one message at a time (the binary payload and its
190 * fd) and is not forced to receive all file descriptors at once.
192 static void test_one_fd_per_message(unsigned int message_count
)
194 const unsigned int payload_content
= 42;
195 int sockets
[2] = {-1, -1};
198 struct lttng_payload sent_payload
;
199 struct lttng_payload received_payload
;
201 diag("Send and receive small messages with one FD each (%u messages)",
203 lttng_payload_init(&sent_payload
);
204 lttng_payload_init(&received_payload
);
206 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
207 ok(ret
== 0, "Created anonymous unix socket pair");
209 PERROR("Failed to create an anonymous pair of unix sockets");
213 /* Send messages with one fd each. */
214 for (i
= 0; i
< message_count
; i
++) {
215 struct fd_handle
*handle
;
218 /* Add dummy content to payload. */
219 ret
= lttng_dynamic_buffer_append(&sent_payload
.buffer
,
220 &payload_content
, sizeof(payload_content
));
222 PERROR("Failed to initialize test payload");
226 fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
228 PERROR("Failed to create fd while creating test payload");
232 handle
= fd_handle_create(fd
);
235 PERROR("Failed to close fd while preparing test payload");
240 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
241 fd_handle_put(handle
);
243 PERROR("Failed to add fd handle to test payload");
250 struct lttng_payload_view pv
=
251 lttng_payload_view_from_payload(
252 &sent_payload
, 0, -1);
254 /* Not expected to block considering the size of the
256 sock_ret
= lttcomm_send_unix_sock(sockets
[0],
257 pv
.buffer
.data
, pv
.buffer
.size
);
258 ok(sock_ret
== pv
.buffer
.size
,
259 "Sent binary payload for message %u",
261 if (sock_ret
!= pv
.buffer
.size
) {
262 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
263 sock_ret
, pv
.buffer
.size
);
267 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(
270 "Sent file descriptors payload for message %u",
274 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
277 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
285 lttng_payload_clear(&sent_payload
);
288 /* Receive messages one at a time. */
289 for (i
= 0; i
< message_count
; i
++) {
292 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
,
293 sizeof(payload_content
));
295 PERROR("Failed to pre-allocate reception buffer");
299 sock_ret
= lttcomm_recv_unix_sock(sockets
[1],
300 received_payload
.buffer
.data
,
301 received_payload
.buffer
.size
);
302 ok(sock_ret
== received_payload
.buffer
.size
,
303 "Received payload bytes for message %u", i
);
304 if (sock_ret
!= received_payload
.buffer
.size
) {
305 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
306 sock_ret
, received_payload
.buffer
.size
);
310 sock_ret
= lttcomm_recv_payload_fds_unix_sock(
311 sockets
[1], 1, &received_payload
);
312 ok(sock_ret
== (int) sizeof(int), "Received fd for message %u",
314 if (sock_ret
!= (int) sizeof(int)) {
315 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u",
316 sock_ret
, (int) sizeof(int));
321 const struct lttng_payload_view pv
=
322 lttng_payload_view_from_payload(
323 &received_payload
, 0,
325 const int fd_handle_count
=
326 lttng_payload_view_get_fd_handle_count(
329 ok(fd_handle_count
== 1,
330 "Payload contains 1 fd for message %u",
334 lttng_payload_clear(&received_payload
);
338 for (i
= 0; i
< 2; i
++) {
339 if (sockets
[i
] < 0) {
343 if (close(sockets
[i
])) {
344 PERROR("Failed to close unix socket");
348 lttng_payload_reset(&sent_payload
);
349 lttng_payload_reset(&received_payload
);
353 * Validate that a large message can be received in multiple chunks.
355 static void test_receive_in_chunks(
356 unsigned int payload_size
, unsigned int max_recv_size
)
358 int sockets
[2] = {-1, -1};
361 struct lttng_payload sent_payload
;
362 struct lttng_payload received_payload
;
363 struct fd_handle
*handle
;
365 ssize_t sock_ret
, received
= 0;
367 diag("Receive a message in multiple chunks");
368 lttng_payload_init(&sent_payload
);
369 lttng_payload_init(&received_payload
);
371 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
372 ok(ret
== 0, "Created anonymous unix socket pair");
374 PERROR("Failed to create an anonymous pair of unix sockets");
378 /* Add dummy content to payload. */
379 ret
= lttng_dynamic_buffer_set_size(&sent_payload
.buffer
, payload_size
);
381 PERROR("Failed to initialize test payload");
385 fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
387 PERROR("Failed to create fd while creating test payload");
391 handle
= fd_handle_create(fd
);
394 PERROR("Failed to close fd while preparing test payload");
399 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
400 fd_handle_put(handle
);
402 PERROR("Failed to add fd handle to test payload");
408 struct lttng_payload_view pv
= lttng_payload_view_from_payload(
409 &sent_payload
, 0, -1);
411 /* Not expected to block considering the size of the payload. */
412 sock_ret
= lttcomm_send_unix_sock(
413 sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
414 ok(sock_ret
== pv
.buffer
.size
, "Sent complete test payload");
415 if (sock_ret
!= pv
.buffer
.size
) {
416 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
417 sock_ret
, pv
.buffer
.size
);
421 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(
423 ok(sock_ret
== 1, "Sent test payload file descriptors");
426 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
429 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
437 /* Receive payload */
438 ret
= lttng_dynamic_buffer_set_size(
439 &received_payload
.buffer
, sent_payload
.buffer
.size
);
441 PERROR("Failed to pre-allocate reception buffer");
446 const ssize_t to_receive_this_pass
= min(max_recv_size
,
447 sent_payload
.buffer
.size
- received
);
449 sock_ret
= lttcomm_recv_unix_sock(sockets
[1],
450 received_payload
.buffer
.data
+ received
,
451 to_receive_this_pass
);
452 if (sock_ret
!= to_receive_this_pass
) {
453 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
454 sock_ret
, to_receive_this_pass
);
458 received
+= sock_ret
;
459 } while (received
< sent_payload
.buffer
.size
);
461 ok(received
== sent_payload
.buffer
.size
,
462 "Received complete payload in chunks of %u bytes",
464 if (received
!= sent_payload
.buffer
.size
) {
468 sock_ret
= lttcomm_recv_payload_fds_unix_sock(
469 sockets
[1], 1, &received_payload
);
470 ok(sock_ret
== (int) sizeof(int),
471 "Received file descriptor after receiving payload in chunks");
472 if (sock_ret
!= (int) sizeof(int)) {
473 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
474 sock_ret
, (int) sizeof(int));
479 const struct lttng_payload_view pv
=
480 lttng_payload_view_from_payload(
481 &received_payload
, 0, -1);
482 const int fd_handle_count
=
483 lttng_payload_view_get_fd_handle_count(&pv
);
485 ok(fd_handle_count
== 1,
486 "Payload contains 1 fd after receiving payload in chunks");
490 for (i
= 0; i
< 2; i
++) {
491 if (sockets
[i
] < 0) {
495 if (close(sockets
[i
])) {
496 PERROR("Failed to close unix socket");
500 lttng_payload_reset(&sent_payload
);
501 lttng_payload_reset(&received_payload
);
505 void test_creds_passing(void)
508 int ret
, parent_socket
= -1, child_connection_socket
= -1;
510 char socket_dir_path
[] = "/tmp/test.unix.socket.creds.passing.XXXXXX";
511 char socket_path
[PATH_MAX
] = {};
512 struct expected_creds
{
518 diag("Receive peer's effective uid, effective gid, and pid from a unix socket");
520 if (!mkdtemp(socket_dir_path
)) {
521 PERROR("Failed to generate temporary socket location");
525 strncat(socket_path
, socket_dir_path
,
526 sizeof(socket_path
) - strlen(socket_path
) - 1);
527 strncat(socket_path
, "/test_unix_socket",
528 sizeof(socket_path
) - strlen(socket_path
) - 1);
530 parent_socket
= lttcomm_create_unix_sock(socket_path
);
531 ok(parent_socket
>= 0, "Created unix socket at path `%s`", socket_path
);
532 if (parent_socket
< 0) {
533 PERROR("Failed to create unix socket at path `%s`", socket_path
);
537 ret
= lttcomm_listen_unix_sock(parent_socket
);
539 PERROR("Failed to mark parent socket as a passive socket");
543 ret
= lttcomm_setsockopt_creds_unix_sock(parent_socket
);
545 PERROR("Failed to set SO_PASSCRED on parent socket");
551 PERROR("Failed to fork");
559 expected_creds
= (struct expected_creds
){
565 child_socket
= lttcomm_connect_unix_sock(socket_path
);
566 if (child_socket
< 0) {
567 PERROR("Failed to connect to parent socket");
571 ret
= lttcomm_setsockopt_creds_unix_sock(child_socket
);
573 PERROR("Failed to set SO_PASSCRED on child socket");
576 sock_ret
= lttcomm_send_creds_unix_sock(child_socket
, &expected_creds
,
577 sizeof(expected_creds
));
579 PERROR("Failed to send expected credentials");
582 ret
= close(child_socket
);
584 PERROR("Failed to close child socket");
590 lttng_sock_cred received_creds
= {};
592 child_connection_socket
=
593 lttcomm_accept_unix_sock(parent_socket
);
594 if (child_connection_socket
< 0) {
599 ret
= lttcomm_setsockopt_creds_unix_sock(
600 child_connection_socket
);
602 PERROR("Failed to set SO_PASSCRED on child connection socket");
606 sock_ret
= lttcomm_recv_creds_unix_sock(child_connection_socket
,
607 &expected_creds
, sizeof(expected_creds
),
610 PERROR("Failed to receive credentials");
614 wait_pid_ret
= waitpid(fork_ret
, &child_status
, 0);
615 if (wait_pid_ret
== -1) {
616 PERROR("Failed to wait for termination of child process");
619 if (!WIFEXITED(child_status
) || WEXITSTATUS(child_status
)) {
620 diag("Child process reported an error, test failed");
624 ok(expected_creds
.euid
== received_creds
.uid
,
625 "Received the expected effective uid (%d == %d)",
626 expected_creds
.euid
, received_creds
.uid
);
627 ok(expected_creds
.egid
== received_creds
.gid
,
628 "Received the expected effective gid (%d == %d)",
629 expected_creds
.egid
, received_creds
.gid
);
630 ok(expected_creds
.pid
== received_creds
.pid
,
631 "Received the expected pid (%d == %d)",
632 expected_creds
.pid
, received_creds
.pid
);
636 if (parent_socket
>= 0) {
637 ret
= close(parent_socket
);
639 PERROR("Failed to close parent socket");
644 if (child_connection_socket
>= 0) {
645 ret
= close(child_connection_socket
);
647 PERROR("Failed to close child connection socket");
651 /* Prevent libtap from printing a result for the child. */
655 /* Child exits at the end of this test. */
657 } else if (parent_socket
>= 0) {
658 ret
= unlink(socket_path
);
660 PERROR("Failed to unlink socket at path `%s`",
664 ret
= rmdir(socket_dir_path
);
666 PERROR("Failed to remove test directory at `%s`",
674 plan_tests(TEST_COUNT
);
676 test_high_fd_count(HIGH_FD_COUNT
);
677 test_one_fd_per_message(MESSAGE_COUNT
);
678 test_receive_in_chunks(LARGE_PAYLOAD_SIZE
, LARGE_PAYLOAD_RECV_SIZE
);
679 test_creds_passing();
681 return exit_status();