2 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
9 #include <common/compat/fcntl.hpp>
10 #include <common/sessiond-comm/sessiond-comm.hpp>
11 #include <common/payload.hpp>
12 #include <common/payload-view.hpp>
13 #include <common/unix.hpp>
14 #include <common/utils.hpp>
15 #include <common/defaults.hpp>
18 #include <common/error.hpp>
19 #include <lttng/constant.h>
26 #define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS
27 #define MESSAGE_COUNT 4
28 #define LARGE_PAYLOAD_SIZE 4 * 1024
29 #define LARGE_PAYLOAD_RECV_SIZE 100
31 static const int TEST_COUNT
= 37;
35 int lttng_opt_verbose
;
39 * Validate that a large number of file descriptors can be received in one shot.
41 static void test_high_fd_count(unsigned int fd_count
)
43 int sockets
[2] = {-1, -1};
46 const unsigned int payload_content
= 42;
47 struct lttng_payload sent_payload
;
48 struct lttng_payload received_payload
;
50 diag("Send and receive high FD count atomically (%u FDs)", fd_count
);
51 lttng_payload_init(&sent_payload
);
52 lttng_payload_init(&received_payload
);
54 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
55 ok(ret
== 0, "Created anonymous unix socket pair");
57 PERROR("Failed to create an anonymous pair of unix sockets");
61 /* Add dummy content to payload. */
62 ret
= lttng_dynamic_buffer_append(&sent_payload
.buffer
,
63 &payload_content
, sizeof(payload_content
));
65 PERROR("Failed to initialize test payload");
69 for (i
= 0; i
< fd_count
; i
++) {
70 struct fd_handle
*handle
;
71 int fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
74 PERROR("Failed to create fd while creating test payload");
78 handle
= fd_handle_create(fd
);
81 PERROR("Failed to close fd while preparing test payload");
86 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
87 fd_handle_put(handle
);
89 PERROR("Failed to add fd handle to test payload");
97 struct lttng_payload_view pv
= lttng_payload_view_from_payload(
98 &sent_payload
, 0, -1);
100 /* Not expected to block considering the size of the payload. */
101 sock_ret
= lttcomm_send_unix_sock(
102 sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
103 ok(sock_ret
== pv
.buffer
.size
, "Sent complete test payload");
104 if (sock_ret
!= pv
.buffer
.size
) {
105 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
106 sock_ret
, pv
.buffer
.size
);
110 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(
112 ok(sock_ret
== 1, "Sent test payload file descriptors");
115 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
118 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
126 /* Receive payload */
130 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
,
131 sent_payload
.buffer
.size
);
133 PERROR("Failed to pre-allocate reception buffer");
137 sock_ret
= lttcomm_recv_unix_sock(sockets
[1],
138 received_payload
.buffer
.data
,
139 received_payload
.buffer
.size
);
140 ok(sock_ret
== received_payload
.buffer
.size
,
141 "Received payload bytes");
142 if (sock_ret
!= received_payload
.buffer
.size
) {
143 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
144 sock_ret
, received_payload
.buffer
.size
);
148 sock_ret
= lttcomm_recv_payload_fds_unix_sock(
149 sockets
[1], fd_count
, &received_payload
);
150 ok(sock_ret
== (int) (sizeof(int) * fd_count
),
151 "FD reception return value is number of fd * sizeof(int)");
152 if (sock_ret
!= (int) (sizeof(int) * fd_count
)) {
153 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
155 (int) (fd_count
* sizeof(int)));
160 const struct lttng_payload_view pv
=
161 lttng_payload_view_from_payload(
162 &received_payload
, 0,
164 const int fd_handle_count
=
165 lttng_payload_view_get_fd_handle_count(
168 ok(fd_handle_count
== fd_count
,
169 "Received all test payload file descriptors in one invocation");
174 for (i
= 0; i
< 2; i
++) {
175 if (sockets
[i
] < 0) {
179 if (close(sockets
[i
])) {
180 PERROR("Failed to close unix socket");
184 lttng_payload_reset(&sent_payload
);
185 lttng_payload_reset(&received_payload
);
189 * Validate that if the sender sent multiple messages, each containing 1 fd,
190 * the receiver can receive one message at a time (the binary payload and its
191 * fd) and is not forced to receive all file descriptors at once.
193 static void test_one_fd_per_message(unsigned int message_count
)
195 const unsigned int payload_content
= 42;
196 int sockets
[2] = {-1, -1};
199 struct lttng_payload sent_payload
;
200 struct lttng_payload received_payload
;
202 diag("Send and receive small messages with one FD each (%u messages)",
204 lttng_payload_init(&sent_payload
);
205 lttng_payload_init(&received_payload
);
207 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
208 ok(ret
== 0, "Created anonymous unix socket pair");
210 PERROR("Failed to create an anonymous pair of unix sockets");
214 /* Send messages with one fd each. */
215 for (i
= 0; i
< message_count
; i
++) {
216 struct fd_handle
*handle
;
219 /* Add dummy content to payload. */
220 ret
= lttng_dynamic_buffer_append(&sent_payload
.buffer
,
221 &payload_content
, sizeof(payload_content
));
223 PERROR("Failed to initialize test payload");
227 fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
229 PERROR("Failed to create fd while creating test payload");
233 handle
= fd_handle_create(fd
);
236 PERROR("Failed to close fd while preparing test payload");
241 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
242 fd_handle_put(handle
);
244 PERROR("Failed to add fd handle to test payload");
251 struct lttng_payload_view pv
=
252 lttng_payload_view_from_payload(
253 &sent_payload
, 0, -1);
255 /* Not expected to block considering the size of the
257 sock_ret
= lttcomm_send_unix_sock(sockets
[0],
258 pv
.buffer
.data
, pv
.buffer
.size
);
259 ok(sock_ret
== pv
.buffer
.size
,
260 "Sent binary payload for message %u",
262 if (sock_ret
!= pv
.buffer
.size
) {
263 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
264 sock_ret
, pv
.buffer
.size
);
268 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(
271 "Sent file descriptors payload for message %u",
275 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
278 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
286 lttng_payload_clear(&sent_payload
);
289 /* Receive messages one at a time. */
290 for (i
= 0; i
< message_count
; i
++) {
293 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
,
294 sizeof(payload_content
));
296 PERROR("Failed to pre-allocate reception buffer");
300 sock_ret
= lttcomm_recv_unix_sock(sockets
[1],
301 received_payload
.buffer
.data
,
302 received_payload
.buffer
.size
);
303 ok(sock_ret
== received_payload
.buffer
.size
,
304 "Received payload bytes for message %u", i
);
305 if (sock_ret
!= received_payload
.buffer
.size
) {
306 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
307 sock_ret
, received_payload
.buffer
.size
);
311 sock_ret
= lttcomm_recv_payload_fds_unix_sock(
312 sockets
[1], 1, &received_payload
);
313 ok(sock_ret
== (int) sizeof(int), "Received fd for message %u",
315 if (sock_ret
!= (int) sizeof(int)) {
316 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u",
317 sock_ret
, (int) sizeof(int));
322 const struct lttng_payload_view pv
=
323 lttng_payload_view_from_payload(
324 &received_payload
, 0,
326 const int fd_handle_count
=
327 lttng_payload_view_get_fd_handle_count(
330 ok(fd_handle_count
== 1,
331 "Payload contains 1 fd for message %u",
335 lttng_payload_clear(&received_payload
);
339 for (i
= 0; i
< 2; i
++) {
340 if (sockets
[i
] < 0) {
344 if (close(sockets
[i
])) {
345 PERROR("Failed to close unix socket");
349 lttng_payload_reset(&sent_payload
);
350 lttng_payload_reset(&received_payload
);
354 * Validate that a large message can be received in multiple chunks.
356 static void test_receive_in_chunks(
357 unsigned int payload_size
, unsigned int max_recv_size
)
359 int sockets
[2] = {-1, -1};
362 struct lttng_payload sent_payload
;
363 struct lttng_payload received_payload
;
364 struct fd_handle
*handle
;
366 ssize_t sock_ret
, received
= 0;
368 diag("Receive a message in multiple chunks");
369 lttng_payload_init(&sent_payload
);
370 lttng_payload_init(&received_payload
);
372 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
373 ok(ret
== 0, "Created anonymous unix socket pair");
375 PERROR("Failed to create an anonymous pair of unix sockets");
379 /* Add dummy content to payload. */
380 ret
= lttng_dynamic_buffer_set_size(&sent_payload
.buffer
, payload_size
);
382 PERROR("Failed to initialize test payload");
386 fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
388 PERROR("Failed to create fd while creating test payload");
392 handle
= fd_handle_create(fd
);
395 PERROR("Failed to close fd while preparing test payload");
400 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
401 fd_handle_put(handle
);
403 PERROR("Failed to add fd handle to test payload");
409 struct lttng_payload_view pv
= lttng_payload_view_from_payload(
410 &sent_payload
, 0, -1);
412 /* Not expected to block considering the size of the payload. */
413 sock_ret
= lttcomm_send_unix_sock(
414 sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
415 ok(sock_ret
== pv
.buffer
.size
, "Sent complete test payload");
416 if (sock_ret
!= pv
.buffer
.size
) {
417 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
418 sock_ret
, pv
.buffer
.size
);
422 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(
424 ok(sock_ret
== 1, "Sent test payload file descriptors");
427 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
430 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
438 /* Receive payload */
439 ret
= lttng_dynamic_buffer_set_size(
440 &received_payload
.buffer
, sent_payload
.buffer
.size
);
442 PERROR("Failed to pre-allocate reception buffer");
447 const ssize_t to_receive_this_pass
=
448 std::min
<ssize_t
>(max_recv_size
,
449 sent_payload
.buffer
.size
- received
);
451 sock_ret
= lttcomm_recv_unix_sock(sockets
[1],
452 received_payload
.buffer
.data
+ received
,
453 to_receive_this_pass
);
454 if (sock_ret
!= to_receive_this_pass
) {
455 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
456 sock_ret
, to_receive_this_pass
);
460 received
+= sock_ret
;
461 } while (received
< sent_payload
.buffer
.size
);
463 ok(received
== sent_payload
.buffer
.size
,
464 "Received complete payload in chunks of %u bytes",
466 if (received
!= sent_payload
.buffer
.size
) {
470 sock_ret
= lttcomm_recv_payload_fds_unix_sock(
471 sockets
[1], 1, &received_payload
);
472 ok(sock_ret
== (int) sizeof(int),
473 "Received file descriptor after receiving payload in chunks");
474 if (sock_ret
!= (int) sizeof(int)) {
475 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
476 sock_ret
, (int) sizeof(int));
481 const struct lttng_payload_view pv
=
482 lttng_payload_view_from_payload(
483 &received_payload
, 0, -1);
484 const int fd_handle_count
=
485 lttng_payload_view_get_fd_handle_count(&pv
);
487 ok(fd_handle_count
== 1,
488 "Payload contains 1 fd after receiving payload in chunks");
492 for (i
= 0; i
< 2; i
++) {
493 if (sockets
[i
] < 0) {
497 if (close(sockets
[i
])) {
498 PERROR("Failed to close unix socket");
502 lttng_payload_reset(&sent_payload
);
503 lttng_payload_reset(&received_payload
);
507 void test_creds_passing(void)
510 int ret
, parent_socket
= -1, child_connection_socket
= -1;
512 char socket_dir_path
[] = "/tmp/test.unix.socket.creds.passing.XXXXXX";
513 char socket_path
[PATH_MAX
] = {};
514 struct expected_creds
{
520 diag("Receive peer's effective uid, effective gid, and pid from a unix socket");
522 if (!mkdtemp(socket_dir_path
)) {
523 PERROR("Failed to generate temporary socket location");
527 strncat(socket_path
, socket_dir_path
,
528 sizeof(socket_path
) - strlen(socket_path
) - 1);
529 strncat(socket_path
, "/test_unix_socket",
530 sizeof(socket_path
) - strlen(socket_path
) - 1);
532 parent_socket
= lttcomm_create_unix_sock(socket_path
);
533 ok(parent_socket
>= 0, "Created unix socket at path `%s`", socket_path
);
534 if (parent_socket
< 0) {
535 PERROR("Failed to create unix socket at path `%s`", socket_path
);
539 ret
= lttcomm_listen_unix_sock(parent_socket
);
541 PERROR("Failed to mark parent socket as a passive socket");
545 ret
= lttcomm_setsockopt_creds_unix_sock(parent_socket
);
547 PERROR("Failed to set SO_PASSCRED on parent socket");
553 PERROR("Failed to fork");
561 expected_creds
= (struct expected_creds
){
567 child_socket
= lttcomm_connect_unix_sock(socket_path
);
568 if (child_socket
< 0) {
569 PERROR("Failed to connect to parent socket");
573 ret
= lttcomm_setsockopt_creds_unix_sock(child_socket
);
575 PERROR("Failed to set SO_PASSCRED on child socket");
578 sock_ret
= lttcomm_send_creds_unix_sock(child_socket
, &expected_creds
,
579 sizeof(expected_creds
));
581 PERROR("Failed to send expected credentials");
584 ret
= close(child_socket
);
586 PERROR("Failed to close child socket");
592 lttng_sock_cred received_creds
= {};
594 child_connection_socket
=
595 lttcomm_accept_unix_sock(parent_socket
);
596 if (child_connection_socket
< 0) {
601 ret
= lttcomm_setsockopt_creds_unix_sock(
602 child_connection_socket
);
604 PERROR("Failed to set SO_PASSCRED on child connection socket");
608 sock_ret
= lttcomm_recv_creds_unix_sock(child_connection_socket
,
609 &expected_creds
, sizeof(expected_creds
),
612 PERROR("Failed to receive credentials");
616 wait_pid_ret
= waitpid(fork_ret
, &child_status
, 0);
617 if (wait_pid_ret
== -1) {
618 PERROR("Failed to wait for termination of child process");
621 if (!WIFEXITED(child_status
) || WEXITSTATUS(child_status
)) {
622 diag("Child process reported an error, test failed");
626 ok(expected_creds
.euid
== received_creds
.uid
,
627 "Received the expected effective uid (%d == %d)",
628 expected_creds
.euid
, received_creds
.uid
);
629 ok(expected_creds
.egid
== received_creds
.gid
,
630 "Received the expected effective gid (%d == %d)",
631 expected_creds
.egid
, received_creds
.gid
);
632 ok(expected_creds
.pid
== received_creds
.pid
,
633 "Received the expected pid (%d == %d)",
634 expected_creds
.pid
, received_creds
.pid
);
638 if (parent_socket
>= 0) {
639 ret
= close(parent_socket
);
641 PERROR("Failed to close parent socket");
646 /* Prevent libtap from printing a result for the child. */
650 /* Child exits at the end of this test. */
652 } else if (parent_socket
>= 0) {
653 if (child_connection_socket
>= 0) {
654 ret
= close(child_connection_socket
);
656 PERROR("Failed to close child connection socket");
660 ret
= unlink(socket_path
);
662 PERROR("Failed to unlink socket at path `%s`",
666 ret
= rmdir(socket_dir_path
);
668 PERROR("Failed to remove test directory at `%s`",
676 plan_tests(TEST_COUNT
);
678 test_high_fd_count(HIGH_FD_COUNT
);
679 test_one_fd_per_message(MESSAGE_COUNT
);
680 test_receive_in_chunks(LARGE_PAYLOAD_SIZE
, LARGE_PAYLOAD_RECV_SIZE
);
681 test_creds_passing();
683 return exit_status();