diff options
Diffstat (limited to 'test/syscalls/linux/sigaltstack.cc')
-rw-r--r-- | test/syscalls/linux/sigaltstack.cc | 275 |
1 files changed, 0 insertions, 275 deletions
diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc deleted file mode 100644 index 7d4a12c1d..000000000 --- a/test/syscalls/linux/sigaltstack.cc +++ /dev/null @@ -1,275 +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 "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 = - reinterpret_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 |