/* Smoke test for SCM_RIGHTS.
Copyright (C) 2021-2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
. */
/* This test passes a file descriptor from a subprocess to the parent
process, using recvmsg/sendmsg or recvmmsg/sendmmsg. */
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* String sent over the socket. */
static char DATA[] = "descriptor";
/* Path that is to be opened and sent over the socket. */
#define PATH "/etc"
/* True if sendmmsg/recvmmsg is to be used. */
static bool use_multi_call;
/* The pair of sockets used for coordination. The subprocess uses
sockets[1]. */
static int sockets[2];
/* Subprocess side of one send/receive test. */
_Noreturn static void
subprocess (void)
{
/* The file descriptor to send. */
int fd = xopen (PATH, O_RDONLY, 0);
struct iovec iov = { .iov_base = DATA, .iov_len = sizeof (DATA) };
union
{
struct cmsghdr header;
char bytes[CMSG_SPACE (sizeof (int))];
} cmsg_storage;
struct mmsghdr mmhdr =
{
.msg_hdr =
{
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsg_storage.bytes,
.msg_controllen = sizeof (cmsg_storage),
},
};
/* Configure the file descriptor for sending. */
struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (sizeof (int));
memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
mmhdr.msg_hdr.msg_controllen = cmsg->cmsg_len;
/* Perform the send operation. */
int ret;
if (use_multi_call)
{
ret = sendmmsg (sockets[1], &mmhdr, 1, 0);
if (ret >= 0)
ret = mmhdr.msg_len;
}
else
ret = sendmsg (sockets[1], &mmhdr.msg_hdr, 0);
TEST_COMPARE (ret, sizeof (DATA));
xclose (fd);
/* Stop the process from exiting. */
while (true)
pause ();
}
/* Performs one send/receive test. */
static void
one_test (void)
{
TEST_COMPARE (socketpair (AF_UNIX, SOCK_STREAM, 0, sockets), 0);
pid_t pid = xfork ();
if (pid == 0)
subprocess ();
char data_storage[sizeof (DATA) + 1];
struct iovec iov =
{
.iov_base = data_storage,
.iov_len = sizeof (data_storage)
};
union
{
struct cmsghdr header;
char bytes[CMSG_SPACE (sizeof (int))];
} cmsg_storage;
struct mmsghdr mmhdr =
{
.msg_hdr =
{
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsg_storage.bytes,
.msg_controllen = sizeof (cmsg_storage),
},
};
/* Set up the space for receiving the file descriptor. */
struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (sizeof (int));
mmhdr.msg_hdr.msg_controllen = cmsg->cmsg_len;
/* Perform the receive operation. */
int ret;
if (use_multi_call)
{
ret = recvmmsg (sockets[0], &mmhdr, 1, 0, NULL);
if (ret >= 0)
ret = mmhdr.msg_len;
}
else
ret = recvmsg (sockets[0], &mmhdr.msg_hdr, 0);
TEST_COMPARE (ret, sizeof (DATA));
TEST_COMPARE_BLOB (data_storage, sizeof (DATA), DATA, sizeof (DATA));
/* Extract the file descriptor. */
TEST_VERIFY (CMSG_FIRSTHDR (&mmhdr.msg_hdr) != NULL);
TEST_COMPARE (CMSG_FIRSTHDR (&mmhdr.msg_hdr)->cmsg_len,
CMSG_LEN (sizeof (int)));
TEST_VERIFY (&cmsg_storage.header == CMSG_FIRSTHDR (&mmhdr.msg_hdr));
int fd;
memcpy (&fd, CMSG_DATA (CMSG_FIRSTHDR (&mmhdr.msg_hdr)), sizeof (fd));
/* Verify the received file descriptor. */
TEST_VERIFY (fd > 2);
struct stat64 st_fd;
TEST_COMPARE (fstat64 (fd, &st_fd), 0);
struct stat64 st_path;
TEST_COMPARE (stat64 (PATH, &st_path), 0);
TEST_COMPARE (st_fd.st_ino, st_path.st_ino);
TEST_COMPARE (st_fd.st_dev, st_path.st_dev);
xclose (fd);
/* Terminate the subprocess. */
TEST_COMPARE (kill (pid, SIGUSR1), 0);
int status;
TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
TEST_VERIFY (WIFSIGNALED (status));
TEST_COMPARE (WTERMSIG (status), SIGUSR1);
xclose (sockets[0]);
xclose (sockets[1]);
}
static int
do_test (void)
{
one_test ();
use_multi_call = true;
one_test ();
return 0;
}
#include