blob: fcc71ab1c399401c151bc97e0417cc892dfe1e34 [file]
//===-- Unittests for sendmsg/recvmsg -------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "hdr/fcntl_macros.h"
#include "hdr/sys_socket_macros.h"
#include "hdr/types/struct_cmsghdr.h"
#include "src/fcntl/fcntl.h"
#include "src/string/memcpy.h"
#include "src/string/memset.h"
#include "src/sys/socket/getsockopt.h"
#include "src/sys/socket/recvmsg.h"
#include "src/sys/socket/sendmsg.h"
#include "src/sys/socket/socketpair.h"
#include "src/unistd/close.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/LibcTest.h"
#include "test/UnitTest/Test.h"
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcSendMsgRecvMsgTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
TEST_F(LlvmLibcSendMsgRecvMsgTest, SucceedsWithSocketPair) {
const char TEST_MESSAGE[] = "connection successful";
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);
int sockpair[2] = {0, 0};
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair),
Succeeds(0));
iovec send_msg_text;
send_msg_text.iov_base =
reinterpret_cast<void *>(const_cast<char *>(TEST_MESSAGE));
send_msg_text.iov_len = MESSAGE_LEN;
msghdr send_message;
send_message.msg_name = nullptr;
send_message.msg_namelen = 0;
send_message.msg_iov = &send_msg_text;
send_message.msg_iovlen = 1;
send_message.msg_control = nullptr;
send_message.msg_controllen = 0;
send_message.msg_flags = 0;
ASSERT_THAT(LIBC_NAMESPACE::sendmsg(sockpair[0], &send_message, 0),
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
char buffer[256];
iovec recv_msg_text;
recv_msg_text.iov_base = reinterpret_cast<void *>(buffer);
recv_msg_text.iov_len = sizeof(buffer);
msghdr recv_message;
recv_message.msg_name = nullptr;
recv_message.msg_namelen = 0;
recv_message.msg_iov = &recv_msg_text;
recv_message.msg_iovlen = 1;
recv_message.msg_control = nullptr;
recv_message.msg_controllen = 0;
recv_message.msg_flags = 0;
ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &recv_message, 0),
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
ASSERT_STREQ(buffer, TEST_MESSAGE);
// close both ends of the socket
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, CmsgDetails) {
ASSERT_EQ(CMSG_ALIGN(0), static_cast<size_t>(0));
ASSERT_EQ(CMSG_ALIGN(1), sizeof(size_t));
// Some implementations align struct cmsghdr in various size computations, but
// this is a noop. This verifies that.
ASSERT_EQ(CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct cmsghdr));
char buf[0x100] = {};
struct msghdr msg;
msg.msg_control = buf;
// We shouldn't be able to get the first header if there's not enough space
// for it.
msg.msg_controllen = 0;
ASSERT_EQ(CMSG_FIRSTHDR(&msg), nullptr);
msg.msg_controllen = sizeof(struct cmsghdr) - 1;
ASSERT_EQ(CMSG_FIRSTHDR(&msg), nullptr);
msg.msg_controllen = sizeof(struct cmsghdr);
ASSERT_EQ(CMSG_FIRSTHDR(&msg), reinterpret_cast<struct cmsghdr *>(buf));
msg.msg_controllen = sizeof(buf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_EQ(cmsg, reinterpret_cast<struct cmsghdr *>(buf));
// We shouldn't be able to get the next header if this one is too big.
cmsg->cmsg_len = 0x1000;
ASSERT_EQ(CMSG_NXTHDR(&msg, cmsg), nullptr);
cmsg->cmsg_len = sizeof(buf) - sizeof(struct cmsghdr) + 1;
ASSERT_EQ(CMSG_NXTHDR(&msg, cmsg), nullptr);
cmsg->cmsg_len = sizeof(buf) - sizeof(struct cmsghdr);
struct cmsghdr *cmsg2 = CMSG_NXTHDR(&msg, cmsg);
ASSERT_LT(buf, reinterpret_cast<char *>(cmsg2));
ASSERT_LT(reinterpret_cast<char *>(cmsg2), buf + sizeof(buf));
// POSIX explicitly does not specify whether CMSG_NXTHDR returns the
// next header if its data array would extend beyond the end of the buffer.
// Our implementation does.
#ifdef LIBC_FULL_BUILD
cmsg2->cmsg_len = sizeof(struct cmsghdr) + 1;
ASSERT_EQ(CMSG_NXTHDR(&msg, cmsg), cmsg2);
#endif
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, SendAndReceiveFileDescriptor) {
int sockpair[2] = {0, 0};
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair),
Succeeds(0));
struct iovec iov;
iov.iov_base = reinterpret_cast<void *>(const_cast<char *>("x"));
iov.iov_len = 1;
char control_buf[CMSG_SPACE(sizeof(int))] = {};
struct msghdr msg;
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = control_buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
LIBC_NAMESPACE::memcpy(CMSG_DATA(cmsg), sockpair + 1, sizeof(int));
ASSERT_THAT(LIBC_NAMESPACE::sendmsg(sockpair[0], &msg, 0),
Succeeds(static_cast<ssize_t>(1)));
char buffer[256];
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
LIBC_NAMESPACE::memset(control_buf, 0, sizeof(control_buf));
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buf;
msg.msg_controllen = sizeof(control_buf);
msg.msg_flags = 0;
ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &msg, 0),
Succeeds(static_cast<ssize_t>(1)));
ASSERT_EQ(buffer[0], 'x');
cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_TRUE(cmsg != nullptr);
ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET);
// Use ASSERT_TRUE, as ASSERT_EQ requires SCM_RIGHTS to be an int,
// which is not true on all systems (e.g. glibc).
ASSERT_TRUE(cmsg->cmsg_type == SCM_RIGHTS);
ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
ASSERT_EQ(CMSG_NXTHDR(&msg, cmsg), nullptr);
int new_fd;
LIBC_NAMESPACE::memcpy(&new_fd, CMSG_DATA(cmsg), sizeof(int));
int new_sock_type = 0;
socklen_t optlen = sizeof(int);
ASSERT_THAT(LIBC_NAMESPACE::getsockopt(new_fd, SOL_SOCKET, SO_TYPE,
&new_sock_type, &optlen),
Succeeds(0));
// Use ASSERT_TRUE, as ASSERT_EQ requires SOCK_STREAM to be an int,
// which is not true on all systems (e.g. glibc).
ASSERT_TRUE(new_sock_type == SOCK_STREAM);
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(new_fd), Succeeds(0));
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, MsgCmsgCloexec) {
int sockpair[2] = {0, 0};
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair),
Succeeds(0));
struct iovec iov;
iov.iov_base = reinterpret_cast<void *>(const_cast<char *>("x"));
iov.iov_len = 1;
char control_buf[CMSG_SPACE(sizeof(int))] = {};
struct msghdr msg;
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = control_buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
LIBC_NAMESPACE::memcpy(CMSG_DATA(cmsg), sockpair + 1, sizeof(int));
ASSERT_THAT(LIBC_NAMESPACE::sendmsg(sockpair[0], &msg, 0),
Succeeds(static_cast<ssize_t>(1)));
char buffer[256];
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
LIBC_NAMESPACE::memset(control_buf, 0, sizeof(control_buf));
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buf;
msg.msg_controllen = sizeof(control_buf);
msg.msg_flags = 0;
// Receive with MSG_CMSG_CLOEXEC
ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &msg, MSG_CMSG_CLOEXEC),
Succeeds(static_cast<ssize_t>(1)));
cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_TRUE(cmsg != nullptr);
int new_fd;
LIBC_NAMESPACE::memcpy(&new_fd, CMSG_DATA(cmsg), sizeof(int));
// Check FD_CLOEXEC
int flags = LIBC_NAMESPACE::fcntl(new_fd, F_GETFD);
ASSERT_GE(flags, 0);
ASSERT_NE(flags & FD_CLOEXEC, 0);
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(new_fd), Succeeds(0));
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, SendFails) {
const char TEST_MESSAGE[] = "connection terminated";
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);
iovec send_msg_text;
send_msg_text.iov_base =
reinterpret_cast<void *>(const_cast<char *>(TEST_MESSAGE));
send_msg_text.iov_len = MESSAGE_LEN;
msghdr send_message;
send_message.msg_name = nullptr;
send_message.msg_namelen = 0;
send_message.msg_iov = &send_msg_text;
send_message.msg_iovlen = 1;
send_message.msg_control = nullptr;
send_message.msg_controllen = 0;
send_message.msg_flags = 0;
ASSERT_THAT(LIBC_NAMESPACE::sendmsg(-1, &send_message, 0),
Fails(EBADF, static_cast<ssize_t>(-1)));
}
TEST_F(LlvmLibcSendMsgRecvMsgTest, RecvFails) {
char buffer[256];
iovec recv_msg_text;
recv_msg_text.iov_base = reinterpret_cast<void *>(buffer);
recv_msg_text.iov_len = sizeof(buffer);
msghdr recv_message;
recv_message.msg_name = nullptr;
recv_message.msg_namelen = 0;
recv_message.msg_iov = &recv_msg_text;
recv_message.msg_iovlen = 1;
recv_message.msg_control = nullptr;
recv_message.msg_controllen = 0;
recv_message.msg_flags = 0;
ASSERT_THAT(LIBC_NAMESPACE::recvmsg(-1, &recv_message, 0),
Fails(EBADF, static_cast<ssize_t>(-1)));
}