summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/sigprocmask.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/sigprocmask.cc')
-rw-r--r--test/syscalls/linux/sigprocmask.cc272
1 files changed, 272 insertions, 0 deletions
diff --git a/test/syscalls/linux/sigprocmask.cc b/test/syscalls/linux/sigprocmask.cc
new file mode 100644
index 000000000..d8b918446
--- /dev/null
+++ b/test/syscalls/linux/sigprocmask.cc
@@ -0,0 +1,272 @@
+// Copyright 2018 Google LLC
+//
+// 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 <signal.h>
+#include <stddef.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "test/util/signal_util.h"
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+// Signals numbers used for testing.
+static constexpr int kTestSignal1 = SIGUSR1;
+static constexpr int kTestSignal2 = SIGUSR2;
+
+static int raw_sigprocmask(int how, const sigset_t* set, sigset_t* oldset) {
+ return syscall(SYS_rt_sigprocmask, how, set, oldset, _NSIG / 8);
+}
+
+// count of the number of signals received
+int signal_count[kMaxSignal + 1];
+
+// signal handler increments the signal counter
+void SigHandler(int sig, siginfo_t* info, void* context) {
+ TEST_CHECK(sig > 0 && sig <= kMaxSignal);
+ signal_count[sig] += 1;
+}
+
+// The test fixture saves and restores the signal mask and
+// sets up handlers for kTestSignal1 and kTestSignal2.
+class SigProcMaskTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // Save the current signal mask.
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &mask_),
+ SyscallSucceeds());
+
+ // Setup signal handlers for kTestSignal1 and kTestSignal2.
+ struct sigaction sa;
+ sa.sa_sigaction = SigHandler;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ EXPECT_THAT(sigaction(kTestSignal1, &sa, &sa_test_sig_1_),
+ SyscallSucceeds());
+ EXPECT_THAT(sigaction(kTestSignal2, &sa, &sa_test_sig_2_),
+ SyscallSucceeds());
+
+ // Clear the signal counters.
+ memset(signal_count, 0, sizeof(signal_count));
+ }
+
+ void TearDown() override {
+ // Restore the signal mask.
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &mask_, nullptr),
+ SyscallSucceeds());
+
+ // Restore the signal handlers for kTestSignal1 and kTestSignal2.
+ EXPECT_THAT(sigaction(kTestSignal1, &sa_test_sig_1_, nullptr),
+ SyscallSucceeds());
+ EXPECT_THAT(sigaction(kTestSignal2, &sa_test_sig_2_, nullptr),
+ SyscallSucceeds());
+ }
+
+ private:
+ sigset_t mask_;
+ struct sigaction sa_test_sig_1_;
+ struct sigaction sa_test_sig_2_;
+};
+
+// Both sigsets nullptr should succeed and do nothing.
+TEST_F(SigProcMaskTest, NullAddress) {
+ EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, nullptr, NULL), SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, nullptr, NULL), SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, NULL), SyscallSucceeds());
+}
+
+// Bad address for either sigset should fail with EFAULT.
+TEST_F(SigProcMaskTest, BadAddress) {
+ sigset_t* bad_addr = reinterpret_cast<sigset_t*>(-1);
+
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, bad_addr, nullptr),
+ SyscallFailsWithErrno(EFAULT));
+
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, bad_addr),
+ SyscallFailsWithErrno(EFAULT));
+}
+
+// Bad value of the "how" parameter should fail with EINVAL.
+TEST_F(SigProcMaskTest, BadParameter) {
+ int bad_param_1 = -1;
+ int bad_param_2 = 42;
+
+ sigset_t set1;
+ sigemptyset(&set1);
+
+ EXPECT_THAT(raw_sigprocmask(bad_param_1, &set1, nullptr),
+ SyscallFailsWithErrno(EINVAL));
+
+ EXPECT_THAT(raw_sigprocmask(bad_param_2, &set1, nullptr),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+// Check that we can get the current signal mask.
+TEST_F(SigProcMaskTest, GetMask) {
+ sigset_t set1;
+ sigset_t set2;
+
+ sigemptyset(&set1);
+ sigfillset(&set2);
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &set1), SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &set2), SyscallSucceeds());
+ EXPECT_THAT(set1, EqualsSigset(set2));
+}
+
+// Check that we can set the signal mask.
+TEST_F(SigProcMaskTest, SetMask) {
+ sigset_t actual;
+ sigset_t expected;
+
+ // Try to mask all signals
+ sigfillset(&expected);
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr),
+ SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual),
+ SyscallSucceeds());
+ // sigprocmask() should have silently ignored SIGKILL and SIGSTOP.
+ sigdelset(&expected, SIGSTOP);
+ sigdelset(&expected, SIGKILL);
+ EXPECT_THAT(actual, EqualsSigset(expected));
+
+ // Try to clear the signal mask
+ sigemptyset(&expected);
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr),
+ SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual),
+ SyscallSucceeds());
+ EXPECT_THAT(actual, EqualsSigset(expected));
+
+ // Try to set a mask with one signal.
+ sigemptyset(&expected);
+ sigaddset(&expected, kTestSignal1);
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr),
+ SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual),
+ SyscallSucceeds());
+ EXPECT_THAT(actual, EqualsSigset(expected));
+}
+
+// Check that we can add and remove signals.
+TEST_F(SigProcMaskTest, BlockUnblock) {
+ sigset_t actual;
+ sigset_t expected;
+
+ // Try to set a mask with one signal.
+ sigemptyset(&expected);
+ sigaddset(&expected, kTestSignal1);
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr),
+ SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual),
+ SyscallSucceeds());
+ EXPECT_THAT(actual, EqualsSigset(expected));
+
+ // Try to add another signal.
+ sigset_t block;
+ sigemptyset(&block);
+ sigaddset(&block, kTestSignal2);
+ EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, &block, nullptr), SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual),
+ SyscallSucceeds());
+ sigaddset(&expected, kTestSignal2);
+ EXPECT_THAT(actual, EqualsSigset(expected));
+
+ // Try to remove a signal.
+ sigset_t unblock;
+ sigemptyset(&unblock);
+ sigaddset(&unblock, kTestSignal1);
+ EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, &unblock, nullptr),
+ SyscallSucceeds());
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual),
+ SyscallSucceeds());
+ sigdelset(&expected, kTestSignal1);
+ EXPECT_THAT(actual, EqualsSigset(expected));
+}
+
+// Test that the signal mask actually blocks signals.
+TEST_F(SigProcMaskTest, SignalHandler) {
+ sigset_t mask;
+
+ // clear the signal mask
+ sigemptyset(&mask);
+ EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, nullptr), SyscallSucceeds());
+
+ // Check the initial signal counts.
+ EXPECT_EQ(0, signal_count[kTestSignal1]);
+ EXPECT_EQ(0, signal_count[kTestSignal2]);
+
+ // Check that both kTestSignal1 and kTestSignal2 are not blocked.
+ raise(kTestSignal1);
+ raise(kTestSignal2);
+ EXPECT_EQ(1, signal_count[kTestSignal1]);
+ EXPECT_EQ(1, signal_count[kTestSignal2]);
+
+ // Block kTestSignal1.
+ sigaddset(&mask, kTestSignal1);
+ EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, &mask, nullptr), SyscallSucceeds());
+
+ // Check that kTestSignal1 is blocked.
+ raise(kTestSignal1);
+ raise(kTestSignal2);
+ EXPECT_EQ(1, signal_count[kTestSignal1]);
+ EXPECT_EQ(2, signal_count[kTestSignal2]);
+
+ // Unblock kTestSignal1.
+ sigaddset(&mask, kTestSignal1);
+ EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, &mask, nullptr), SyscallSucceeds());
+
+ // Check that the unblocked kTestSignal1 has been delivered.
+ // TODO: gvisor currently drops masked signals on the floor.
+ if (!IsRunningOnGvisor()) {
+ EXPECT_EQ(2, signal_count[kTestSignal1]);
+ }
+ EXPECT_EQ(2, signal_count[kTestSignal2]);
+}
+
+// Check that sigprocmask correctly handles aliasing of the set and oldset
+// pointers.
+TEST_F(SigProcMaskTest, AliasedSets) {
+ sigset_t mask;
+
+ // Set a mask in which only kTestSignal1 is blocked.
+ sigset_t mask1;
+ sigemptyset(&mask1);
+ sigaddset(&mask1, kTestSignal1);
+ mask = mask1;
+ ASSERT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, nullptr), SyscallSucceeds());
+
+ // Exchange it with a mask in which only kTestSignal2 is blocked.
+ sigset_t mask2;
+ sigemptyset(&mask2);
+ sigaddset(&mask2, kTestSignal2);
+ mask = mask2;
+ ASSERT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, &mask), SyscallSucceeds());
+
+ // Check that the exchange succeeeded:
+ // mask should now contain the previously-set mask blocking only kTestSignal1.
+ EXPECT_THAT(mask, EqualsSigset(mask1));
+ // The current mask should block only kTestSignal2.
+ ASSERT_THAT(raw_sigprocmask(0, nullptr, &mask), SyscallSucceeds());
+ EXPECT_THAT(mask, EqualsSigset(mask2));
+}
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor