diff options
author | Rahat Mahmood <rahat@google.com> | 2021-08-05 18:44:10 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-08-05 18:47:30 -0700 |
commit | 15853bdc88523021f89261860eaebab06bb19dbf (patch) | |
tree | 46452d61cbd79c96cc10ae3244f84bfc960206dd /test/syscalls | |
parent | a72efae969e6affc406efa9bccaa23b09e99b43c (diff) |
Replace unsafe use of fork() in msgqueue tests.
Msgqueue tests were using fork() to run create a separate thread of
execution for passing messages back and forth over a queue. However,
the child process after a fork() may only use async-signal-safe
functions, which at a minimum exclude gtest asserts.
Instead, use threads.
PiperOrigin-RevId: 389073744
Diffstat (limited to 'test/syscalls')
-rw-r--r-- | test/syscalls/linux/BUILD | 1 | ||||
-rw-r--r-- | test/syscalls/linux/msgqueue.cc | 175 |
2 files changed, 75 insertions, 101 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 7129a797b..b7e83b5de 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -4176,6 +4176,7 @@ cc_binary( "//test/util:temp_path", "//test/util:test_main", "//test/util:test_util", + "//test/util:thread_util", "@com_google_absl//absl/time", ], ) diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc index 837e913d9..cc4022077 100644 --- a/test/syscalls/linux/msgqueue.cc +++ b/test/syscalls/linux/msgqueue.cc @@ -21,6 +21,7 @@ #include "test/util/capability_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" +#include "test/util/thread_util.h" namespace gvisor { namespace testing { @@ -419,25 +420,19 @@ TEST(MsgqueueTest, MsgRcvBlocking) { msgbuf buf{1, "A message."}; - const pid_t child_pid = fork(); - if (child_pid == 0) { + ScopedThread t([&] { msgbuf rcv; - TEST_PCHECK(RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, - 0) == sizeof(buf.mtext) && - buf == rcv); - _exit(0); - } + ASSERT_THAT( + RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, 0), + SyscallSucceedsWithValue(sizeof(buf.mtext))); + EXPECT_TRUE(rcv == buf); + }); // Sleep to try and make msgrcv block before sending a message. absl::SleepFor(absl::Milliseconds(150)); EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } // Test msgrcv (most probably) waiting for a specific-type message. @@ -451,15 +446,14 @@ TEST(MsgqueueTest, MsgRcvTypeBlocking) { {1, "A message."}, {2, "A different message."}}; - const pid_t child_pid = fork(); - if (child_pid == 0) { + ScopedThread t([&] { msgbuf buf = bufs[4]; // Buffer that should be received. msgbuf rcv; - TEST_PCHECK(RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, 2, - 0) == sizeof(buf.mtext) && - buf == rcv); - _exit(0); - } + ASSERT_THAT( + RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, 2, 0), + SyscallSucceedsWithValue(sizeof(buf.mtext))); + EXPECT_TRUE(rcv == buf); + }); // Sleep to try and make msgrcv block before sending messages. absl::SleepFor(absl::Milliseconds(150)); @@ -469,11 +463,6 @@ TEST(MsgqueueTest, MsgRcvTypeBlocking) { EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), SyscallSucceeds()); } - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } // Test msgsnd (most probably) blocking on a full queue. @@ -493,18 +482,17 @@ TEST(MsgqueueTest, MsgSndBlocking) { const size_t msgCount = msgMnb / msgMax; // Number of messages that can be // sent without blocking. - const pid_t child_pid = fork(); - if (child_pid == 0) { + ScopedThread t([&] { // Fill the queue. for (size_t i = 0; i < msgCount; i++) { - TEST_PCHECK(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0) == 0); + ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0), + SyscallSucceeds()); } // Next msgsnd should block. - TEST_PCHECK(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0) == - 0); - _exit(0); - } + ASSERT_THAT(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0), + SyscallSucceeds()); + }); // To increase the chance of the last msgsnd blocking before doing a msgrcv, // we use MSG_COPY option to copy the last index in the queue. As long as @@ -521,11 +509,6 @@ TEST(MsgqueueTest, MsgSndBlocking) { EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext), 0, 0), SyscallSucceedsWithValue(sizeof(buf.mtext))); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } // Test removing a queue while a blocking msgsnd is executing. @@ -542,8 +525,7 @@ TEST(MsgqueueTest, MsgSndRmWhileBlocking) { const size_t msgCount = msgMnb / msgMax; // Number of messages that can be // sent without blocking. - const pid_t child_pid = fork(); - if (child_pid == 0) { + ScopedThread t([&] { // Fill the queue. msgmax buf{1, ""}; for (size_t i = 0; i < msgCount; i++) { @@ -553,11 +535,10 @@ TEST(MsgqueueTest, MsgSndRmWhileBlocking) { // Next msgsnd should block. Because we're repeating on EINTR, msgsnd may // race with msgctl(IPC_RMID) and return EINVAL. - TEST_PCHECK(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0) == - -1 && - (errno == EIDRM || errno == EINVAL)); - _exit(0); - } + EXPECT_THAT(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0), + SyscallFails()); + EXPECT_TRUE((errno == EIDRM || errno == EINVAL)); + }); // Similar to MsgSndBlocking, we do this to increase the chance of msgsnd // blocking before removing the queue. @@ -569,11 +550,6 @@ TEST(MsgqueueTest, MsgSndRmWhileBlocking) { absl::SleepFor(absl::Milliseconds(100)); EXPECT_THAT(msgctl(queue.release(), IPC_RMID, nullptr), SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } // Test removing a queue while a blocking msgrcv is executing. @@ -581,25 +557,18 @@ TEST(MsgqueueTest, MsgRcvRmWhileBlocking) { Queue queue(msgget(IPC_PRIVATE, 0600)); ASSERT_THAT(queue.get(), SyscallSucceeds()); - const pid_t child_pid = fork(); - if (child_pid == 0) { + ScopedThread t([&] { // Because we're repeating on EINTR, msgsnd may race with msgctl(IPC_RMID) // and return EINVAL. msgbuf rcv; - TEST_PCHECK(RetryEINTR(msgrcv)(queue.get(), &rcv, 1, 2, 0) == -1 && - (errno == EIDRM || errno == EINVAL)); - _exit(0); - } + EXPECT_THAT(RetryEINTR(msgrcv)(queue.get(), &rcv, 1, 2, 0), SyscallFails()); + EXPECT_TRUE(errno == EIDRM || errno == EINVAL); + }); // Sleep to try and make msgrcv block before sending messages. absl::SleepFor(absl::Milliseconds(150)); EXPECT_THAT(msgctl(queue.release(), IPC_RMID, nullptr), SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } // Test a collection of msgsnd/msgrcv operations in different processes. @@ -607,51 +576,55 @@ TEST(MsgqueueTest, MsgOpGeneral) { Queue queue(msgget(IPC_PRIVATE, 0600)); ASSERT_THAT(queue.get(), SyscallSucceeds()); - // Create 50 sending, and 50 receiving processes. There are only 5 messages to - // be sent and received, each with a different type. All messages will be sent - // and received equally (10 of each.) By the end of the test all processes - // should unblock and return normally. - const size_t msgCount = 5; - std::map<int64_t, msgbuf> typeToBuf = {{1, msgbuf{1, "Message 1."}}, - {2, msgbuf{2, "Message 2."}}, - {3, msgbuf{3, "Message 3."}}, - {4, msgbuf{4, "Message 4."}}, - {5, msgbuf{5, "Message 5."}}}; - - std::vector<pid_t> children; - - const size_t pCount = 50; - for (size_t i = 1; i <= pCount; i++) { - const pid_t child_pid = fork(); - if (child_pid == 0) { - msgbuf buf = typeToBuf[(i % msgCount) + 1]; + // 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, + // all threads should succeed and return. + const std::vector<msgbuf> msgs = { + msgbuf{1, "Message 1."}, msgbuf{2, "Message 2."}, msgbuf{3, "Message 3."}, + msgbuf{4, "Message 4."}, msgbuf{5, "Message 5."}}; + + auto receiver = [&](int i) { + return [i, &msgs, &queue]() { + const msgbuf& target = msgs[i]; msgbuf rcv; - TEST_PCHECK(RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, - (i % msgCount) + 1, - 0) == sizeof(buf.mtext) && - buf == rcv); - _exit(0); - } - children.push_back(child_pid); - } + EXPECT_THAT(RetryEINTR(msgrcv)(queue.get(), &rcv, + sizeof(target.mtext) + 1, target.mtype, 0), + SyscallSucceedsWithValue(sizeof(target.mtext))); + EXPECT_EQ(rcv.mtype, target.mtype); + EXPECT_EQ(0, memcmp(rcv.mtext, target.mtext, sizeof(target.mtext))); + }; + }; - for (size_t i = 1; i <= pCount; i++) { - const pid_t child_pid = fork(); - if (child_pid == 0) { - msgbuf buf = typeToBuf[(i % msgCount) + 1]; - TEST_PCHECK(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0) == - 0); - _exit(0); - } - children.push_back(child_pid); - } + ScopedThread r1(receiver(0)); + ScopedThread r2(receiver(1)); + ScopedThread r3(receiver(2)); + ScopedThread r4(receiver(3)); + ScopedThread r5(receiver(4)); + ScopedThread r6(receiver(0)); + ScopedThread r7(receiver(1)); + ScopedThread r8(receiver(2)); + ScopedThread r9(receiver(3)); + ScopedThread r10(receiver(4)); + + auto sender = [&](int i) { + return [i, &msgs, &queue]() { + const msgbuf& target = msgs[i]; + EXPECT_THAT( + RetryEINTR(msgsnd)(queue.get(), &target, sizeof(target.mtext), 0), + SyscallSucceeds()); + }; + }; - for (auto const& pid : children) { - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - } + ScopedThread s1(sender(0)); + ScopedThread s2(sender(1)); + ScopedThread s3(sender(2)); + ScopedThread s4(sender(3)); + ScopedThread s5(sender(4)); + ScopedThread s6(sender(0)); + ScopedThread s7(sender(1)); + ScopedThread s8(sender(2)); + ScopedThread s9(sender(3)); + ScopedThread s10(sender(4)); } } // namespace |