summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/sigaltstack.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/sigaltstack.cc')
-rw-r--r--test/syscalls/linux/sigaltstack.cc274
1 files changed, 0 insertions, 274 deletions
diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc
deleted file mode 100644
index 6fd3989a4..000000000
--- a/test/syscalls/linux/sigaltstack.cc
+++ /dev/null
@@ -1,274 +0,0 @@
-// 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 <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <functional>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "test/util/cleanup.h"
-#include "test/util/fs_util.h"
-#include "test/util/multiprocess_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/signal_util.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-PosixErrorOr<Cleanup> ScopedSigaltstack(stack_t const& stack) {
- stack_t old_stack;
- int rc = sigaltstack(&stack, &old_stack);
- MaybeSave();
- if (rc < 0) {
- return PosixError(errno, "sigaltstack failed");
- }
- return Cleanup([old_stack] {
- EXPECT_THAT(sigaltstack(&old_stack, nullptr), SyscallSucceeds());
- });
-}
-
-volatile bool got_signal = false;
-volatile int sigaltstack_errno = 0;
-volatile int ss_flags = 0;
-
-void sigaltstack_handler(int sig, siginfo_t* siginfo, void* arg) {
- got_signal = true;
-
- stack_t stack;
- int ret = sigaltstack(nullptr, &stack);
- MaybeSave();
- if (ret < 0) {
- sigaltstack_errno = errno;
- return;
- }
- ss_flags = stack.ss_flags;
-}
-
-TEST(SigaltstackTest, Success) {
- std::vector<char> stack_mem(SIGSTKSZ);
- stack_t stack = {};
- stack.ss_sp = stack_mem.data();
- stack.ss_size = stack_mem.size();
- auto const cleanup_sigstack =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack));
-
- struct sigaction sa = {};
- sa.sa_sigaction = sigaltstack_handler;
- sigfillset(&sa.sa_mask);
- sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
- auto const cleanup_sa =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa));
-
- // Send signal to this thread, as sigaltstack is per-thread.
- EXPECT_THAT(tgkill(getpid(), gettid(), SIGUSR1), SyscallSucceeds());
-
- EXPECT_TRUE(got_signal);
- EXPECT_EQ(sigaltstack_errno, 0);
- EXPECT_NE(0, ss_flags & SS_ONSTACK);
-}
-
-TEST(SigaltstackTest, ResetByExecve) {
- std::vector<char> stack_mem(SIGSTKSZ);
- stack_t stack = {};
- stack.ss_sp = stack_mem.data();
- stack.ss_size = stack_mem.size();
- auto const cleanup_sigstack =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack));
-
- std::string full_path;
- char* test_src = getenv("TEST_SRCDIR");
- if (test_src) {
- full_path = JoinPath(test_src, "../../linux/sigaltstack_check");
- }
-
- ASSERT_FALSE(full_path.empty());
-
- pid_t child_pid = -1;
- int execve_errno = 0;
- auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(
- ForkAndExec(full_path, {"sigaltstack_check"}, {}, nullptr, &child_pid,
- &execve_errno));
-
- ASSERT_GT(child_pid, 0);
- ASSERT_EQ(execve_errno, 0);
-
- int status = 0;
- ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds());
- ASSERT_TRUE(WIFEXITED(status));
- ASSERT_EQ(WEXITSTATUS(status), 0);
-}
-
-volatile bool badhandler_on_sigaltstack = true; // Set by the handler.
-char* volatile badhandler_low_water_mark = nullptr; // Set by the handler.
-volatile uint8_t badhandler_recursive_faults = 0; // Consumed by the handler.
-
-void badhandler(int sig, siginfo_t* siginfo, void* arg) {
- char stack_var = 0;
- char* current_ss = &stack_var;
-
- stack_t stack;
- int ret = sigaltstack(nullptr, &stack);
- if (ret < 0 || (stack.ss_flags & SS_ONSTACK) != SS_ONSTACK) {
- // We should always be marked as being on the stack. Don't allow this to hit
- // the bottom if this is ever not true (the main test will fail as a
- // result, but we still need to unwind the recursive faults).
- badhandler_on_sigaltstack = false;
- }
- if (current_ss < badhandler_low_water_mark) {
- // Record the low point for the signal stack. We never expected this to be
- // before stack bottom, but this is asserted in the actual test.
- badhandler_low_water_mark = current_ss;
- }
- if (badhandler_recursive_faults > 0) {
- badhandler_recursive_faults--;
- Fault();
- }
- FixupFault(reinterpret_cast<ucontext_t*>(arg));
-}
-
-TEST(SigaltstackTest, WalksOffBottom) {
- // This test marks the upper half of the stack_mem array as the signal stack.
- // It asserts that when a fault occurs in the handler (already on the signal
- // stack), we eventually continue to fault our way off the stack. We should
- // not revert to the top of the signal stack when we fall off the bottom and
- // the signal stack should remain "in use". When we fall off the signal stack,
- // we should have an unconditional signal delivered and not start using the
- // first part of the stack_mem array.
- std::vector<char> stack_mem(SIGSTKSZ * 2);
- stack_t stack = {};
- stack.ss_sp = stack_mem.data() + SIGSTKSZ; // See above: upper half.
- stack.ss_size = SIGSTKSZ; // Only one half the array.
- auto const cleanup_sigstack =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack));
-
- // Setup the handler: this must be for SIGSEGV, and it must allow proper
- // nesting (no signal mask, no defer) so that we can trigger multiple times.
- //
- // When we walk off the bottom of the signal stack and force signal delivery
- // of a SIGSEGV, the handler will revert to the default behavior (kill).
- struct sigaction sa = {};
- sa.sa_sigaction = badhandler;
- sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER;
- auto const cleanup_sa =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa));
-
- // Trigger a single fault.
- badhandler_low_water_mark =
- static_cast<char*>(stack.ss_sp) + SIGSTKSZ; // Expected top.
- badhandler_recursive_faults = 0; // Disable refault.
- Fault();
- EXPECT_TRUE(badhandler_on_sigaltstack);
- EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds());
- EXPECT_EQ(stack.ss_flags & SS_ONSTACK, 0);
- EXPECT_LT(badhandler_low_water_mark,
- reinterpret_cast<char*>(stack.ss_sp) + 2 * SIGSTKSZ);
- EXPECT_GT(badhandler_low_water_mark, reinterpret_cast<char*>(stack.ss_sp));
-
- // Trigger two faults.
- char* prev_low_water_mark = badhandler_low_water_mark; // Previous top.
- badhandler_recursive_faults = 1; // One refault.
- Fault();
- ASSERT_TRUE(badhandler_on_sigaltstack);
- EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds());
- EXPECT_EQ(stack.ss_flags & SS_ONSTACK, 0);
- EXPECT_LT(badhandler_low_water_mark, prev_low_water_mark);
- EXPECT_GT(badhandler_low_water_mark, reinterpret_cast<char*>(stack.ss_sp));
-
- // Calculate the stack growth for a fault, and set the recursive faults to
- // ensure that the signal handler stack required exceeds our marked stack area
- // by a minimal amount. It should remain in the valid stack_mem area so that
- // we can test the signal is forced merely by going out of the signal stack
- // bounds, not by a genuine fault.
- uintptr_t frame_size =
- static_cast<uintptr_t>(prev_low_water_mark - badhandler_low_water_mark);
- badhandler_recursive_faults = (SIGSTKSZ + frame_size) / frame_size;
- EXPECT_EXIT(Fault(), ::testing::KilledBySignal(SIGSEGV), "");
-}
-
-volatile int setonstack_retval = 0; // Set by the handler.
-volatile int setonstack_errno = 0; // Set by the handler.
-
-void setonstack(int sig, siginfo_t* siginfo, void* arg) {
- char stack_mem[SIGSTKSZ];
- stack_t stack = {};
- stack.ss_sp = &stack_mem[0];
- stack.ss_size = SIGSTKSZ;
- setonstack_retval = sigaltstack(&stack, nullptr);
- setonstack_errno = errno;
- FixupFault(reinterpret_cast<ucontext_t*>(arg));
-}
-
-TEST(SigaltstackTest, SetWhileOnStack) {
- // Reserve twice as much stack here, since the handler will allocate a vector
- // of size SIGTKSZ and attempt to set the sigaltstack to that value.
- std::vector<char> stack_mem(2 * SIGSTKSZ);
- stack_t stack = {};
- stack.ss_sp = stack_mem.data();
- stack.ss_size = stack_mem.size();
- auto const cleanup_sigstack =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack));
-
- // See above.
- struct sigaction sa = {};
- sa.sa_sigaction = setonstack;
- sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
- auto const cleanup_sa =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa));
-
- // Trigger a fault.
- Fault();
-
- // The set should have failed.
- EXPECT_EQ(setonstack_retval, -1);
- EXPECT_EQ(setonstack_errno, EPERM);
-}
-
-TEST(SigaltstackTest, SetCurrentStack) {
- // This is executed as an exit test because once the signal stack is set to
- // the local stack, there's no good way to unwind. We don't want to taint the
- // test of any other tests that might run within this process.
- EXPECT_EXIT(
- {
- char stack_value = 0;
- stack_t stack = {};
- stack.ss_sp = &stack_value - kPageSize; // Lower than current level.
- stack.ss_size = 2 * kPageSize; // => &stack_value +/- kPageSize.
- TEST_CHECK(sigaltstack(&stack, nullptr) == 0);
- TEST_CHECK(sigaltstack(nullptr, &stack) == 0);
- TEST_CHECK((stack.ss_flags & SS_ONSTACK) != 0);
-
- // Should not be able to change the stack (even no-op).
- TEST_CHECK(sigaltstack(&stack, nullptr) == -1 && errno == EPERM);
-
- // Should not be able to disable the stack.
- stack.ss_flags = SS_DISABLE;
- TEST_CHECK(sigaltstack(&stack, nullptr) == -1 && errno == EPERM);
- exit(0);
- },
- ::testing::ExitedWithCode(0), "");
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor