diff options
Diffstat (limited to 'test/syscalls')
-rw-r--r-- | test/syscalls/linux/BUILD | 4 | ||||
-rw-r--r-- | test/syscalls/linux/cgroup.cc | 17 | ||||
-rw-r--r-- | test/syscalls/linux/link.cc | 3 | ||||
-rw-r--r-- | test/syscalls/linux/mmap.cc | 2 | ||||
-rw-r--r-- | test/syscalls/linux/msgqueue.cc | 291 | ||||
-rw-r--r-- | test/syscalls/linux/packet_socket.cc | 13 | ||||
-rw-r--r-- | test/syscalls/linux/packet_socket_raw.cc | 42 | ||||
-rw-r--r-- | test/syscalls/linux/pipe.cc | 24 | ||||
-rw-r--r-- | test/syscalls/linux/proc_net.cc | 78 | ||||
-rw-r--r-- | test/syscalls/linux/raw_socket.cc | 122 | ||||
-rw-r--r-- | test/syscalls/linux/raw_socket_hdrincl.cc | 4 | ||||
-rw-r--r-- | test/syscalls/linux/raw_socket_icmp.cc | 26 | ||||
-rw-r--r-- | test/syscalls/linux/tcp_socket.cc | 60 |
13 files changed, 494 insertions, 192 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 960421466..01ee432cb 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -7,6 +7,8 @@ package( exports_files( [ + "packet_socket.cc", + "packet_socket_raw.cc", "raw_socket.cc", "raw_socket_hdrincl.cc", "raw_socket_icmp.cc", @@ -1446,6 +1448,7 @@ cc_binary( deps = [ ":unix_domain_socket_test_util", "//test/util:capability_util", + "//test/util:cleanup", "//test/util:file_descriptor", "//test/util:socket_util", "@com_google_absl//absl/base:core_headers", @@ -1464,6 +1467,7 @@ cc_binary( deps = [ ":unix_domain_socket_test_util", "//test/util:capability_util", + "//test/util:cleanup", "//test/util:file_descriptor", "//test/util:socket_util", "@com_google_absl//absl/base:core_headers", diff --git a/test/syscalls/linux/cgroup.cc b/test/syscalls/linux/cgroup.cc index f29891571..ca23dfeee 100644 --- a/test/syscalls/linux/cgroup.cc +++ b/test/syscalls/linux/cgroup.cc @@ -279,6 +279,23 @@ TEST(Cgroup, UnmountRepeated) { EXPECT_THAT(umount(c.Path().c_str()), SyscallFailsWithErrno(EINVAL)); } +TEST(Cgroup, Create) { + SKIP_IF(!CgroupsAvailable()); + Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir())); + Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs("")); + ASSERT_NO_ERRNO(c.CreateChild("child1")); + EXPECT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(Exists(c.Path()))); +} + +TEST(Cgroup, SubcontainerInitiallyEmpty) { + SKIP_IF(!CgroupsAvailable()); + Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir())); + Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs("")); + Cgroup child = ASSERT_NO_ERRNO_AND_VALUE(c.CreateChild("child1")); + auto procs = ASSERT_NO_ERRNO_AND_VALUE(child.Procs()); + EXPECT_TRUE(procs.empty()); +} + TEST(MemoryCgroup, MemoryUsageInBytes) { SKIP_IF(!CgroupsAvailable()); diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc index 4f9ca1a65..8b208f99a 100644 --- a/test/syscalls/linux/link.cc +++ b/test/syscalls/linux/link.cc @@ -142,7 +142,8 @@ TEST(LinkTest, OldnameIsEmpty) { TEST(LinkTest, OldnameDoesNotExist) { const std::string oldname = NewTempAbsPath(); const std::string newname = NewTempAbsPath(); - EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); + EXPECT_THAT(link(oldname.c_str(), newname.c_str()), + SyscallFailsWithErrno(ENOENT)); } TEST(LinkTest, NewnameCannotExist) { diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc index caad575cf..fda176261 100644 --- a/test/syscalls/linux/mmap.cc +++ b/test/syscalls/linux/mmap.cc @@ -795,7 +795,7 @@ class MMapFileTest : public MMapTest { bool FSSupportsMap() const { bool supported = true; - void* ret = mmap(nullptr, 1, PROT_NONE, 0, fd_.get(), 0); + void* ret = mmap(nullptr, 1, PROT_NONE, MAP_PRIVATE, fd_.get(), 0); if (ret == MAP_FAILED && errno != ENODEV) { supported = false; } diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc index 8994b739a..c4761eba8 100644 --- a/test/syscalls/linux/msgqueue.cc +++ b/test/syscalls/linux/msgqueue.cc @@ -30,9 +30,15 @@ namespace gvisor { namespace testing { namespace { -constexpr int msgMax = 8192; // Max size for message in bytes. +// Source: include/uapi/linux/msg.h +constexpr int msgMnb = 16384; // Maximum number of bytes in a queue. constexpr int msgMni = 32000; // Max number of identifiers. -constexpr int msgMnb = 16384; // Default max size of message queue in bytes. +constexpr int msgPool = + (msgMni * msgMnb / 1024); // Size of buffer pool used to hold message data. +constexpr int msgMap = msgMnb; // Maximum number of entries in message map. +constexpr int msgMax = 8192; // Maximum number of bytes in a single message. +constexpr int msgSsz = 16; // Message segment size. +constexpr int msgTql = msgMnb; // Maximum number of messages on all queues. constexpr int kInterruptSignal = SIGALRM; @@ -40,6 +46,10 @@ constexpr int kInterruptSignal = SIGALRM; class Queue { public: explicit Queue(int id) : id_(id) {} + Queue(const Queue&) = delete; + Queue& operator=(const Queue&) = delete; + + Queue(Queue&& other) { id_ = other.release(); } ~Queue() { if (id_ >= 0) { @@ -59,6 +69,14 @@ class Queue { int id_ = -1; }; +PosixErrorOr<Queue> Msgget(key_t key, int flags) { + int id = msgget(key, flags); + if (id == -1) { + return PosixError(errno, absl::StrFormat("msgget(%d, %d)", key, flags)); + } + return Queue(id); +} + // Default size for messages. constexpr size_t msgSize = 50; @@ -90,8 +108,7 @@ TEST(MsgqueueTest, MsgGet) { const key_t key = ftok(keyfile.path().c_str(), 1); ASSERT_THAT(key, SyscallSucceeds()); - Queue queue(msgget(key, IPC_CREAT)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(key, IPC_CREAT)); EXPECT_THAT(msgget(key, 0), SyscallSucceedsWithValue(queue.get())); } @@ -103,27 +120,20 @@ TEST(MsgqueueTest, MsgGetFail) { EXPECT_THAT(msgget(key, 0), SyscallFailsWithErrno(ENOENT)); - Queue queue(msgget(key, IPC_CREAT)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(key, IPC_CREAT)); EXPECT_THAT(msgget(key, IPC_CREAT | IPC_EXCL), SyscallFailsWithErrno(EEXIST)); } // Test using msgget(2) with IPC_PRIVATE option. TEST(MsgqueueTest, MsgGetIpcPrivate) { - Queue queue1(msgget(IPC_PRIVATE, 0)); - ASSERT_THAT(queue1.get(), SyscallSucceeds()); - - Queue queue2(msgget(IPC_PRIVATE, 0)); - ASSERT_THAT(queue2.get(), SyscallSucceeds()); - + Queue queue1 = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0)); + Queue queue2 = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0)); EXPECT_NE(queue1.get(), queue2.get()); } // Test simple msgsnd and msgrcv. TEST(MsgqueueTest, MsgOpSimple) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, "A message."}; msgbuf rcv; @@ -138,8 +148,7 @@ TEST(MsgqueueTest, MsgOpSimple) { // Test msgsnd and msgrcv of an empty message. TEST(MsgqueueTest, MsgOpEmpty) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, ""}; msgbuf rcv; @@ -151,8 +160,7 @@ TEST(MsgqueueTest, MsgOpEmpty) { // Test truncation of message with MSG_NOERROR flag. TEST(MsgqueueTest, MsgOpTruncate) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, ""}; msgbuf rcv; @@ -166,8 +174,7 @@ TEST(MsgqueueTest, MsgOpTruncate) { // Test msgsnd and msgrcv using invalid arguments. TEST(MsgqueueTest, MsgOpInvalidArgs) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, ""}; @@ -184,8 +191,7 @@ TEST(MsgqueueTest, MsgOpInvalidArgs) { // Test non-blocking msgrcv with an empty queue. TEST(MsgqueueTest, MsgOpNoMsg) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf rcv; EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(rcv.mtext) + 1, 0, IPC_NOWAIT), @@ -195,8 +201,7 @@ TEST(MsgqueueTest, MsgOpNoMsg) { // Test non-blocking msgrcv with a non-empty queue, but no messages of wanted // type. TEST(MsgqueueTest, MsgOpNoMsgType) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, ""}; ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), @@ -208,8 +213,7 @@ TEST(MsgqueueTest, MsgOpNoMsgType) { // Test msgrcv with a larger size message than wanted, and truncation disabled. TEST(MsgqueueTest, MsgOpTooBig) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, ""}; ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), @@ -221,8 +225,7 @@ TEST(MsgqueueTest, MsgOpTooBig) { // Test receiving messages based on type. TEST(MsgqueueTest, MsgRcvType) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); // Send messages in an order and receive them in reverse, based on type, // which shouldn't block. @@ -248,8 +251,7 @@ TEST(MsgqueueTest, MsgRcvType) { // Test using MSG_EXCEPT to receive a different-type message. TEST(MsgqueueTest, MsgExcept) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); std::map<int64_t, msgbuf> typeToBuf = { {1, msgbuf{1, "Message 1."}}, @@ -274,8 +276,7 @@ TEST(MsgqueueTest, MsgExcept) { // Test msgrcv with a negative type. TEST(MsgqueueTest, MsgRcvTypeNegative) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); // When msgtyp is negative, msgrcv returns the first message with mtype less // than or equal to the absolute value. @@ -298,8 +299,7 @@ TEST(MsgqueueTest, MsgRcvTypeNegative) { TEST(MsgqueueTest, MsgOpPermissions) { AutoCapability cap(CAP_IPC_OWNER, false); - Queue queue(msgget(IPC_PRIVATE, 0000)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0000)); msgbuf buf{1, ""}; @@ -311,8 +311,7 @@ TEST(MsgqueueTest, MsgOpPermissions) { // Test limits for messages and queues. TEST(MsgqueueTest, MsgOpLimits) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, "A message."}; @@ -340,11 +339,14 @@ bool MsgCopySupported() { // test if errno == ENOSYS. This means that the test will always run on // gVisor, but may be skipped on native linux. - Queue queue(msgget(IPC_PRIVATE, 0600)); - + auto maybe_id = Msgget(IPC_PRIVATE, 0600); + if (!maybe_id.ok()) { + return false; + } + Queue queue(std::move(maybe_id.ValueOrDie())); msgbuf buf{1, "Test message."}; - msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0); + msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0); return !(msgrcv(queue.get(), &buf, sizeof(buf.mtext) + 1, 0, MSG_COPY | IPC_NOWAIT) == -1 && errno == ENOSYS); @@ -354,9 +356,7 @@ bool MsgCopySupported() { TEST(MsgqueueTest, MsgCopy) { SKIP_IF(!MsgCopySupported()); - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf bufs[5] = { msgbuf{1, "Message 1."}, msgbuf{2, "Message 2."}, msgbuf{3, "Message 3."}, msgbuf{4, "Message 4."}, msgbuf{5, "Message 5."}, @@ -390,9 +390,7 @@ TEST(MsgqueueTest, MsgCopy) { TEST(MsgqueueTest, MsgCopyInvalidArgs) { SKIP_IF(!MsgCopySupported()); - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf rcv; EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, 1, MSG_COPY), SyscallFailsWithErrno(EINVAL)); @@ -406,9 +404,7 @@ TEST(MsgqueueTest, MsgCopyInvalidArgs) { TEST(MsgqueueTest, MsgCopyInvalidIndex) { SKIP_IF(!MsgCopySupported()); - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf rcv; EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, -3, MSG_COPY | IPC_NOWAIT), SyscallFailsWithErrno(ENOMSG)); @@ -419,9 +415,7 @@ TEST(MsgqueueTest, MsgCopyInvalidIndex) { // Test msgrcv (most probably) blocking on an empty queue. TEST(MsgqueueTest, MsgRcvBlocking) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf buf{1, "A message."}; ScopedThread t([&] { @@ -441,9 +435,7 @@ TEST(MsgqueueTest, MsgRcvBlocking) { // Test msgrcv (most probably) waiting for a specific-type message. TEST(MsgqueueTest, MsgRcvTypeBlocking) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgbuf bufs[5] = {{1, "A message."}, {1, "A message."}, {1, "A message."}, @@ -471,9 +463,7 @@ TEST(MsgqueueTest, MsgRcvTypeBlocking) { // Test msgsnd (most probably) blocking on a full queue. TEST(MsgqueueTest, MsgSndBlocking) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); - + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgmax buf{1, ""}; // Has max amount of bytes. const size_t msgCount = msgMnb / msgMax; // Number of messages that can be @@ -512,8 +502,7 @@ TEST(MsgqueueTest, MsgSndBlocking) { // Test removing a queue while a blocking msgsnd is executing. TEST(MsgqueueTest, MsgSndRmWhileBlocking) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); // Number of messages that can be sent without blocking. const size_t msgCount = msgMnb / msgMax; @@ -549,8 +538,7 @@ TEST(MsgqueueTest, MsgSndRmWhileBlocking) { // Test removing a queue while a blocking msgrcv is executing. TEST(MsgqueueTest, MsgRcvRmWhileBlocking) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); ScopedThread t([&] { // Because we're repeating on EINTR, msgsnd may race with msgctl(IPC_RMID) @@ -568,8 +556,7 @@ TEST(MsgqueueTest, MsgRcvRmWhileBlocking) { // Test a collection of msgsnd/msgrcv operations in different processes. TEST(MsgqueueTest, MsgOpGeneral) { - Queue queue(msgget(IPC_PRIVATE, 0600)); - ASSERT_THAT(queue.get(), SyscallSucceeds()); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); // Create multiple sending/receiving threads that send messages back and // forth. There's a matching recv for each send, so by the end of the test, @@ -625,7 +612,7 @@ TEST(MsgqueueTest, MsgOpGeneral) { void empty_sighandler(int sig, siginfo_t* info, void* context) {} TEST(MsgqueueTest, InterruptRecv) { - Queue queue(msgget(IPC_PRIVATE, 0600)); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); char buf[64]; absl::Notification done, exit; @@ -663,7 +650,7 @@ TEST(MsgqueueTest, InterruptRecv) { } TEST(MsgqueueTest, InterruptSend) { - Queue queue(msgget(IPC_PRIVATE, 0600)); + Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600)); msgmax buf{1, ""}; // Number of messages that can be sent without blocking. const size_t msgCount = msgMnb / msgMax; @@ -708,6 +695,178 @@ TEST(MsgqueueTest, InterruptSend) { t.Join(); } +// Test msgctl with IPC_STAT option. +TEST(MsgqueueTest, MsgCtlIpcStat) { + auto start = absl::Now(); + + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + const uid_t uid = getuid(); + const gid_t gid = getgid(); + const pid_t pid = getpid(); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_EQ(ds.msg_perm.__key, IPC_PRIVATE); + EXPECT_EQ(ds.msg_perm.uid, uid); + EXPECT_EQ(ds.msg_perm.gid, gid); + EXPECT_EQ(ds.msg_perm.cuid, uid); + EXPECT_EQ(ds.msg_perm.cgid, gid); + EXPECT_EQ(ds.msg_perm.mode, 0600); + + EXPECT_EQ(ds.msg_stime, 0); + EXPECT_EQ(ds.msg_rtime, 0); + EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start)); + + EXPECT_EQ(ds.msg_cbytes, 0); + EXPECT_EQ(ds.msg_qnum, 0); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + EXPECT_EQ(ds.msg_lspid, 0); + EXPECT_EQ(ds.msg_lrpid, 0); + + // The timestamps only have a resolution of seconds; slow down so we actually + // see the timestamps change. + absl::SleepFor(absl::Seconds(1)); + auto pre_send = absl::Now(); + + msgbuf buf{1, "A message."}; + ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), + SyscallSucceeds()); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send)); + EXPECT_EQ(ds.msg_rtime, 0); + EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start)); + + EXPECT_EQ(ds.msg_cbytes, msgSize); + EXPECT_EQ(ds.msg_qnum, 1); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + EXPECT_EQ(ds.msg_lspid, pid); + EXPECT_EQ(ds.msg_lrpid, 0); + + absl::SleepFor(absl::Seconds(1)); + auto pre_receive = absl::Now(); + + ASSERT_THAT(msgrcv(queue.get(), &buf, sizeof(buf.mtext), 0, 0), + SyscallSucceedsWithValue(msgSize)); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send)); + EXPECT_GE(ds.msg_rtime, absl::ToTimeT(pre_receive)); + EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start)); + + EXPECT_EQ(ds.msg_cbytes, 0); + EXPECT_EQ(ds.msg_qnum, 0); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + EXPECT_EQ(ds.msg_lspid, pid); + EXPECT_EQ(ds.msg_lrpid, pid); +} + +// Test msgctl with IPC_STAT option on a write-only queue. +TEST(MsgqueueTest, MsgCtlIpcStatWriteOnly) { + // Drop CAP_IPC_OWNER which allows us to bypass permissions. + AutoCapability cap(CAP_IPC_OWNER, false); + + Queue queue(msgget(IPC_PRIVATE, 0200)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), + SyscallFailsWithErrno(EACCES)); +} + +// Test msgctl with IPC_SET option. +TEST(MsgqueueTest, MsgCtlIpcSet) { + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_perm.mode, 0600); + + ds.msg_perm.mode = 0777; + ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds()); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_perm.mode, 0777); +} + +// Test increasing msg_qbytes beyond limit with IPC_SET. +TEST(MsgqueueTest, MsgCtlIpcSetMaxBytes) { + // Drop CAP_SYS_RESOURCE which allows us to increase msg_qbytes beyond the + // system parameter MSGMNB. + AutoCapability cap(CAP_SYS_RESOURCE, false); + + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + struct msqid_ds ds; + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_qbytes, msgMnb); + + ds.msg_qbytes = msgMnb - 10; + ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds()); + + ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.msg_qbytes, msgMnb - 10); + + ds.msg_qbytes = msgMnb + 10; + EXPECT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallFailsWithErrno(EPERM)); +} + +// Test msgctl with IPC_INFO option. +TEST(MsgqueueTest, MsgCtlIpcInfo) { + struct msginfo info; + ASSERT_THAT(msgctl(0, IPC_INFO, reinterpret_cast<struct msqid_ds*>(&info)), + SyscallSucceeds()); + + EXPECT_GT(info.msgmax, 0); + EXPECT_GT(info.msgmni, 0); + EXPECT_GT(info.msgmnb, 0); + EXPECT_EQ(info.msgpool, msgPool); + EXPECT_EQ(info.msgmap, msgMap); + EXPECT_EQ(info.msgssz, msgSsz); + EXPECT_EQ(info.msgtql, msgTql); +} + +// Test msgctl with MSG_INFO option. +TEST(MsgqueueTest, MsgCtlMsgInfo) { + struct msginfo info; + ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast<struct msqid_ds*>(&info)), + SyscallSucceeds()); + + EXPECT_GT(info.msgmax, 0); + EXPECT_GT(info.msgmni, 0); + EXPECT_GT(info.msgmnb, 0); + EXPECT_EQ(info.msgpool, 0); // Number of queues in the system. + EXPECT_EQ(info.msgmap, 0); // Total number of messages in all queues. + EXPECT_EQ(info.msgtql, 0); // Total number of bytes in all messages. + EXPECT_EQ(info.msgssz, msgSsz); + + // Add a queue and a message. + Queue queue(msgget(IPC_PRIVATE, 0600)); + ASSERT_THAT(queue.get(), SyscallSucceeds()); + + msgbuf buf{1, "A message."}; + ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), + SyscallSucceeds()); + + ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast<struct msqid_ds*>(&info)), + SyscallSucceeds()); + + EXPECT_GT(info.msgmax, 0); + EXPECT_GT(info.msgmni, 0); + EXPECT_GT(info.msgmnb, 0); + EXPECT_EQ(info.msgpool, 1); // Number of queues in the system. + EXPECT_EQ(info.msgmap, 1); // Total number of messages in all queues. + EXPECT_EQ(info.msgtql, msgSize); // Total number of bytes in all messages. + EXPECT_EQ(info.msgssz, msgSsz); +} + } // namespace } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc index 98339277b..ca4ab0aad 100644 --- a/test/syscalls/linux/packet_socket.cc +++ b/test/syscalls/linux/packet_socket.cc @@ -14,13 +14,13 @@ #include <arpa/inet.h> #include <ifaddrs.h> -#include <linux/capability.h> -#include <linux/if_arp.h> -#include <linux/if_packet.h> #include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/udp.h> +#include <netpacket/packet.h> #include <poll.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -31,6 +31,7 @@ #include "absl/base/internal/endian.h" #include "test/syscalls/linux/unix_domain_socket_test_util.h" #include "test/util/capability_util.h" +#include "test/util/cleanup.h" #include "test/util/file_descriptor.h" #include "test/util/socket_util.h" #include "test/util/test_util.h" @@ -85,7 +86,7 @@ void SendUDPMessage(int sock) { // Send an IP packet and make sure ETH_P_<something else> doesn't pick it up. TEST(BasicCookedPacketTest, WrongType) { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) { ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, ETH_P_PUP), SyscallFailsWithErrno(EPERM)); GTEST_SKIP(); @@ -123,7 +124,7 @@ class CookedPacketTest : public ::testing::TestWithParam<int> { }; void CookedPacketTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) { ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())), SyscallFailsWithErrno(EPERM)); GTEST_SKIP(); @@ -149,7 +150,7 @@ void CookedPacketTest::SetUp() { void CookedPacketTest::TearDown() { // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) { EXPECT_THAT(close(socket_), SyscallSucceeds()); } } diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc index 07beb8ba0..61714d1da 100644 --- a/test/syscalls/linux/packet_socket_raw.cc +++ b/test/syscalls/linux/packet_socket_raw.cc @@ -13,14 +13,13 @@ // limitations under the License. #include <arpa/inet.h> -#include <linux/capability.h> -#include <linux/filter.h> -#include <linux/if_arp.h> -#include <linux/if_packet.h> #include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/udp.h> +#include <netpacket/packet.h> #include <poll.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -32,6 +31,7 @@ #include "absl/base/internal/endian.h" #include "test/syscalls/linux/unix_domain_socket_test_util.h" #include "test/util/capability_util.h" +#include "test/util/cleanup.h" #include "test/util/file_descriptor.h" #include "test/util/socket_util.h" #include "test/util/test_util.h" @@ -100,7 +100,7 @@ class RawPacketTest : public ::testing::TestWithParam<int> { }; void RawPacketTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) { ASSERT_THAT(socket(AF_PACKET, SOCK_RAW, htons(GetParam())), SyscallFailsWithErrno(EPERM)); GTEST_SKIP(); @@ -150,7 +150,7 @@ void RawPacketTest::SetUp() { void RawPacketTest::TearDown() { // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) { EXPECT_THAT(close(s_), SyscallSucceeds()); } } @@ -340,7 +340,7 @@ TEST_P(RawPacketTest, Send) { // Check that setting SO_RCVBUF below min is clamped to the minimum // receive buffer size. TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); // Discover minimum receive buf size by trying to set it to zero. // See: @@ -373,7 +373,7 @@ TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) { // Check that setting SO_RCVBUF above max is clamped to the maximum // receive buffer size. TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); // Discover max buf size by trying to set the largest possible buffer size. constexpr int kRcvBufSz = 0xffffffff; @@ -400,7 +400,7 @@ TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) { // Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored. TEST_P(RawPacketTest, SetSocketRecvBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); int max = 0; int min = 0; @@ -449,7 +449,7 @@ TEST_P(RawPacketTest, SetSocketRecvBuf) { // Check that setting SO_SNDBUF below min is clamped to the minimum // receive buffer size. TEST_P(RawPacketTest, SetSocketSendBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); // Discover minimum buffer size by trying to set it to zero. constexpr int kSndBufSz = 0; @@ -480,7 +480,7 @@ TEST_P(RawPacketTest, SetSocketSendBufBelowMin) { // Check that setting SO_SNDBUF above max is clamped to the maximum // send buffer size. TEST_P(RawPacketTest, SetSocketSendBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); // Discover maximum buffer size by trying to set it to a large value. constexpr int kSndBufSz = 0xffffffff; @@ -507,7 +507,7 @@ TEST_P(RawPacketTest, SetSocketSendBufAboveMax) { // Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored. TEST_P(RawPacketTest, SetSocketSendBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); int max = 0; int min = 0; @@ -551,7 +551,7 @@ TEST_P(RawPacketTest, SetSocketSendBuf) { } TEST_P(RawPacketTest, GetSocketError) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); int val = 0; socklen_t val_len = sizeof(val); @@ -561,7 +561,7 @@ TEST_P(RawPacketTest, GetSocketError) { } TEST_P(RawPacketTest, GetSocketErrorBind) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); { // Bind to the loopback device. @@ -627,7 +627,7 @@ TEST_P(RawPacketTest, SetSocketDetachFilterNoInstalledFilter) { } TEST_P(RawPacketTest, GetSocketDetachFilter) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); int val = 0; socklen_t val_len = sizeof(val); @@ -636,7 +636,7 @@ TEST_P(RawPacketTest, GetSocketDetachFilter) { } TEST_P(RawPacketTest, SetAndGetSocketLinger) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); int level = SOL_SOCKET; int type = SO_LINGER; @@ -657,7 +657,7 @@ TEST_P(RawPacketTest, SetAndGetSocketLinger) { } TEST_P(RawPacketTest, GetSocketAcceptConn) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); int got = -1; socklen_t length = sizeof(got); @@ -673,7 +673,7 @@ INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest, class RawPacketMsgSizeTest : public ::testing::TestWithParam<TestAddress> {}; TEST_P(RawPacketMsgSizeTest, SendTooLong) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); TestAddress addr = GetParam().WithPort(kPort); @@ -690,8 +690,11 @@ TEST_P(RawPacketMsgSizeTest, SendTooLong) { SyscallFailsWithErrno(EMSGSIZE)); } +// TODO(https://fxbug.dev/76957): Run this test on Fuchsia once splice is +// available. +#ifndef __Fuchsia__ TEST_P(RawPacketMsgSizeTest, SpliceTooLong) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())); const char buf[65536] = {}; int fds[2]; @@ -718,6 +721,7 @@ TEST_P(RawPacketMsgSizeTest, SpliceTooLong) { EXPECT_THAT(n, SyscallSucceedsWithValue(sizeof(buf))); } } +#endif // __Fuchsia__ INSTANTIATE_TEST_SUITE_P(AllRawPacketMsgSizeTest, RawPacketMsgSizeTest, ::testing::Values(V4Loopback(), V6Loopback())); diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc index 0bba86846..209801e36 100644 --- a/test/syscalls/linux/pipe.cc +++ b/test/syscalls/linux/pipe.cc @@ -13,11 +13,13 @@ // limitations under the License. #include <fcntl.h> /* Obtain O_* constant definitions */ +#include <linux/futex.h> #include <linux/magic.h> #include <signal.h> #include <sys/ioctl.h> #include <sys/statfs.h> #include <sys/uio.h> +#include <syscall.h> #include <unistd.h> #include <vector> @@ -50,6 +52,9 @@ std::atomic<int> global_num_signals_received = 0; void SigRecordingHandler(int signum, siginfo_t* siginfo, void* unused_ucontext) { global_num_signals_received++; + ASSERT_THAT(syscall(SYS_futex, &global_num_signals_received, + FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT_MAX, 0, 0, 0), + SyscallSucceeds()); } PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) { @@ -61,11 +66,14 @@ PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) { return ScopedSigaction(signum, handler); } -void WaitForSignalDelivery(absl::Duration timeout, int max_expected) { - absl::Time wait_start = absl::Now(); - while (global_num_signals_received < max_expected && - absl::Now() - wait_start < timeout) { - absl::SleepFor(absl::Milliseconds(10)); +void WaitForSignalDelivery(int expected) { + while (1) { + int v = global_num_signals_received; + if (v >= expected) { + break; + } + RetryEINTR(syscall)(SYS_futex, &global_num_signals_received, + FUTEX_WAIT | FUTEX_PRIVATE_FLAG, v, 0, 0, 0); } } @@ -371,7 +379,7 @@ TEST_P(PipeTest, ReaderSideCloses) { EXPECT_THAT(write(wfd_.get(), &buf, sizeof(buf)), SyscallFailsWithErrno(EPIPE)); - WaitForSignalDelivery(absl::Seconds(1), 1); + WaitForSignalDelivery(1); ASSERT_EQ(global_num_signals_received, 1); } @@ -411,7 +419,7 @@ TEST_P(PipeTest, BlockWriteClosed) { notify.WaitForNotification(); ASSERT_THAT(close(rfd_.release()), SyscallSucceeds()); - WaitForSignalDelivery(absl::Seconds(1), 1); + WaitForSignalDelivery(1); ASSERT_EQ(global_num_signals_received, 1); t.Join(); @@ -443,7 +451,7 @@ TEST_P(PipeTest, BlockPartialWriteClosed) { // Unblock the above. ASSERT_THAT(close(rfd_.release()), SyscallSucceeds()); - WaitForSignalDelivery(absl::Seconds(1), 2); + WaitForSignalDelivery(2); ASSERT_EQ(global_num_signals_received, 2); t.Join(); diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc index 4cbe30fc1..672f2dd42 100644 --- a/test/syscalls/linux/proc_net.cc +++ b/test/syscalls/linux/proc_net.cc @@ -152,6 +152,37 @@ TEST(ProcNetDev, Format) { EXPECT_GT(entries.size(), 0); } +// GetMibsAllocationSysctl retuns a value of the net.core.mibs_allocation +// sysctl./proc/sys/net/core/mibs_allocation +// +// When mibs_allocation is unset, a netns creation inherits MIB from init +// network namespace. Otherwise, MIBS is allocated for each namespace. +int GetMibsAllocationSysctl() { + auto ret = GetContents("/proc/sys/net/core/mibs_allocation"); + if (!ret.ok()) { + // The current kernel doesn't support mibs_allocation. + return 1; + } + int32_t val; + EXPECT_TRUE(absl::SimpleAtoi(ret.ValueOrDie(), &val)); + return val; +} + +// GetSNMPMetricFromProc retrieves the metric named `item` of `type` from the +// `snmp` string which represents the file contents of /proc/net/snmp. +// +// Note to test writers: If you are writing tests to check the change in value +// of SNMP metrics, note that if GetMibsAllocationSysctl() == 0 +// (net.core.mibs_allocation isn't set), MIB is from the init network namespace +// and hence these metrics can have system-wide noise. +// +// A feasible way of testing is the following: +// * Consult RFC 1213 to learn the "SYNTAX" of the metric. +// * If net.core.mibs_allocation is set: +// - Can test any metric while checking for hard equality. +// * If net.core.mibs_allocation is NOT set (system-wide noise is present): +// - Only test "SYNTAX Counter" metrics. +// - Counter metrics are increasing in nature, so use LE/GE equality checks. PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp, const std::string& type, const std::string& item) { @@ -226,12 +257,25 @@ TEST(ProcNetSnmp, TcpReset) { newAttemptFails = ASSERT_NO_ERRNO_AND_VALUE( GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails")); - EXPECT_EQ(oldActiveOpens, newActiveOpens - 1); - EXPECT_EQ(oldOutRsts, newOutRsts - 1); - EXPECT_EQ(oldAttemptFails, newAttemptFails - 1); + if (GetMibsAllocationSysctl()) { + EXPECT_EQ(oldActiveOpens + 1, newActiveOpens); + EXPECT_EQ(oldOutRsts + 1, newOutRsts); + EXPECT_EQ(oldAttemptFails + 1, newAttemptFails); + } else { + // System-wide statistics can have some noise. These metrics should have + // increased by at least 1. + EXPECT_LE(oldActiveOpens + 1, newActiveOpens); + EXPECT_LE(oldOutRsts + 1, newOutRsts); + EXPECT_LE(oldAttemptFails + 1, newAttemptFails); + } } TEST(ProcNetSnmp, TcpEstab) { + // This test aims to test the tcpCurrEstab metric which has "SYNTAX Gauge" as + // per RFC 1213. Hence, it becomes infeasible to test this when system-wide + // statistics have noise. + SKIP_IF(GetMibsAllocationSysctl() == 0); + // TODO(gvisor.dev/issue/866): epsocket metrics are not savable. DisableSave ds; @@ -286,9 +330,9 @@ TEST(ProcNetSnmp, TcpEstab) { newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE( GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab")); - EXPECT_EQ(oldActiveOpens, newActiveOpens - 1); - EXPECT_EQ(oldPassiveOpens, newPassiveOpens - 1); - EXPECT_EQ(oldCurrEstab, newCurrEstab - 2); + EXPECT_EQ(oldActiveOpens + 1, newActiveOpens); + EXPECT_EQ(oldPassiveOpens + 1, newPassiveOpens); + EXPECT_EQ(oldCurrEstab + 2, newCurrEstab); // Send 1 byte from client to server. ASSERT_THAT(send(s_connect.get(), "a", 1, 0), SyscallSucceedsWithValue(1)); @@ -355,8 +399,15 @@ TEST(ProcNetSnmp, UdpNoPorts) { newNoPorts = ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts")); - EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1); - EXPECT_EQ(oldNoPorts, newNoPorts - 1); + if (GetMibsAllocationSysctl()) { + EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams); + EXPECT_EQ(oldNoPorts + 1, newNoPorts); + } else { + // System-wide statistics can have some noise. These metrics should have + // increased by at least 1. + EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams); + EXPECT_LE(oldNoPorts + 1, newNoPorts); + } } TEST(ProcNetSnmp, UdpIn) { @@ -405,8 +456,15 @@ TEST(ProcNetSnmp, UdpIn) { newInDatagrams = ASSERT_NO_ERRNO_AND_VALUE( GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams")); - EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1); - EXPECT_EQ(oldInDatagrams, newInDatagrams - 1); + if (GetMibsAllocationSysctl()) { + EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams); + EXPECT_EQ(oldInDatagrams + 1, newInDatagrams); + } else { + // System-wide statistics can have some noise. These metrics should have + // increased by at least 1. + EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams); + EXPECT_LE(oldInDatagrams + 1, newInDatagrams); + } } TEST(ProcNetSnmp, CheckNetStat) { diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc index 4e69e389b..66f0e6ca4 100644 --- a/test/syscalls/linux/raw_socket.cc +++ b/test/syscalls/linux/raw_socket.cc @@ -97,7 +97,7 @@ class RawSocketTest : public ::testing::TestWithParam<std::tuple<int, int>> { }; void RawSocketTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) { ASSERT_THAT(socket(Family(), SOCK_RAW, Protocol()), SyscallFailsWithErrno(EPERM)); GTEST_SKIP(); @@ -121,7 +121,7 @@ void RawSocketTest::SetUp() { void RawSocketTest::TearDown() { // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) { EXPECT_THAT(close(s_), SyscallSucceeds()); } } @@ -130,7 +130,7 @@ void RawSocketTest::TearDown() { // BasicRawSocket::Setup creates the first one, so we only have to create one // more here. TEST_P(RawSocketTest, MultipleCreation) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int s2; ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds()); @@ -140,7 +140,7 @@ TEST_P(RawSocketTest, MultipleCreation) { // Test that shutting down an unconnected socket fails. TEST_P(RawSocketTest, FailShutdownWithoutConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); @@ -148,7 +148,7 @@ TEST_P(RawSocketTest, FailShutdownWithoutConnect) { // Shutdown is a no-op for raw sockets (and datagram sockets in general). TEST_P(RawSocketTest, ShutdownWriteNoop) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), @@ -163,7 +163,7 @@ TEST_P(RawSocketTest, ShutdownWriteNoop) { // Shutdown is a no-op for raw sockets (and datagram sockets in general). TEST_P(RawSocketTest, ShutdownReadNoop) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), @@ -180,14 +180,14 @@ TEST_P(RawSocketTest, ShutdownReadNoop) { // Test that listen() fails. TEST_P(RawSocketTest, FailListen) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT(listen(s_, 1), SyscallFailsWithErrno(ENOTSUP)); } // Test that accept() fails. TEST_P(RawSocketTest, FailAccept) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct sockaddr saddr; socklen_t addrlen; @@ -195,7 +195,7 @@ TEST_P(RawSocketTest, FailAccept) { } TEST_P(RawSocketTest, BindThenGetSockName) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_); ASSERT_THAT(bind(s_, addr, AddrLen()), SyscallSucceeds()); @@ -219,7 +219,7 @@ TEST_P(RawSocketTest, BindThenGetSockName) { } TEST_P(RawSocketTest, ConnectThenGetSockName) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_); ASSERT_THAT(connect(s_, addr, AddrLen()), SyscallSucceeds()); @@ -244,7 +244,7 @@ TEST_P(RawSocketTest, ConnectThenGetSockName) { // Test that getpeername() returns nothing before connect(). TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct sockaddr saddr; socklen_t addrlen = sizeof(saddr); @@ -254,7 +254,7 @@ TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) { // Test that getpeername() returns something after connect(). TEST_P(RawSocketTest, GetPeerName) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), @@ -268,7 +268,7 @@ TEST_P(RawSocketTest, GetPeerName) { // Test that the socket is writable immediately. TEST_P(RawSocketTest, PollWritableImmediately) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct pollfd pfd = {}; pfd.fd = s_; @@ -278,7 +278,7 @@ TEST_P(RawSocketTest, PollWritableImmediately) { // Test that the socket isn't readable before receiving anything. TEST_P(RawSocketTest, PollNotReadableInitially) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Try to receive data with MSG_DONTWAIT, which returns immediately if there's // nothing to be read. @@ -289,7 +289,7 @@ TEST_P(RawSocketTest, PollNotReadableInitially) { // Test that the socket becomes readable once something is written to it. TEST_P(RawSocketTest, PollTriggeredOnWrite) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Write something so that there's data to be read. // Arbitrary. @@ -304,7 +304,7 @@ TEST_P(RawSocketTest, PollTriggeredOnWrite) { // Test that we can connect() to a valid IP (loopback). TEST_P(RawSocketTest, ConnectToLoopback) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), @@ -313,7 +313,7 @@ TEST_P(RawSocketTest, ConnectToLoopback) { // Test that calling send() without connect() fails. TEST_P(RawSocketTest, SendWithoutConnectFails) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Arbitrary. constexpr char kBuf[] = "Endgame was good"; @@ -323,7 +323,7 @@ TEST_P(RawSocketTest, SendWithoutConnectFails) { // Wildcard Bind. TEST_P(RawSocketTest, BindToWildcard) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct sockaddr_storage addr; addr = {}; @@ -344,16 +344,15 @@ TEST_P(RawSocketTest, BindToWildcard) { // Bind to localhost. TEST_P(RawSocketTest, BindToLocalhost) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); + ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), + SyscallSucceeds()); } // Bind to a different address. TEST_P(RawSocketTest, BindToInvalid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); struct sockaddr_storage bind_addr = addr_; if (Family() == AF_INET) { @@ -365,13 +364,14 @@ TEST_P(RawSocketTest, BindToInvalid) { memset(&sin6->sin6_addr.s6_addr, 0, sizeof(sin6->sin6_addr.s6_addr)); sin6->sin6_addr.s6_addr[0] = 1; // 1: - An address that we can't bind to. } - ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), - AddrLen()), SyscallFailsWithErrno(EADDRNOTAVAIL)); + ASSERT_THAT( + bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), AddrLen()), + SyscallFailsWithErrno(EADDRNOTAVAIL)); } // Send and receive an packet. TEST_P(RawSocketTest, SendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Arbitrary. constexpr char kBuf[] = "TB12"; @@ -386,7 +386,7 @@ TEST_P(RawSocketTest, SendAndReceive) { // We should be able to create multiple raw sockets for the same protocol and // receive the same packet on both. TEST_P(RawSocketTest, MultipleSocketReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int s2; ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds()); @@ -401,11 +401,11 @@ TEST_P(RawSocketTest, MultipleSocketReceive) { // Receive it on socket 2. std::vector<char> recv_buf2(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBufFrom(s2, recv_buf2.data(), - recv_buf2.size())); + ASSERT_NO_FATAL_FAILURE( + ReceiveBufFrom(s2, recv_buf2.data(), recv_buf2.size())); - EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(), - recv_buf2.data() + HdrLen(), sizeof(kBuf)), + EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(), recv_buf2.data() + HdrLen(), + sizeof(kBuf)), 0); ASSERT_THAT(close(s2), SyscallSucceeds()); @@ -413,7 +413,7 @@ TEST_P(RawSocketTest, MultipleSocketReceive) { // Test that connect sends packets to the right place. TEST_P(RawSocketTest, SendAndReceiveViaConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), @@ -432,11 +432,10 @@ TEST_P(RawSocketTest, SendAndReceiveViaConnect) { // Bind to localhost, then send and receive packets. TEST_P(RawSocketTest, BindSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); + ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), + SyscallSucceeds()); // Arbitrary. constexpr char kBuf[] = "DR16"; @@ -450,11 +449,10 @@ TEST_P(RawSocketTest, BindSendAndReceive) { // Bind and connect to localhost and send/receive packets. TEST_P(RawSocketTest, BindConnectSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); + ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), + SyscallSucceeds()); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), SyscallSucceeds()); @@ -472,7 +470,7 @@ TEST_P(RawSocketTest, BindConnectSendAndReceive) { // Check that setting SO_RCVBUF below min is clamped to the minimum // receive buffer size. TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Discover minimum receive buf size by trying to set it to zero. // See: @@ -505,7 +503,7 @@ TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) { // Check that setting SO_RCVBUF above max is clamped to the maximum // receive buffer size. TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Discover max buf size by trying to set the largest possible buffer size. constexpr int kRcvBufSz = 0xffffffff; @@ -532,7 +530,7 @@ TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) { // Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored. TEST_P(RawSocketTest, SetSocketRecvBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int max = 0; int min = 0; @@ -582,7 +580,7 @@ TEST_P(RawSocketTest, SetSocketRecvBuf) { // Check that setting SO_SNDBUF below min is clamped to the minimum // receive buffer size. TEST_P(RawSocketTest, SetSocketSendBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Discover minimum buffer size by trying to set it to zero. constexpr int kSndBufSz = 0; @@ -613,7 +611,7 @@ TEST_P(RawSocketTest, SetSocketSendBufBelowMin) { // Check that setting SO_SNDBUF above max is clamped to the maximum // send buffer size. TEST_P(RawSocketTest, SetSocketSendBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Discover maximum buffer size by trying to set it to a large value. constexpr int kSndBufSz = 0xffffffff; @@ -640,7 +638,7 @@ TEST_P(RawSocketTest, SetSocketSendBufAboveMax) { // Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored. TEST_P(RawSocketTest, SetSocketSendBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int max = 0; int min = 0; @@ -686,11 +684,10 @@ TEST_P(RawSocketTest, SetSocketSendBuf) { // Test that receive buffer limits are not enforced when the recv buffer is // empty. TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); + ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), + SyscallSucceeds()); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), SyscallSucceeds()); @@ -717,9 +714,7 @@ TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) { // Receive the packet and make sure it's identical. std::vector<char> recv_buf(buf.size() + HdrLen()); ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ( - memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), - 0); + EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0); } { @@ -732,9 +727,7 @@ TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) { // Receive the packet and make sure it's identical. std::vector<char> recv_buf(buf.size() + HdrLen()); ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ( - memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), - 0); + EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0); } } @@ -748,11 +741,10 @@ TEST_P(RawSocketTest, RecvBufLimits) { if (Protocol() == IPPROTO_TCP) { return; } - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); + ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), + SyscallSucceeds()); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), SyscallSucceeds()); @@ -812,9 +804,7 @@ TEST_P(RawSocketTest, RecvBufLimits) { // Receive the packet and make sure it's identical. std::vector<char> recv_buf(buf.size() + HdrLen()); ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), - buf.size()), - 0); + EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0); } // Assert that the last packet is dropped because the receive buffer should @@ -883,7 +873,7 @@ TEST_P(RawSocketTest, GetSocketDetachFilter) { // AF_INET6+SOCK_RAW+IPPROTO_RAW sockets can be created, but not written to. TEST(RawSocketTest, IPv6ProtoRaw) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int sock; ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW), @@ -900,7 +890,7 @@ TEST(RawSocketTest, IPv6ProtoRaw) { } TEST(RawSocketTest, IPv6SendMsg) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int sock; ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP), @@ -928,7 +918,7 @@ TEST(RawSocketTest, IPv6SendMsg) { } TEST_P(RawSocketTest, ConnectOnIPv6Socket) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int sock; ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP), diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc index e54d781c9..d45bd07bc 100644 --- a/test/syscalls/linux/raw_socket_hdrincl.cc +++ b/test/syscalls/linux/raw_socket_hdrincl.cc @@ -62,7 +62,7 @@ class RawHDRINCL : public ::testing::Test { }; void RawHDRINCL::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) { ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_RAW), SyscallFailsWithErrno(EPERM)); GTEST_SKIP(); @@ -80,7 +80,7 @@ void RawHDRINCL::SetUp() { void RawHDRINCL::TearDown() { // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) { EXPECT_THAT(close(socket_), SyscallSucceeds()); } } diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc index 80a524273..3f9717284 100644 --- a/test/syscalls/linux/raw_socket_icmp.cc +++ b/test/syscalls/linux/raw_socket_icmp.cc @@ -76,7 +76,7 @@ class RawSocketICMPTest : public ::testing::Test { }; void RawSocketICMPTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) { ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), SyscallFailsWithErrno(EPERM)); GTEST_SKIP(); @@ -94,7 +94,7 @@ void RawSocketICMPTest::SetUp() { void RawSocketICMPTest::TearDown() { // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { + if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) { EXPECT_THAT(close(s_), SyscallSucceeds()); } } @@ -102,7 +102,7 @@ void RawSocketICMPTest::TearDown() { // We'll only read an echo in this case, as the kernel won't respond to the // malformed ICMP checksum. TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence, // and ID. None of that should matter for raw sockets - the kernel should @@ -131,7 +131,7 @@ TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) { // Send and receive an ICMP packet. TEST_F(RawSocketICMPTest, SendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); // Prepare and send an ICMP packet. Use arbitrary junk for sequence and ID. // None of that should matter for raw sockets - the kernel should still give @@ -151,7 +151,7 @@ TEST_F(RawSocketICMPTest, SendAndReceive) { // We should be able to create multiple raw sockets for the same protocol and // receive the same packet on both. TEST_F(RawSocketICMPTest, MultipleSocketReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor s2 = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)); @@ -214,7 +214,7 @@ TEST_F(RawSocketICMPTest, MultipleSocketReceive) { // A raw ICMP socket and ping socket should both receive the ICMP packets // intended for the ping socket. TEST_F(RawSocketICMPTest, RawAndPingSockets) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor ping_sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); @@ -264,7 +264,7 @@ TEST_F(RawSocketICMPTest, RawAndPingSockets) { // while a ping socket should not. Neither should be able to receieve a short // malformed packet. TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor ping_sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); @@ -305,7 +305,7 @@ TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) { // while ping socket should not. // Neither should be able to receieve a short malformed packet. TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor ping_sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); @@ -344,7 +344,7 @@ TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) { // Test that connect() sends packets to the right place. TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), @@ -368,7 +368,7 @@ TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) { // Bind to localhost, then send and receive packets. TEST_F(RawSocketICMPTest, BindSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), @@ -391,7 +391,7 @@ TEST_F(RawSocketICMPTest, BindSendAndReceive) { // Bind and connect to localhost and send/receive packets. TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); ASSERT_THAT( bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), @@ -417,7 +417,7 @@ TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) { // Set and get SO_LINGER. TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int level = SOL_SOCKET; int type = SO_LINGER; @@ -439,7 +439,7 @@ TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) { // Test getsockopt for SO_ACCEPTCONN. TEST_F(RawSocketICMPTest, GetSocketAcceptConn) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); int got = -1; socklen_t length = sizeof(got); diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index cb77986c2..3fbbf1423 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -2088,6 +2088,66 @@ TEST_P(SimpleTcpSocketTest, ConnectUnspecifiedAddress) { } } +// Tests that send will return EWOULDBLOCK initially with large buffer and will +// succeed after the send buffer size is increased. +TEST_P(TcpSocketTest, SendUnblocksOnSendBufferIncrease) { + // Set the FD to O_NONBLOCK. + int opts; + ASSERT_THAT(opts = fcntl(first_fd, F_GETFL), SyscallSucceeds()); + opts |= O_NONBLOCK; + ASSERT_THAT(fcntl(first_fd, F_SETFL, opts), SyscallSucceeds()); + + // Get maximum buffer size by trying to set it to a large value. + constexpr int kSndBufSz = 0xffffffff; + ASSERT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, + sizeof(kSndBufSz)), + SyscallSucceeds()); + + int max_buffer_sz = 0; + socklen_t max_len = sizeof(max_buffer_sz); + ASSERT_THAT( + getsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &max_buffer_sz, &max_len), + SyscallSucceeds()); + + int buffer_sz = max_buffer_sz >> 2; + EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &buffer_sz, + sizeof(buffer_sz)), + SyscallSucceedsWithValue(0)); + + // Create a large buffer that will be used for sending. + std::vector<char> buffer(max_buffer_sz); + + // Write until we receive an error. + while (RetryEINTR(send)(first_fd, buffer.data(), buffer.size(), 0) != -1) { + // Sleep to give linux a chance to move data from the send buffer to the + // receive buffer. + usleep(10000); // 10ms. + } + + // The last error should have been EWOULDBLOCK. + ASSERT_EQ(errno, EWOULDBLOCK); + + ScopedThread send_thread([this]() { + int flags = 0; + ASSERT_THAT(flags = fcntl(first_fd, F_GETFL), SyscallSucceeds()); + EXPECT_THAT(fcntl(first_fd, F_SETFL, flags & ~O_NONBLOCK), + SyscallSucceeds()); + + // Expect the send() to succeed. + char buffer; + ASSERT_THAT(RetryEINTR(send)(first_fd, &buffer, sizeof(buffer), 0), + SyscallSucceeds()); + }); + + // Set SO_SNDBUF to maximum buffer size allowed. + buffer_sz = max_buffer_sz >> 1; + EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &buffer_sz, + sizeof(buffer_sz)), + SyscallSucceedsWithValue(0)); + + send_thread.Join(); +} + INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest, ::testing::Values(AF_INET, AF_INET6)); |