Add Unix socket tests for the MSG_CTRUNC msghdr flag.

TCP tests and the implementation will come in followup CLs.

Updates google/gvisor#206
Updates google/gvisor#207

PiperOrigin-RevId: 245121470
Change-Id: Ib50b62724d3ba0cbfb1374e1f908798431ee2b21
This commit is contained in:
Ian Gudger
2019-04-24 14:51:42 -07:00
committed by Shentubot
parent 99b877fa1d
commit 962567aafd
+438
View File
@@ -19,7 +19,9 @@
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <vector>
#include "gtest/gtest.h"
@@ -181,6 +183,162 @@ TEST_P(UnixSocketPairTest, BasicFDPassNoSpace) {
EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
}
// BasicFDPassNoSpaceMsgCtrunc sends an FD, but does not provide any space to
// receive it. It then verifies that the MSG_CTRUNC flag is set in the msghdr.
TEST_P(UnixSocketPairTest, BasicFDPassNoSpaceMsgCtrunc) {
// FIXME: Support MSG_CTRUNC.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
auto pair =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
sent_data, sizeof(sent_data)));
struct msghdr msg = {};
std::vector<char> control(CMSG_SPACE(0));
msg.msg_control = &control[0];
msg.msg_controllen = control.size();
char received_data[sizeof(sent_data)];
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(msg.msg_controllen, 0);
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
}
// BasicFDPassNullControlMsgCtrunc sends an FD and sets contradictory values for
// msg_controllen and msg_control. msg_controllen is set to the correct size to
// accomidate the FD, but msg_control is set to NULL. In this case, msg_control
// should override msg_controllen.
TEST_P(UnixSocketPairTest, BasicFDPassNullControlMsgCtrunc) {
// FIXME: Fix handling of NULL msg_control.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
auto pair =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
sent_data, sizeof(sent_data)));
struct msghdr msg = {};
msg.msg_controllen = CMSG_SPACE(1);
char received_data[sizeof(sent_data)];
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(msg.msg_controllen, 0);
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
}
// BasicFDPassNotEnoughSpaceMsgCtrunc sends an FD, but does not provide enough
// space to receive it. It then verifies that the MSG_CTRUNC flag is set in the
// msghdr.
TEST_P(UnixSocketPairTest, BasicFDPassNotEnoughSpaceMsgCtrunc) {
// FIXME: Support MSG_CTRUNC.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
auto pair =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
sent_data, sizeof(sent_data)));
struct msghdr msg = {};
std::vector<char> control(CMSG_SPACE(0) + 1);
msg.msg_control = &control[0];
msg.msg_controllen = control.size();
char received_data[sizeof(sent_data)];
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(msg.msg_controllen, 0);
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
}
// BasicThreeFDPassTruncationMsgCtrunc sends three FDs, but only provides enough
// space to receive two of them. It then verifies that the MSG_CTRUNC flag is
// set in the msghdr.
TEST_P(UnixSocketPairTest, BasicThreeFDPassTruncationMsgCtrunc) {
// FIXME: Support MSG_CTRUNC.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
auto pair1 =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
auto pair2 =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
auto pair3 =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
int sent_fds[] = {pair1->second_fd(), pair2->second_fd(), pair3->second_fd()};
ASSERT_NO_FATAL_FAILURE(
SendFDs(sockets->first_fd(), sent_fds, 3, sent_data, sizeof(sent_data)));
struct msghdr msg = {};
std::vector<char> control(CMSG_SPACE(2 * sizeof(int)));
msg.msg_control = &control[0];
msg.msg_controllen = control.size();
char received_data[sizeof(sent_data)];
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(2 * sizeof(int)));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
}
// BasicFDPassUnalignedRecv starts off by sending a single FD just like
// BasicFDPass. The difference is that when calling recvmsg, the length of the
// receive data is only aligned on a 4 byte boundry instead of the normal 8.
@@ -206,6 +364,90 @@ TEST_P(UnixSocketPairTest, BasicFDPassUnalignedRecv) {
ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
}
// BasicFDPassUnalignedRecvNoMsgTrunc sends one FD and only provides enough
// space to receive just it. (Normally the minimum amount of space one would
// provide would be enough space for two FDs.) It then verifies that the
// MSG_CTRUNC flag is not set in the msghdr.
TEST_P(UnixSocketPairTest, BasicFDPassUnalignedRecvNoMsgTrunc) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
auto pair =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
sent_data, sizeof(sent_data)));
struct msghdr msg = {};
char control[CMSG_SPACE(sizeof(int)) - sizeof(int)];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
char received_data[sizeof(sent_data)] = {};
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(msg.msg_flags, 0);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
}
// BasicTwoFDPassUnalignedRecvTruncationMsgTrunc sends two FDs, but only
// provides enough space to receive one of them. It then verifies that the
// MSG_CTRUNC flag is set in the msghdr.
TEST_P(UnixSocketPairTest, BasicTwoFDPassUnalignedRecvTruncationMsgTrunc) {
// FIXME: Support MSG_CTRUNC.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
auto pair =
ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
int sent_fds[] = {pair->first_fd(), pair->second_fd()};
ASSERT_NO_FATAL_FAILURE(
SendFDs(sockets->first_fd(), sent_fds, 2, sent_data, sizeof(sent_data)));
struct msghdr msg = {};
// CMSG_SPACE rounds up to two FDs, we only want one.
char control[CMSG_SPACE(sizeof(int)) - sizeof(int)];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
char received_data[sizeof(sent_data)] = {};
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
}
TEST_P(UnixSocketPairTest, ConcurrentBasicFDPass) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@@ -670,6 +912,202 @@ TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredRecvEndAfterSendEnd) {
EXPECT_EQ(want_creds.gid, received_creds.gid);
}
TEST_P(UnixSocketPairTest, CredPassTruncated) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
struct ucred sent_creds;
ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds());
ASSERT_NO_FATAL_FAILURE(
SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data)));
SetSoPassCred(sockets->second_fd());
struct msghdr msg = {};
char control[CMSG_SPACE(0) + sizeof(pid_t)];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
char received_data[sizeof(sent_data)] = {};
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
EXPECT_EQ(msg.msg_controllen, sizeof(control));
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, sizeof(control));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS);
pid_t pid = 0;
memcpy(&pid, CMSG_DATA(cmsg), sizeof(pid));
EXPECT_EQ(pid, sent_creds.pid);
}
// CredPassNoMsgCtrunc passes a full set of credentials. It then verifies that
// receiving the full set does not result in MSG_CTRUNC being set in the msghdr.
TEST_P(UnixSocketPairTest, CredPassNoMsgCtrunc) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
struct ucred sent_creds;
ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds());
ASSERT_NO_FATAL_FAILURE(
SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data)));
SetSoPassCred(sockets->second_fd());
struct msghdr msg = {};
char control[CMSG_SPACE(sizeof(struct ucred))];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
char received_data[sizeof(sent_data)] = {};
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
// The control message should not be truncated.
EXPECT_EQ(msg.msg_flags, 0);
EXPECT_EQ(msg.msg_controllen, sizeof(control));
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct ucred)));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS);
}
// CredPassNoSpaceMsgCtrunc passes a full set of credentials. It then receives
// the data without providing space for any credentials and verifies that
// MSG_CTRUNC is set in the msghdr.
TEST_P(UnixSocketPairTest, CredPassNoSpaceMsgCtrunc) {
// FIXME: Support MSG_CTRUNC.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
struct ucred sent_creds;
ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds());
ASSERT_NO_FATAL_FAILURE(
SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data)));
SetSoPassCred(sockets->second_fd());
struct msghdr msg = {};
char control[CMSG_SPACE(0)];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
char received_data[sizeof(sent_data)] = {};
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
// The control message should be truncated.
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
EXPECT_EQ(msg.msg_controllen, sizeof(control));
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, sizeof(control));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS);
}
// CredPassTruncatedMsgCtrunc passes a full set of credentials. It then receives
// the data while providing enough space for only the first field of the
// credentials and verifies that MSG_CTRUNC is set in the msghdr.
TEST_P(UnixSocketPairTest, CredPassTruncatedMsgCtrunc) {
// FIXME: Support MSG_CTRUNC.
SKIP_IF(IsRunningOnGvisor());
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
char sent_data[20];
RandomizeBuffer(sent_data, sizeof(sent_data));
struct ucred sent_creds;
ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds());
ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds());
ASSERT_NO_FATAL_FAILURE(
SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data)));
SetSoPassCred(sockets->second_fd());
struct msghdr msg = {};
char control[CMSG_SPACE(0) + sizeof(pid_t)];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
char received_data[sizeof(sent_data)] = {};
struct iovec iov;
iov.iov_base = received_data;
iov.iov_len = sizeof(received_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
SyscallSucceedsWithValue(sizeof(received_data)));
EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
// The control message should be truncated.
EXPECT_EQ(msg.msg_flags, MSG_CTRUNC);
EXPECT_EQ(msg.msg_controllen, sizeof(control));
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, sizeof(control));
EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET);
EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS);
}
TEST_P(UnixSocketPairTest, SoPassCred) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());