summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/fs/host/socket.go62
-rw-r--r--pkg/sentry/fs/host/socket_test.go156
-rw-r--r--runsc/boot/filter/config.go4
-rw-r--r--test/syscalls/linux/BUILD23
-rw-r--r--test/syscalls/linux/socket_abstract.cc5
-rw-r--r--test/syscalls/linux/socket_filesystem.cc5
-rw-r--r--test/syscalls/linux/socket_unix.cc1508
-rw-r--r--test/syscalls/linux/socket_unix_cmsg.cc1473
-rw-r--r--test/syscalls/linux/socket_unix_cmsg.h30
-rw-r--r--test/syscalls/linux/socket_unix_pair.cc5
10 files changed, 1650 insertions, 1621 deletions
diff --git a/pkg/sentry/fs/host/socket.go b/pkg/sentry/fs/host/socket.go
index 3ed137006..e4ec0f62c 100644
--- a/pkg/sentry/fs/host/socket.go
+++ b/pkg/sentry/fs/host/socket.go
@@ -15,6 +15,7 @@
package host
import (
+ "fmt"
"sync"
"syscall"
@@ -51,20 +52,6 @@ type ConnectedEndpoint struct {
// ref keeps track of references to a connectedEndpoint.
ref refs.AtomicRefCount
- // mu protects fd, readClosed and writeClosed.
- mu sync.RWMutex `state:"nosave"`
-
- // file is an *fd.FD containing the FD backing this endpoint. It must be
- // set to nil if it has been closed.
- file *fd.FD `state:"nosave"`
-
- // readClosed is true if the FD has read shutdown or if it has been closed.
- readClosed bool
-
- // writeClosed is true if the FD has write shutdown or if it has been
- // closed.
- writeClosed bool
-
// If srfd >= 0, it is the host FD that file was imported from.
srfd int `state:"wait"`
@@ -78,6 +65,13 @@ type ConnectedEndpoint struct {
// prevent lots of small messages from filling the real send buffer
// size on the host.
sndbuf int `state:"nosave"`
+
+ // mu protects the fields below.
+ mu sync.RWMutex `state:"nosave"`
+
+ // file is an *fd.FD containing the FD backing this endpoint. It must be
+ // set to nil if it has been closed.
+ file *fd.FD `state:"nosave"`
}
// init performs initialization required for creating new ConnectedEndpoints and
@@ -208,9 +202,6 @@ func newSocket(ctx context.Context, orgfd int, saveable bool) (*fs.File, error)
func (c *ConnectedEndpoint) Send(data [][]byte, controlMessages transport.ControlMessages, from tcpip.FullAddress) (uintptr, bool, *syserr.Error) {
c.mu.RLock()
defer c.mu.RUnlock()
- if c.writeClosed {
- return 0, false, syserr.ErrClosedForSend
- }
if !controlMessages.Empty() {
return 0, false, syserr.ErrInvalidEndpointState
@@ -244,8 +235,13 @@ func (c *ConnectedEndpoint) SendNotify() {}
// CloseSend implements transport.ConnectedEndpoint.CloseSend.
func (c *ConnectedEndpoint) CloseSend() {
c.mu.Lock()
- c.writeClosed = true
- c.mu.Unlock()
+ defer c.mu.Unlock()
+
+ if err := syscall.Shutdown(c.file.FD(), syscall.SHUT_WR); err != nil {
+ // A well-formed UDS shutdown can't fail. See
+ // net/unix/af_unix.c:unix_shutdown.
+ panic(fmt.Sprintf("failed write shutdown on host socket %+v: %v", c, err))
+ }
}
// CloseNotify implements transport.ConnectedEndpoint.CloseNotify.
@@ -255,9 +251,7 @@ func (c *ConnectedEndpoint) CloseNotify() {}
func (c *ConnectedEndpoint) Writable() bool {
c.mu.RLock()
defer c.mu.RUnlock()
- if c.writeClosed {
- return true
- }
+
return fdnotifier.NonBlockingPoll(int32(c.file.FD()), waiter.EventOut)&waiter.EventOut != 0
}
@@ -285,9 +279,6 @@ func (c *ConnectedEndpoint) EventUpdate() {
func (c *ConnectedEndpoint) Recv(data [][]byte, creds bool, numRights uintptr, peek bool) (uintptr, uintptr, transport.ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) {
c.mu.RLock()
defer c.mu.RUnlock()
- if c.readClosed {
- return 0, 0, transport.ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.ErrClosedForReceive
- }
var cm unet.ControlMessage
if numRights > 0 {
@@ -344,31 +335,34 @@ func (c *ConnectedEndpoint) RecvNotify() {}
// CloseRecv implements transport.Receiver.CloseRecv.
func (c *ConnectedEndpoint) CloseRecv() {
c.mu.Lock()
- c.readClosed = true
- c.mu.Unlock()
+ defer c.mu.Unlock()
+
+ if err := syscall.Shutdown(c.file.FD(), syscall.SHUT_RD); err != nil {
+ // A well-formed UDS shutdown can't fail. See
+ // net/unix/af_unix.c:unix_shutdown.
+ panic(fmt.Sprintf("failed read shutdown on host socket %+v: %v", c, err))
+ }
}
// Readable implements transport.Receiver.Readable.
func (c *ConnectedEndpoint) Readable() bool {
c.mu.RLock()
defer c.mu.RUnlock()
- if c.readClosed {
- return true
- }
+
return fdnotifier.NonBlockingPoll(int32(c.file.FD()), waiter.EventIn)&waiter.EventIn != 0
}
// SendQueuedSize implements transport.Receiver.SendQueuedSize.
func (c *ConnectedEndpoint) SendQueuedSize() int64 {
- // SendQueuedSize isn't supported for host sockets because we don't allow the
- // sentry to call ioctl(2).
+ // TODO(gvisor.dev/issue/273): SendQueuedSize isn't supported for host
+ // sockets because we don't allow the sentry to call ioctl(2).
return -1
}
// RecvQueuedSize implements transport.Receiver.RecvQueuedSize.
func (c *ConnectedEndpoint) RecvQueuedSize() int64 {
- // RecvQueuedSize isn't supported for host sockets because we don't allow the
- // sentry to call ioctl(2).
+ // TODO(gvisor.dev/issue/273): RecvQueuedSize isn't supported for host
+ // sockets because we don't allow the sentry to call ioctl(2).
return -1
}
diff --git a/pkg/sentry/fs/host/socket_test.go b/pkg/sentry/fs/host/socket_test.go
index 06392a65a..bc3ce5627 100644
--- a/pkg/sentry/fs/host/socket_test.go
+++ b/pkg/sentry/fs/host/socket_test.go
@@ -198,20 +198,6 @@ func TestListen(t *testing.T) {
}
}
-func TestSend(t *testing.T) {
- e := ConnectedEndpoint{writeClosed: true}
- if _, _, err := e.Send(nil, transport.ControlMessages{}, tcpip.FullAddress{}); err != syserr.ErrClosedForSend {
- t.Errorf("Got %#v.Send() = %v, want = %v", e, err, syserr.ErrClosedForSend)
- }
-}
-
-func TestRecv(t *testing.T) {
- e := ConnectedEndpoint{readClosed: true}
- if _, _, _, _, _, _, err := e.Recv(nil, false, 0, false); err != syserr.ErrClosedForReceive {
- t.Errorf("Got %#v.Recv() = %v, want = %v", e, err, syserr.ErrClosedForReceive)
- }
-}
-
func TestPasscred(t *testing.T) {
e := ConnectedEndpoint{}
if got, want := e.Passcred(), false; got != want {
@@ -244,20 +230,6 @@ func TestQueuedSize(t *testing.T) {
}
}
-func TestReadable(t *testing.T) {
- e := ConnectedEndpoint{readClosed: true}
- if got, want := e.Readable(), true; got != want {
- t.Errorf("Got %#v.Readable() = %t, want = %t", e, got, want)
- }
-}
-
-func TestWritable(t *testing.T) {
- e := ConnectedEndpoint{writeClosed: true}
- if got, want := e.Writable(), true; got != want {
- t.Errorf("Got %#v.Writable() = %t, want = %t", e, got, want)
- }
-}
-
func TestRelease(t *testing.T) {
f, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
if err != nil {
@@ -272,131 +244,3 @@ func TestRelease(t *testing.T) {
t.Errorf("got = %#v, want = %#v", c, want)
}
}
-
-func TestClose(t *testing.T) {
- type testCase struct {
- name string
- cep *ConnectedEndpoint
- addFD bool
- f func()
- want *ConnectedEndpoint
- }
-
- var tests []testCase
-
- // nil is the value used by ConnectedEndpoint to indicate a closed file.
- // Non-nil files are used to check if the file gets closed.
-
- f, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c := &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f)}
- tests = append(tests, testCase{
- name: "First CloseRecv",
- cep: c,
- addFD: false,
- f: c.CloseRecv,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, readClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f), readClosed: true}
- tests = append(tests, testCase{
- name: "Second CloseRecv",
- cep: c,
- addFD: false,
- f: c.CloseRecv,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, readClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f)}
- tests = append(tests, testCase{
- name: "First CloseSend",
- cep: c,
- addFD: false,
- f: c.CloseSend,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, writeClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f), writeClosed: true}
- tests = append(tests, testCase{
- name: "Second CloseSend",
- cep: c,
- addFD: false,
- f: c.CloseSend,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, writeClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f), writeClosed: true}
- tests = append(tests, testCase{
- name: "CloseSend then CloseRecv",
- cep: c,
- addFD: true,
- f: c.CloseRecv,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, readClosed: true, writeClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f), readClosed: true}
- tests = append(tests, testCase{
- name: "CloseRecv then CloseSend",
- cep: c,
- addFD: true,
- f: c.CloseSend,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, readClosed: true, writeClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f), readClosed: true, writeClosed: true}
- tests = append(tests, testCase{
- name: "Full close then CloseRecv",
- cep: c,
- addFD: false,
- f: c.CloseRecv,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, readClosed: true, writeClosed: true},
- })
-
- f, err = syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- t.Fatal("Creating socket:", err)
- }
- c = &ConnectedEndpoint{queue: &waiter.Queue{}, file: fd.New(f), readClosed: true, writeClosed: true}
- tests = append(tests, testCase{
- name: "Full close then CloseSend",
- cep: c,
- addFD: false,
- f: c.CloseSend,
- want: &ConnectedEndpoint{queue: c.queue, file: c.file, readClosed: true, writeClosed: true},
- })
-
- for _, test := range tests {
- if test.addFD {
- fdnotifier.AddFD(int32(test.cep.file.FD()), nil)
- }
- if test.f(); !reflect.DeepEqual(test.cep, test.want) {
- t.Errorf("%s: got = %#v, want = %#v", test.name, test.cep, test.want)
- }
- }
-}
diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go
index 652da1cef..ef2dbfad2 100644
--- a/runsc/boot/filter/config.go
+++ b/runsc/boot/filter/config.go
@@ -246,6 +246,10 @@ var allowedSyscalls = seccomp.SyscallRules{
},
syscall.SYS_SETITIMER: {},
syscall.SYS_SHUTDOWN: []seccomp.Rule{
+ // Used by fs/host to shutdown host sockets.
+ {seccomp.AllowAny{}, seccomp.AllowValue(syscall.SHUT_RD)},
+ {seccomp.AllowAny{}, seccomp.AllowValue(syscall.SHUT_WR)},
+ // Used by unet to shutdown connections.
{seccomp.AllowAny{}, seccomp.AllowValue(syscall.SHUT_RDWR)},
},
syscall.SYS_SIGALTSTACK: {},
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 7633ab162..0cb7b47b6 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -2096,6 +2096,7 @@ cc_binary(
deps = [
":socket_generic_test_cases",
":socket_test_util",
+ ":socket_unix_cmsg_test_cases",
":socket_unix_test_cases",
":unix_domain_socket_test_util",
"//test/util:test_main",
@@ -2369,6 +2370,7 @@ cc_binary(
deps = [
":socket_generic_test_cases",
":socket_test_util",
+ ":socket_unix_cmsg_test_cases",
":socket_unix_test_cases",
":unix_domain_socket_test_util",
"//test/util:test_main",
@@ -2491,6 +2493,26 @@ cc_library(
)
cc_library(
+ name = "socket_unix_cmsg_test_cases",
+ testonly = 1,
+ srcs = [
+ "socket_unix_cmsg.cc",
+ ],
+ hdrs = [
+ "socket_unix_cmsg.h",
+ ],
+ deps = [
+ ":socket_test_util",
+ ":unix_domain_socket_test_util",
+ "//test/util:test_util",
+ "//test/util:thread_util",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest",
+ ],
+ alwayslink = 1,
+)
+
+cc_library(
name = "socket_stream_blocking_test_cases",
testonly = 1,
srcs = [
@@ -2733,6 +2755,7 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_test_util",
+ ":socket_unix_cmsg_test_cases",
":socket_unix_test_cases",
":unix_domain_socket_test_util",
"//test/util:test_main",
diff --git a/test/syscalls/linux/socket_abstract.cc b/test/syscalls/linux/socket_abstract.cc
index 503ba986b..715d87b76 100644
--- a/test/syscalls/linux/socket_abstract.cc
+++ b/test/syscalls/linux/socket_abstract.cc
@@ -17,6 +17,7 @@
#include "test/syscalls/linux/socket_generic.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix.h"
+#include "test/syscalls/linux/socket_unix_cmsg.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
@@ -38,5 +39,9 @@ INSTANTIATE_TEST_SUITE_P(
AbstractUnixSockets, UnixSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+INSTANTIATE_TEST_SUITE_P(
+ AbstractUnixSockets, UnixSocketPairCmsgTest,
+ ::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_filesystem.cc b/test/syscalls/linux/socket_filesystem.cc
index e38a320f6..74e262959 100644
--- a/test/syscalls/linux/socket_filesystem.cc
+++ b/test/syscalls/linux/socket_filesystem.cc
@@ -17,6 +17,7 @@
#include "test/syscalls/linux/socket_generic.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix.h"
+#include "test/syscalls/linux/socket_unix_cmsg.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
@@ -38,5 +39,9 @@ INSTANTIATE_TEST_SUITE_P(
FilesystemUnixSockets, UnixSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+INSTANTIATE_TEST_SUITE_P(
+ FilesystemUnixSockets, UnixSocketPairCmsgTest,
+ ::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc
index 95cf8d2a3..875f0391f 100644
--- a/test/syscalls/linux/socket_unix.cc
+++ b/test/syscalls/linux/socket_unix.cc
@@ -32,6 +32,9 @@
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
+// This file contains tests specific to Unix domain sockets. It does not contain
+// tests for UDS control messages. Those belong in socket_unix_cmsg.cc.
+//
// This file is a generic socket test file. It must be built with another file
// that provides the test types.
@@ -40,1430 +43,6 @@ namespace testing {
namespace {
-TEST_P(UnixSocketPairTest, BasicFDPass) {
- 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)));
-
- char received_data[20];
- int fd = -1;
- ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data,
- sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
-}
-
-TEST_P(UnixSocketPairTest, BasicTwoFDPass) {
- 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());
- int sent_fds[] = {pair1->second_fd(), pair2->second_fd()};
-
- ASSERT_NO_FATAL_FAILURE(
- SendFDs(sockets->first_fd(), sent_fds, 2, sent_data, sizeof(sent_data)));
-
- char received_data[20];
- int received_fds[] = {-1, -1};
-
- ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 2,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd()));
- ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd()));
-}
-
-TEST_P(UnixSocketPairTest, BasicThreeFDPass) {
- 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)));
-
- char received_data[20];
- int received_fds[] = {-1, -1, -1};
-
- ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 3,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd()));
- ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd()));
- ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[2], pair3->first_fd()));
-}
-
-TEST_P(UnixSocketPairTest, BadFDPass) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- int sent_fd = -1;
-
- struct msghdr msg = {};
- char control[CMSG_SPACE(sizeof(sent_fd))];
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(sent_fd));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- memcpy(CMSG_DATA(cmsg), &sent_fd, sizeof(sent_fd));
-
- struct iovec iov;
- iov.iov_base = sent_data;
- iov.iov_len = sizeof(sent_data);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0),
- SyscallFailsWithErrno(EBADF));
-}
-
-// BasicFDPassNoSpace starts off by sending a single FD just like BasicFDPass.
-// The difference is that when calling recvmsg, no space for FDs is provided,
-// only space for the cmsg header.
-TEST_P(UnixSocketPairTest, BasicFDPassNoSpace) {
- 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)));
-
- char received_data[20];
-
- struct msghdr msg = {};
- std::vector<char> control(CMSG_SPACE(0));
- msg.msg_control = &control[0];
- msg.msg_controllen = control.size();
-
- 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(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) {
- 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(gvisor.dev/issue/207): 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) {
- 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) {
- 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.
-TEST_P(UnixSocketPairTest, BasicFDPassUnalignedRecv) {
- 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)));
-
- char received_data[20];
- int fd = -1;
- ASSERT_NO_FATAL_FAILURE(RecvSingleFDUnaligned(
- sockets->second_fd(), &fd, received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- 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) {
- 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());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- int sockfd1 = sockets->first_fd();
- auto recv_func = [sockfd1, sent_data]() {
- char received_data[20];
- int fd = -1;
- RecvSingleFD(sockfd1, &fd, received_data, sizeof(received_data));
- ASSERT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
- char buf[20];
- ASSERT_THAT(ReadFd(fd, buf, sizeof(buf)),
- SyscallSucceedsWithValue(sizeof(buf)));
- ASSERT_THAT(WriteFd(fd, buf, sizeof(buf)),
- SyscallSucceedsWithValue(sizeof(buf)));
- };
-
- auto pair =
- ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->second_fd(), pair->second_fd(),
- sent_data, sizeof(sent_data)));
-
- ScopedThread t(recv_func);
-
- RandomizeBuffer(sent_data, sizeof(sent_data));
- ASSERT_THAT(WriteFd(pair->first_fd(), sent_data, sizeof(sent_data)),
- SyscallSucceedsWithValue(sizeof(sent_data)));
-
- char received_data[20];
- ASSERT_THAT(ReadFd(pair->first_fd(), received_data, sizeof(received_data)),
- SyscallSucceedsWithValue(sizeof(received_data)));
-
- t.Join();
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-}
-
-// FDPassNoRecv checks that the control message can be safely ignored by using
-// read(2) instead of recvmsg(2).
-TEST_P(UnixSocketPairTest, FDPassNoRecv) {
- 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)));
-
- // Read while ignoring the passed FD.
- char received_data[20];
- ASSERT_THAT(
- ReadFd(sockets->second_fd(), received_data, sizeof(received_data)),
- SyscallSucceedsWithValue(sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- // Check that the socket still works for reads and writes.
- ASSERT_NO_FATAL_FAILURE(
- TransferTest(sockets->first_fd(), sockets->second_fd()));
-}
-
-// FDPassInterspersed1 checks that sent control messages cannot be read before
-// their associated data has been read.
-TEST_P(UnixSocketPairTest, FDPassInterspersed1) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char written_data[20];
- RandomizeBuffer(written_data, sizeof(written_data));
-
- ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)),
- SyscallSucceedsWithValue(sizeof(written_data)));
-
- 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)));
-
- // Check that we don't get a control message, but do get the data.
- char received_data[20];
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data));
- EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data)));
-}
-
-// FDPassInterspersed2 checks that sent control messages cannot be read after
-// their assocated data has been read while ignoring the control message by
-// using read(2) instead of recvmsg(2).
-TEST_P(UnixSocketPairTest, FDPassInterspersed2) {
- 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)));
-
- char written_data[20];
- RandomizeBuffer(written_data, sizeof(written_data));
- ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)),
- SyscallSucceedsWithValue(sizeof(written_data)));
-
- char received_data[20];
- ASSERT_THAT(
- ReadFd(sockets->second_fd(), received_data, sizeof(received_data)),
- SyscallSucceedsWithValue(sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- ASSERT_NO_FATAL_FAILURE(
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
- EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data)));
-}
-
-TEST_P(UnixSocketPairTest, FDPassNotCoalesced) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data1[20];
- RandomizeBuffer(sent_data1, sizeof(sent_data1));
-
- auto pair1 =
- ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(),
- sent_data1, sizeof(sent_data1)));
-
- char sent_data2[20];
- RandomizeBuffer(sent_data2, sizeof(sent_data2));
-
- auto pair2 =
- ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(),
- sent_data2, sizeof(sent_data2)));
-
- char received_data1[sizeof(sent_data1) + sizeof(sent_data2)];
- int received_fd1 = -1;
-
- RecvSingleFD(sockets->second_fd(), &received_fd1, received_data1,
- sizeof(received_data1), sizeof(sent_data1));
-
- EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1)));
- TransferTest(pair1->first_fd(), pair1->second_fd());
-
- char received_data2[sizeof(sent_data1) + sizeof(sent_data2)];
- int received_fd2 = -1;
-
- RecvSingleFD(sockets->second_fd(), &received_fd2, received_data2,
- sizeof(received_data2), sizeof(sent_data2));
-
- EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2)));
- TransferTest(pair2->first_fd(), pair2->second_fd());
-}
-
-TEST_P(UnixSocketPairTest, FDPassPeek) {
- 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)));
-
- char peek_data[20];
- int peek_fd = -1;
- PeekSingleFD(sockets->second_fd(), &peek_fd, peek_data, sizeof(peek_data));
- EXPECT_EQ(0, memcmp(sent_data, peek_data, sizeof(sent_data)));
- TransferTest(peek_fd, pair->first_fd());
- EXPECT_THAT(close(peek_fd), SyscallSucceeds());
-
- char received_data[20];
- int received_fd = -1;
- RecvSingleFD(sockets->second_fd(), &received_fd, received_data,
- sizeof(received_data));
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
- TransferTest(received_fd, pair->first_fd());
- EXPECT_THAT(close(received_fd), SyscallSucceeds());
-}
-
-TEST_P(UnixSocketPairTest, BasicCredPass) {
- 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());
-
- char received_data[20];
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
- EXPECT_EQ(sent_creds.pid, received_creds.pid);
- EXPECT_EQ(sent_creds.uid, received_creds.uid);
- EXPECT_EQ(sent_creds.gid, received_creds.gid);
-}
-
-TEST_P(UnixSocketPairTest, SendNullCredsBeforeSoPassCredRecvEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- ASSERT_NO_FATAL_FAILURE(
- SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
-
- SetSoPassCred(sockets->second_fd());
-
- char received_data[20];
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds {
- 0, 65534, 65534
- };
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-}
-
-TEST_P(UnixSocketPairTest, SendNullCredsAfterSoPassCredRecvEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- SetSoPassCred(sockets->second_fd());
-
- ASSERT_NO_FATAL_FAILURE(
- SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
-
- char received_data[20];
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds;
- ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-}
-
-TEST_P(UnixSocketPairTest, SendNullCredsBeforeSoPassCredSendEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- ASSERT_NO_FATAL_FAILURE(
- SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
-
- SetSoPassCred(sockets->first_fd());
-
- char received_data[20];
- ASSERT_NO_FATAL_FAILURE(
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-}
-
-TEST_P(UnixSocketPairTest, SendNullCredsAfterSoPassCredSendEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- SetSoPassCred(sockets->first_fd());
-
- ASSERT_NO_FATAL_FAILURE(
- SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
-
- char received_data[20];
- ASSERT_NO_FATAL_FAILURE(
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-}
-
-TEST_P(UnixSocketPairTest, SendNullCredsBeforeSoPassCredRecvEndAfterSendEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- SetSoPassCred(sockets->first_fd());
-
- ASSERT_NO_FATAL_FAILURE(
- SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
-
- SetSoPassCred(sockets->second_fd());
-
- char received_data[20];
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds;
- ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-}
-
-TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredRecvEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
- SyscallSucceedsWithValue(sizeof(sent_data)));
-
- SetSoPassCred(sockets->second_fd());
-
- char received_data[20];
-
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds {
- 0, 65534, 65534
- };
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-}
-
-TEST_P(UnixSocketPairTest, WriteAfterSoPassCredRecvEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- SetSoPassCred(sockets->second_fd());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
- ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
- SyscallSucceedsWithValue(sizeof(sent_data)));
-
- char received_data[20];
-
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds;
- ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-}
-
-TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredSendEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
- SyscallSucceedsWithValue(sizeof(sent_data)));
-
- SetSoPassCred(sockets->first_fd());
-
- char received_data[20];
- ASSERT_NO_FATAL_FAILURE(
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-}
-
-TEST_P(UnixSocketPairTest, WriteAfterSoPassCredSendEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- SetSoPassCred(sockets->first_fd());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
- SyscallSucceedsWithValue(sizeof(sent_data)));
-
- char received_data[20];
- ASSERT_NO_FATAL_FAILURE(
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-}
-
-TEST_P(UnixSocketPairTest, WriteBeforeSoPassCredRecvEndAfterSendEnd) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- SetSoPassCred(sockets->first_fd());
-
- ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
- SyscallSucceedsWithValue(sizeof(sent_data)));
-
- SetSoPassCred(sockets->second_fd());
-
- char received_data[20];
-
- struct ucred received_creds;
- ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
- received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds;
- ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- 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) {
- 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) {
- 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());
-
- int opt;
- socklen_t optLen = sizeof(opt);
- EXPECT_THAT(
- getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
- SyscallSucceeds());
- EXPECT_FALSE(opt);
-
- optLen = sizeof(opt);
- EXPECT_THAT(
- getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
- SyscallSucceeds());
- EXPECT_FALSE(opt);
-
- SetSoPassCred(sockets->first_fd());
-
- optLen = sizeof(opt);
- EXPECT_THAT(
- getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
- SyscallSucceeds());
- EXPECT_TRUE(opt);
-
- optLen = sizeof(opt);
- EXPECT_THAT(
- getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
- SyscallSucceeds());
- EXPECT_FALSE(opt);
-
- int zero = 0;
- EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &zero,
- sizeof(zero)),
- SyscallSucceeds());
-
- optLen = sizeof(opt);
- EXPECT_THAT(
- getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
- SyscallSucceeds());
- EXPECT_FALSE(opt);
-
- optLen = sizeof(opt);
- EXPECT_THAT(
- getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
- SyscallSucceeds());
- EXPECT_FALSE(opt);
-}
-
-TEST_P(UnixSocketPairTest, NoDataCredPass) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- char sent_data[20];
- RandomizeBuffer(sent_data, sizeof(sent_data));
-
- struct msghdr msg = {};
-
- struct iovec iov;
- iov.iov_base = sent_data;
- iov.iov_len = sizeof(sent_data);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- char control[CMSG_SPACE(0)];
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
- cmsg->cmsg_len = CMSG_LEN(0);
-
- ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST_P(UnixSocketPairTest, NoPassCred) {
- 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)));
-
- char received_data[20];
-
- ASSERT_NO_FATAL_FAILURE(
- RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-}
-
-TEST_P(UnixSocketPairTest, CredAndFDPass) {
- 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());
-
- auto pair =
- ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
-
- ASSERT_NO_FATAL_FAILURE(SendCredsAndFD(sockets->first_fd(), sent_creds,
- pair->second_fd(), sent_data,
- sizeof(sent_data)));
-
- SetSoPassCred(sockets->second_fd());
-
- char received_data[20];
- struct ucred received_creds;
- int fd = -1;
- ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds,
- &fd, received_data,
- sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- EXPECT_EQ(sent_creds.pid, received_creds.pid);
- EXPECT_EQ(sent_creds.uid, received_creds.uid);
- EXPECT_EQ(sent_creds.gid, received_creds.gid);
-
- ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
-}
-
-TEST_P(UnixSocketPairTest, FDPassBeforeSoPassCred) {
- 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)));
-
- SetSoPassCred(sockets->second_fd());
-
- char received_data[20];
- struct ucred received_creds;
- int fd = -1;
- ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds,
- &fd, received_data,
- sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds {
- 0, 65534, 65534
- };
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-
- ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
-}
-
-TEST_P(UnixSocketPairTest, FDPassAfterSoPassCred) {
- 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());
-
- SetSoPassCred(sockets->second_fd());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
- sent_data, sizeof(sent_data)));
-
- char received_data[20];
- struct ucred received_creds;
- int fd = -1;
- ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds,
- &fd, received_data,
- sizeof(received_data)));
-
- EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
-
- struct ucred want_creds;
- ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
- ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
-
- EXPECT_EQ(want_creds.pid, received_creds.pid);
- EXPECT_EQ(want_creds.uid, received_creds.uid);
- EXPECT_EQ(want_creds.gid, received_creds.gid);
-
- ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
-}
-
-TEST_P(UnixSocketPairTest, CloexecDroppedWhenFDPassed) {
- 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 | SOCK_CLOEXEC).Create());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
- sent_data, sizeof(sent_data)));
-
- char received_data[20];
- int fd = -1;
- ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data,
- sizeof(received_data)));
-
- EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(0));
-}
-
-TEST_P(UnixSocketPairTest, CloexecRecvFDPass) {
- 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))];
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- struct iovec iov;
- char received_data[20];
- 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, MSG_CMSG_CLOEXEC),
- SyscallSucceedsWithValue(sizeof(received_data)));
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- ASSERT_NE(cmsg, nullptr);
- ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
- ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET);
- ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
-
- int fd = -1;
- memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
-
- EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC));
-}
-
-TEST_P(UnixSocketPairTest, FDPassAfterSoPassCredWithoutCredSpace) {
- 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());
-
- SetSoPassCred(sockets->second_fd());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
- sent_data, sizeof(sent_data)));
-
- struct msghdr msg = {};
- char control[CMSG_LEN(0)];
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- char received_data[20];
- 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);
-}
-
-// This test will validate that MSG_CTRUNC as an input flag to recvmsg will
-// not appear as an output flag on the control message when truncation doesn't
-// happen.
-TEST_P(UnixSocketPairTest, MsgCtruncInputIsNoop) {
- 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)) /* we're passing a single fd */];
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- struct iovec iov;
- char received_data[20];
- 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, MSG_CTRUNC),
- SyscallSucceedsWithValue(sizeof(received_data)));
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- ASSERT_NE(cmsg, nullptr);
- ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
- ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET);
- ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
-
- // Now we should verify that MSG_CTRUNC wasn't set as an output flag.
- EXPECT_EQ(msg.msg_flags & MSG_CTRUNC, 0);
-}
-
-TEST_P(UnixSocketPairTest, FDPassAfterSoPassCredWithoutCredHeaderSpace) {
- 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());
-
- SetSoPassCred(sockets->second_fd());
-
- ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
- sent_data, sizeof(sent_data)));
-
- struct msghdr msg = {};
- char control[CMSG_LEN(0) / 2];
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- char received_data[20];
- 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, 0);
-}
-
TEST_P(UnixSocketPairTest, InvalidGetSockOpt) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int opt;
@@ -1519,6 +98,14 @@ TEST_P(UnixSocketPairTest, RecvmmsgTimeoutAfterRecv) {
TEST_P(UnixSocketPairTest, TIOCINQSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ if (IsRunningOnGvisor()) {
+ // TODO(gvisor.dev/issue/273): Inherited host UDS don't support TIOCINQ.
+ // Skip the test.
+ int size = -1;
+ int ret = ioctl(sockets->first_fd(), TIOCINQ, &size);
+ SKIP_IF(ret == -1 && errno == ENOTTY);
+ }
+
int size = -1;
EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds());
EXPECT_EQ(size, 0);
@@ -1544,6 +131,14 @@ TEST_P(UnixSocketPairTest, TIOCINQSucceeds) {
TEST_P(UnixSocketPairTest, TIOCOUTQSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ if (IsRunningOnGvisor()) {
+ // TODO(gvisor.dev/issue/273): Inherited host UDS don't support TIOCOUTQ.
+ // Skip the test.
+ int size = -1;
+ int ret = ioctl(sockets->second_fd(), TIOCOUTQ, &size);
+ SKIP_IF(ret == -1 && errno == ENOTTY);
+ }
+
int size = -1;
EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds());
EXPECT_EQ(size, 0);
@@ -1580,19 +175,70 @@ TEST_P(UnixSocketPairTest, NetdeviceIoctlsSucceed) {
}
}
-TEST_P(UnixSocketPairTest, SocketShutdown) {
+TEST_P(UnixSocketPairTest, Shutdown) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
- char buf[20];
+
const std::string data = "abc";
- ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), 3),
- SyscallSucceedsWithValue(3));
+ ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), data.size()),
+ SyscallSucceedsWithValue(data.size()));
+
ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
// Shutting down a socket does not clear the buffer.
- ASSERT_THAT(ReadFd(sockets->second_fd(), buf, 3),
- SyscallSucceedsWithValue(3));
- EXPECT_EQ(data, absl::string_view(buf, 3));
+ char buf[3];
+ ASSERT_THAT(ReadFd(sockets->second_fd(), buf, data.size()),
+ SyscallSucceedsWithValue(data.size()));
+ EXPECT_EQ(data, absl::string_view(buf, data.size()));
+}
+
+TEST_P(UnixSocketPairTest, ShutdownRead) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RD), SyscallSucceeds());
+
+ // When the socket is shutdown for read, read behavior varies between
+ // different socket types. This is covered by the various ReadOneSideClosed
+ // test cases.
+
+ // ... and the peer cannot write.
+ const std::string data = "abc";
+ EXPECT_THAT(WriteFd(sockets->second_fd(), data.c_str(), data.size()),
+ SyscallFailsWithErrno(EPIPE));
+
+ // ... but the socket can still write.
+ ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), data.size()),
+ SyscallSucceedsWithValue(data.size()));
+
+ // ... and the peer can still read.
+ char buf[3];
+ EXPECT_THAT(ReadFd(sockets->second_fd(), buf, data.size()),
+ SyscallSucceedsWithValue(data.size()));
+ EXPECT_EQ(data, absl::string_view(buf, data.size()));
+}
+
+TEST_P(UnixSocketPairTest, ShutdownWrite) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_WR), SyscallSucceeds());
+
+ // When the socket is shutdown for write, it cannot write.
+ const std::string data = "abc";
+ EXPECT_THAT(WriteFd(sockets->first_fd(), data.c_str(), data.size()),
+ SyscallFailsWithErrno(EPIPE));
+
+ // ... and the peer read behavior varies between different socket types. This
+ // is covered by the various ReadOneSideClosed test cases.
+
+ // ... but the peer can still write.
+ char buf[3];
+ ASSERT_THAT(WriteFd(sockets->second_fd(), data.c_str(), data.size()),
+ SyscallSucceedsWithValue(data.size()));
+
+ // ... and the socket can still read.
+ EXPECT_THAT(ReadFd(sockets->first_fd(), buf, data.size()),
+ SyscallSucceedsWithValue(data.size()));
+ EXPECT_EQ(data, absl::string_view(buf, data.size()));
}
TEST_P(UnixSocketPairTest, SocketReopenFromProcfs) {
diff --git a/test/syscalls/linux/socket_unix_cmsg.cc b/test/syscalls/linux/socket_unix_cmsg.cc
new file mode 100644
index 000000000..b0ab26847
--- /dev/null
+++ b/test/syscalls/linux/socket_unix_cmsg.cc
@@ -0,0 +1,1473 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/syscalls/linux/socket_unix_cmsg.h"
+
+#include <errno.h>
+#include <net/if.h>
+#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"
+#include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
+#include "test/syscalls/linux/socket_test_util.h"
+#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
+
+// This file contains tests for control message in Unix domain sockets.
+//
+// This file is a generic socket test file. It must be built with another file
+// that provides the test types.
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+TEST_P(UnixSocketPairCmsgTest, BasicFDPass) {
+ 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)));
+
+ char received_data[20];
+ int fd = -1;
+ ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data,
+ sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
+}
+
+TEST_P(UnixSocketPairCmsgTest, BasicTwoFDPass) {
+ 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());
+ int sent_fds[] = {pair1->second_fd(), pair2->second_fd()};
+
+ ASSERT_NO_FATAL_FAILURE(
+ SendFDs(sockets->first_fd(), sent_fds, 2, sent_data, sizeof(sent_data)));
+
+ char received_data[20];
+ int received_fds[] = {-1, -1};
+
+ ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 2,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd()));
+ ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd()));
+}
+
+TEST_P(UnixSocketPairCmsgTest, BasicThreeFDPass) {
+ 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)));
+
+ char received_data[20];
+ int received_fds[] = {-1, -1, -1};
+
+ ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 3,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd()));
+ ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd()));
+ ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[2], pair3->first_fd()));
+}
+
+TEST_P(UnixSocketPairCmsgTest, BadFDPass) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ int sent_fd = -1;
+
+ struct msghdr msg = {};
+ char control[CMSG_SPACE(sizeof(sent_fd))];
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(sent_fd));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(cmsg), &sent_fd, sizeof(sent_fd));
+
+ struct iovec iov;
+ iov.iov_base = sent_data;
+ iov.iov_len = sizeof(sent_data);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0),
+ SyscallFailsWithErrno(EBADF));
+}
+
+// BasicFDPassNoSpace starts off by sending a single FD just like BasicFDPass.
+// The difference is that when calling recvmsg, no space for FDs is provided,
+// only space for the cmsg header.
+TEST_P(UnixSocketPairCmsgTest, BasicFDPassNoSpace) {
+ 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)));
+
+ char received_data[20];
+
+ struct msghdr msg = {};
+ std::vector<char> control(CMSG_SPACE(0));
+ msg.msg_control = &control[0];
+ msg.msg_controllen = control.size();
+
+ 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(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(UnixSocketPairCmsgTest, BasicFDPassNoSpaceMsgCtrunc) {
+ 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(UnixSocketPairCmsgTest, BasicFDPassNullControlMsgCtrunc) {
+ // FIXME(gvisor.dev/issue/207): 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(UnixSocketPairCmsgTest, BasicFDPassNotEnoughSpaceMsgCtrunc) {
+ 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(UnixSocketPairCmsgTest, BasicThreeFDPassTruncationMsgCtrunc) {
+ 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.
+TEST_P(UnixSocketPairCmsgTest, BasicFDPassUnalignedRecv) {
+ 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)));
+
+ char received_data[20];
+ int fd = -1;
+ ASSERT_NO_FATAL_FAILURE(RecvSingleFDUnaligned(
+ sockets->second_fd(), &fd, received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ 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(UnixSocketPairCmsgTest, 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(UnixSocketPairCmsgTest, BasicTwoFDPassUnalignedRecvTruncationMsgTrunc) {
+ 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(UnixSocketPairCmsgTest, ConcurrentBasicFDPass) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ int sockfd1 = sockets->first_fd();
+ auto recv_func = [sockfd1, sent_data]() {
+ char received_data[20];
+ int fd = -1;
+ RecvSingleFD(sockfd1, &fd, received_data, sizeof(received_data));
+ ASSERT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+ char buf[20];
+ ASSERT_THAT(ReadFd(fd, buf, sizeof(buf)),
+ SyscallSucceedsWithValue(sizeof(buf)));
+ ASSERT_THAT(WriteFd(fd, buf, sizeof(buf)),
+ SyscallSucceedsWithValue(sizeof(buf)));
+ };
+
+ auto pair =
+ ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->second_fd(), pair->second_fd(),
+ sent_data, sizeof(sent_data)));
+
+ ScopedThread t(recv_func);
+
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+ ASSERT_THAT(WriteFd(pair->first_fd(), sent_data, sizeof(sent_data)),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ char received_data[20];
+ ASSERT_THAT(ReadFd(pair->first_fd(), received_data, sizeof(received_data)),
+ SyscallSucceedsWithValue(sizeof(received_data)));
+
+ t.Join();
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+}
+
+// FDPassNoRecv checks that the control message can be safely ignored by using
+// read(2) instead of recvmsg(2).
+TEST_P(UnixSocketPairCmsgTest, FDPassNoRecv) {
+ 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)));
+
+ // Read while ignoring the passed FD.
+ char received_data[20];
+ ASSERT_THAT(
+ ReadFd(sockets->second_fd(), received_data, sizeof(received_data)),
+ SyscallSucceedsWithValue(sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ // Check that the socket still works for reads and writes.
+ ASSERT_NO_FATAL_FAILURE(
+ TransferTest(sockets->first_fd(), sockets->second_fd()));
+}
+
+// FDPassInterspersed1 checks that sent control messages cannot be read before
+// their associated data has been read.
+TEST_P(UnixSocketPairCmsgTest, FDPassInterspersed1) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char written_data[20];
+ RandomizeBuffer(written_data, sizeof(written_data));
+
+ ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)),
+ SyscallSucceedsWithValue(sizeof(written_data)));
+
+ 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)));
+
+ // Check that we don't get a control message, but do get the data.
+ char received_data[20];
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data));
+ EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data)));
+}
+
+// FDPassInterspersed2 checks that sent control messages cannot be read after
+// their assocated data has been read while ignoring the control message by
+// using read(2) instead of recvmsg(2).
+TEST_P(UnixSocketPairCmsgTest, FDPassInterspersed2) {
+ 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)));
+
+ char written_data[20];
+ RandomizeBuffer(written_data, sizeof(written_data));
+ ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)),
+ SyscallSucceedsWithValue(sizeof(written_data)));
+
+ char received_data[20];
+ ASSERT_THAT(
+ ReadFd(sockets->second_fd(), received_data, sizeof(received_data)),
+ SyscallSucceedsWithValue(sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
+ EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data)));
+}
+
+TEST_P(UnixSocketPairCmsgTest, FDPassNotCoalesced) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data1[20];
+ RandomizeBuffer(sent_data1, sizeof(sent_data1));
+
+ auto pair1 =
+ ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(),
+ sent_data1, sizeof(sent_data1)));
+
+ char sent_data2[20];
+ RandomizeBuffer(sent_data2, sizeof(sent_data2));
+
+ auto pair2 =
+ ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(),
+ sent_data2, sizeof(sent_data2)));
+
+ char received_data1[sizeof(sent_data1) + sizeof(sent_data2)];
+ int received_fd1 = -1;
+
+ RecvSingleFD(sockets->second_fd(), &received_fd1, received_data1,
+ sizeof(received_data1), sizeof(sent_data1));
+
+ EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1)));
+ TransferTest(pair1->first_fd(), pair1->second_fd());
+
+ char received_data2[sizeof(sent_data1) + sizeof(sent_data2)];
+ int received_fd2 = -1;
+
+ RecvSingleFD(sockets->second_fd(), &received_fd2, received_data2,
+ sizeof(received_data2), sizeof(sent_data2));
+
+ EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2)));
+ TransferTest(pair2->first_fd(), pair2->second_fd());
+}
+
+TEST_P(UnixSocketPairCmsgTest, FDPassPeek) {
+ 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)));
+
+ char peek_data[20];
+ int peek_fd = -1;
+ PeekSingleFD(sockets->second_fd(), &peek_fd, peek_data, sizeof(peek_data));
+ EXPECT_EQ(0, memcmp(sent_data, peek_data, sizeof(sent_data)));
+ TransferTest(peek_fd, pair->first_fd());
+ EXPECT_THAT(close(peek_fd), SyscallSucceeds());
+
+ char received_data[20];
+ int received_fd = -1;
+ RecvSingleFD(sockets->second_fd(), &received_fd, received_data,
+ sizeof(received_data));
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+ TransferTest(received_fd, pair->first_fd());
+ EXPECT_THAT(close(received_fd), SyscallSucceeds());
+}
+
+TEST_P(UnixSocketPairCmsgTest, BasicCredPass) {
+ 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());
+
+ char received_data[20];
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+ EXPECT_EQ(sent_creds.pid, received_creds.pid);
+ EXPECT_EQ(sent_creds.uid, received_creds.uid);
+ EXPECT_EQ(sent_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, SendNullCredsBeforeSoPassCredRecvEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ ASSERT_NO_FATAL_FAILURE(
+ SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
+
+ SetSoPassCred(sockets->second_fd());
+
+ char received_data[20];
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds {
+ 0, 65534, 65534
+ };
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, SendNullCredsAfterSoPassCredRecvEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ SetSoPassCred(sockets->second_fd());
+
+ ASSERT_NO_FATAL_FAILURE(
+ SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
+
+ char received_data[20];
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds;
+ ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, SendNullCredsBeforeSoPassCredSendEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ ASSERT_NO_FATAL_FAILURE(
+ SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
+
+ SetSoPassCred(sockets->first_fd());
+
+ char received_data[20];
+ ASSERT_NO_FATAL_FAILURE(
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+}
+
+TEST_P(UnixSocketPairCmsgTest, SendNullCredsAfterSoPassCredSendEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ SetSoPassCred(sockets->first_fd());
+
+ ASSERT_NO_FATAL_FAILURE(
+ SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
+
+ char received_data[20];
+ ASSERT_NO_FATAL_FAILURE(
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+}
+
+TEST_P(UnixSocketPairCmsgTest,
+ SendNullCredsBeforeSoPassCredRecvEndAfterSendEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ SetSoPassCred(sockets->first_fd());
+
+ ASSERT_NO_FATAL_FAILURE(
+ SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data)));
+
+ SetSoPassCred(sockets->second_fd());
+
+ char received_data[20];
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds;
+ ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, WriteBeforeSoPassCredRecvEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ SetSoPassCred(sockets->second_fd());
+
+ char received_data[20];
+
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds {
+ 0, 65534, 65534
+ };
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, WriteAfterSoPassCredRecvEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ SetSoPassCred(sockets->second_fd());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+ ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ char received_data[20];
+
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds;
+ ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, WriteBeforeSoPassCredSendEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ SetSoPassCred(sockets->first_fd());
+
+ char received_data[20];
+ ASSERT_NO_FATAL_FAILURE(
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+}
+
+TEST_P(UnixSocketPairCmsgTest, WriteAfterSoPassCredSendEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ SetSoPassCred(sockets->first_fd());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ char received_data[20];
+ ASSERT_NO_FATAL_FAILURE(
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+}
+
+TEST_P(UnixSocketPairCmsgTest, WriteBeforeSoPassCredRecvEndAfterSendEnd) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ SetSoPassCred(sockets->first_fd());
+
+ ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ SetSoPassCred(sockets->second_fd());
+
+ char received_data[20];
+
+ struct ucred received_creds;
+ ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds,
+ received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds;
+ ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+}
+
+TEST_P(UnixSocketPairCmsgTest, 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(UnixSocketPairCmsgTest, 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(UnixSocketPairCmsgTest, CredPassNoSpaceMsgCtrunc) {
+ 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(UnixSocketPairCmsgTest, CredPassTruncatedMsgCtrunc) {
+ 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(UnixSocketPairCmsgTest, SoPassCred) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ int opt;
+ socklen_t optLen = sizeof(opt);
+ EXPECT_THAT(
+ getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
+ SyscallSucceeds());
+ EXPECT_FALSE(opt);
+
+ optLen = sizeof(opt);
+ EXPECT_THAT(
+ getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
+ SyscallSucceeds());
+ EXPECT_FALSE(opt);
+
+ SetSoPassCred(sockets->first_fd());
+
+ optLen = sizeof(opt);
+ EXPECT_THAT(
+ getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
+ SyscallSucceeds());
+ EXPECT_TRUE(opt);
+
+ optLen = sizeof(opt);
+ EXPECT_THAT(
+ getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
+ SyscallSucceeds());
+ EXPECT_FALSE(opt);
+
+ int zero = 0;
+ EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &zero,
+ sizeof(zero)),
+ SyscallSucceeds());
+
+ optLen = sizeof(opt);
+ EXPECT_THAT(
+ getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
+ SyscallSucceeds());
+ EXPECT_FALSE(opt);
+
+ optLen = sizeof(opt);
+ EXPECT_THAT(
+ getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen),
+ SyscallSucceeds());
+ EXPECT_FALSE(opt);
+}
+
+TEST_P(UnixSocketPairCmsgTest, NoDataCredPass) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ char sent_data[20];
+ RandomizeBuffer(sent_data, sizeof(sent_data));
+
+ struct msghdr msg = {};
+
+ struct iovec iov;
+ iov.iov_base = sent_data;
+ iov.iov_len = sizeof(sent_data);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char control[CMSG_SPACE(0)];
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = CMSG_LEN(0);
+
+ ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST_P(UnixSocketPairCmsgTest, NoPassCred) {
+ 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)));
+
+ char received_data[20];
+
+ ASSERT_NO_FATAL_FAILURE(
+ RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+}
+
+TEST_P(UnixSocketPairCmsgTest, CredAndFDPass) {
+ 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());
+
+ auto pair =
+ ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create());
+
+ ASSERT_NO_FATAL_FAILURE(SendCredsAndFD(sockets->first_fd(), sent_creds,
+ pair->second_fd(), sent_data,
+ sizeof(sent_data)));
+
+ SetSoPassCred(sockets->second_fd());
+
+ char received_data[20];
+ struct ucred received_creds;
+ int fd = -1;
+ ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds,
+ &fd, received_data,
+ sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ EXPECT_EQ(sent_creds.pid, received_creds.pid);
+ EXPECT_EQ(sent_creds.uid, received_creds.uid);
+ EXPECT_EQ(sent_creds.gid, received_creds.gid);
+
+ ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
+}
+
+TEST_P(UnixSocketPairCmsgTest, FDPassBeforeSoPassCred) {
+ 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)));
+
+ SetSoPassCred(sockets->second_fd());
+
+ char received_data[20];
+ struct ucred received_creds;
+ int fd = -1;
+ ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds,
+ &fd, received_data,
+ sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds {
+ 0, 65534, 65534
+ };
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+
+ ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
+}
+
+TEST_P(UnixSocketPairCmsgTest, FDPassAfterSoPassCred) {
+ 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());
+
+ SetSoPassCred(sockets->second_fd());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
+ sent_data, sizeof(sent_data)));
+
+ char received_data[20];
+ struct ucred received_creds;
+ int fd = -1;
+ ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds,
+ &fd, received_data,
+ sizeof(received_data)));
+
+ EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
+
+ struct ucred want_creds;
+ ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds());
+ ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds());
+
+ EXPECT_EQ(want_creds.pid, received_creds.pid);
+ EXPECT_EQ(want_creds.uid, received_creds.uid);
+ EXPECT_EQ(want_creds.gid, received_creds.gid);
+
+ ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd()));
+}
+
+TEST_P(UnixSocketPairCmsgTest, CloexecDroppedWhenFDPassed) {
+ 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 | SOCK_CLOEXEC).Create());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
+ sent_data, sizeof(sent_data)));
+
+ char received_data[20];
+ int fd = -1;
+ ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data,
+ sizeof(received_data)));
+
+ EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(0));
+}
+
+TEST_P(UnixSocketPairCmsgTest, CloexecRecvFDPass) {
+ 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))];
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ struct iovec iov;
+ char received_data[20];
+ 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, MSG_CMSG_CLOEXEC),
+ SyscallSucceedsWithValue(sizeof(received_data)));
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ ASSERT_NE(cmsg, nullptr);
+ ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
+ ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET);
+ ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
+
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
+
+ EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC));
+}
+
+TEST_P(UnixSocketPairCmsgTest, FDPassAfterSoPassCredWithoutCredSpace) {
+ 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());
+
+ SetSoPassCred(sockets->second_fd());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
+ sent_data, sizeof(sent_data)));
+
+ struct msghdr msg = {};
+ char control[CMSG_LEN(0)];
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ char received_data[20];
+ 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);
+}
+
+// This test will validate that MSG_CTRUNC as an input flag to recvmsg will
+// not appear as an output flag on the control message when truncation doesn't
+// happen.
+TEST_P(UnixSocketPairCmsgTest, MsgCtruncInputIsNoop) {
+ 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)) /* we're passing a single fd */];
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ struct iovec iov;
+ char received_data[20];
+ 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, MSG_CTRUNC),
+ SyscallSucceedsWithValue(sizeof(received_data)));
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ ASSERT_NE(cmsg, nullptr);
+ ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int)));
+ ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET);
+ ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS);
+
+ // Now we should verify that MSG_CTRUNC wasn't set as an output flag.
+ EXPECT_EQ(msg.msg_flags & MSG_CTRUNC, 0);
+}
+
+TEST_P(UnixSocketPairCmsgTest, FDPassAfterSoPassCredWithoutCredHeaderSpace) {
+ 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());
+
+ SetSoPassCred(sockets->second_fd());
+
+ ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(),
+ sent_data, sizeof(sent_data)));
+
+ struct msghdr msg = {};
+ char control[CMSG_LEN(0) / 2];
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ char received_data[20];
+ 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, 0);
+}
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_unix_cmsg.h b/test/syscalls/linux/socket_unix_cmsg.h
new file mode 100644
index 000000000..431606903
--- /dev/null
+++ b/test/syscalls/linux/socket_unix_cmsg.h
@@ -0,0 +1,30 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_
+#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_
+
+#include "test/syscalls/linux/socket_test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// Test fixture for tests that apply to pairs of connected unix sockets about
+// control messages.
+using UnixSocketPairCmsgTest = SocketPairTest;
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_
diff --git a/test/syscalls/linux/socket_unix_pair.cc b/test/syscalls/linux/socket_unix_pair.cc
index bacfc11e4..411fb4518 100644
--- a/test/syscalls/linux/socket_unix_pair.cc
+++ b/test/syscalls/linux/socket_unix_pair.cc
@@ -16,6 +16,7 @@
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix.h"
+#include "test/syscalls/linux/socket_unix_cmsg.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
@@ -33,5 +34,9 @@ INSTANTIATE_TEST_SUITE_P(
AllUnixDomainSockets, UnixSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+INSTANTIATE_TEST_SUITE_P(
+ AllUnixDomainSockets, UnixSocketPairCmsgTest,
+ ::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+
} // namespace testing
} // namespace gvisor