diff options
Diffstat (limited to 'test/syscalls/linux/alarm.cc')
-rw-r--r-- | test/syscalls/linux/alarm.cc | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/test/syscalls/linux/alarm.cc b/test/syscalls/linux/alarm.cc new file mode 100644 index 000000000..e0ddbb415 --- /dev/null +++ b/test/syscalls/linux/alarm.cc @@ -0,0 +1,193 @@ +// 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 <unistd.h> + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// N.B. Below, main blocks SIGALRM. Test cases must unblock it if they want +// delivery. + +void do_nothing_handler(int sig, siginfo_t* siginfo, void* arg) {} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and read. +TEST(AlarmTest, Interrupt_NoRandomSave) { + int pipe_fds[2]; + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + FileDescriptor read_fd(pipe_fds[0]); + FileDescriptor write_fd(pipe_fds[1]); + + // Use a signal handler that interrupts but does nothing rather than using the + // default terminate action. + struct sigaction sa; + sa.sa_sigaction = do_nothing_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after read blocks below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + + char buf; + ASSERT_THAT(read(read_fd.get(), &buf, 1), SyscallFailsWithErrno(EINTR)); +} + +/* Count of the number of SIGALARMS handled. */ +static volatile int alarms_received = 0; + +void inc_alarms_handler(int sig, siginfo_t* siginfo, void* arg) { + alarms_received++; +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and read. +TEST(AlarmTest, Restart_NoRandomSave) { + alarms_received = 0; + + int pipe_fds[2]; + ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); + + FileDescriptor read_fd(pipe_fds[0]); + // Write end closed by thread below. + + struct sigaction sa; + sa.sa_sigaction = inc_alarms_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Spawn a thread to eventually unblock the read below. + ScopedThread t([pipe_fds] { + absl::SleepFor(absl::Seconds(30)); + EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); + }); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after read blocks below, but + // before it returns. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + + // Read and eventually get an EOF from the writer closing. If SA_RESTART + // didn't work, then the alarm would not have fired and we wouldn't increment + // our alarms_received count in our signal handler, or we would have not + // restarted the syscall gracefully, which we expect below in order to be + // able to get the final EOF on the pipe. + char buf; + ASSERT_THAT(read(read_fd.get(), &buf, 1), SyscallSucceeds()); + EXPECT_EQ(alarms_received, 1); + + t.Join(); +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and pause. +TEST(AlarmTest, SaSiginfo_NoRandomSave) { + // Use a signal handler that interrupts but does nothing rather than using the + // default terminate action. + struct sigaction sa; + sa.sa_sigaction = do_nothing_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after pause blocks below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + ASSERT_THAT(pause(), SyscallFailsWithErrno(EINTR)); +} + +// No random save as the test relies on alarm timing. Cooperative save tests +// already cover the save between alarm and pause. +TEST(AlarmTest, SaInterrupt_NoRandomSave) { + // Use a signal handler that interrupts but does nothing rather than using the + // default terminate action. + struct sigaction sa; + sa.sa_sigaction = do_nothing_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_INTERRUPT; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well after pause blocks below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + ASSERT_THAT(pause(), SyscallFailsWithErrno(EINTR)); +} + +TEST(AlarmTest, UserModeSpinning) { + alarms_received = 0; + + struct sigaction sa = {}; + sa.sa_sigaction = inc_alarms_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); + + // Actually allow SIGALRM delivery. + auto mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); + + // Alarm in 20 second, which should be well into the loop below. + ASSERT_THAT(alarm(20), SyscallSucceeds()); + // Make sure that the signal gets delivered even if we are spinning in user + // mode when it arrives. + while (!alarms_received) { + } +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // These tests depend on delivering SIGALRM to the main thread. Block SIGALRM + // so that any other threads created by TestInit will also have SIGALRM + // blocked. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); + + gvisor::testing::TestInit(&argc, &argv); + + return RUN_ALL_TESTS(); +} |