diff options
Diffstat (limited to 'test/syscalls/linux/sigiret.cc')
-rw-r--r-- | test/syscalls/linux/sigiret.cc | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/test/syscalls/linux/sigiret.cc b/test/syscalls/linux/sigiret.cc new file mode 100644 index 000000000..6227774a4 --- /dev/null +++ b/test/syscalls/linux/sigiret.cc @@ -0,0 +1,136 @@ +// 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 <signal.h> +#include <sys/types.h> +#include <sys/ucontext.h> +#include <unistd.h> + +#include "gtest/gtest.h" +#include "test/util/logging.h" +#include "test/util/signal_util.h" +#include "test/util/test_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +constexpr uint64_t kOrigRcx = 0xdeadbeeffacefeed; +constexpr uint64_t kOrigR11 = 0xfacefeedbaad1dea; + +volatile int gotvtalrm, ready; + +void sigvtalrm(int sig, siginfo_t* siginfo, void* _uc) { + ucontext_t* uc = reinterpret_cast<ucontext_t*>(_uc); + + // Verify that: + // - test is in the busy-wait loop waiting for signal. + // - %rcx and %r11 values in mcontext_t match kOrigRcx and kOrigR11. + if (ready && + static_cast<uint64_t>(uc->uc_mcontext.gregs[REG_RCX]) == kOrigRcx && + static_cast<uint64_t>(uc->uc_mcontext.gregs[REG_R11]) == kOrigR11) { + // Modify the values %rcx and %r11 in the ucontext. These are the + // values seen by the application after the signal handler returns. + uc->uc_mcontext.gregs[REG_RCX] = ~kOrigRcx; + uc->uc_mcontext.gregs[REG_R11] = ~kOrigR11; + gotvtalrm = 1; + } +} + +TEST(SigIretTest, CheckRcxR11) { + // Setup signal handler for SIGVTALRM. + struct sigaction sa = {}; + sigfillset(&sa.sa_mask); + sa.sa_sigaction = sigvtalrm; + sa.sa_flags = SA_SIGINFO; + auto const action_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGVTALRM, sa)); + + auto const mask_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGVTALRM)); + + // Setup itimer to fire after 500 msecs. + struct itimerval itimer = {}; + itimer.it_value.tv_usec = 500 * 1000; // 500 msecs. + auto const timer_cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_VIRTUAL, itimer)); + + // Initialize %rcx and %r11 and spin until the signal handler returns. + uint64_t rcx = kOrigRcx; + uint64_t r11 = kOrigR11; + asm volatile( + "movq %[rcx], %%rcx;" // %rcx = rcx + "movq %[r11], %%r11;" // %r11 = r11 + "movl $1, %[ready];" // ready = 1 + "1: pause; cmpl $0, %[gotvtalrm]; je 1b;" // while (!gotvtalrm); + "movq %%rcx, %[rcx];" // rcx = %rcx + "movq %%r11, %[r11];" // r11 = %r11 + : [ ready ] "=m"(ready), [ rcx ] "+m"(rcx), [ r11 ] "+m"(r11) + : [ gotvtalrm ] "m"(gotvtalrm) + : "cc", "memory", "rcx", "r11"); + + // If sigreturn(2) returns via 'sysret' then %rcx and %r11 will be + // clobbered and set to 'ptregs->rip' and 'ptregs->rflags' respectively. + // + // The following check verifies that %rcx and %r11 were not clobbered + // when returning from the signal handler (via sigreturn(2)). + EXPECT_EQ(rcx, ~kOrigRcx); + EXPECT_EQ(r11, ~kOrigR11); +} + +constexpr uint64_t kNonCanonicalRip = 0xCCCC000000000000; + +// Test that a non-canonical signal handler faults as expected. +TEST(SigIretTest, BadHandler) { + struct sigaction sa = {}; + sa.sa_sigaction = + reinterpret_cast<void (*)(int, siginfo_t*, void*)>(kNonCanonicalRip); + auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); + + pid_t pid = fork(); + if (pid == 0) { + // Child, wait for signal. + while (1) { + pause(); + } + } + ASSERT_THAT(pid, SyscallSucceeds()); + + EXPECT_THAT(kill(pid, SIGUSR1), SyscallSucceeds()); + + int status; + EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + << "status = " << status; +} + +} // namespace + +} // namespace testing +} // namespace gvisor + +int main(int argc, char** argv) { + // SigIretTest.CheckRcxR11 depends on delivering SIGVTALRM to the main thread. + // Block SIGVTALRM so that any other threads created by TestInit will also + // have SIGVTALRM blocked. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGVTALRM); + TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); + + gvisor::testing::TestInit(&argc, &argv); + return gvisor::testing::RunAllTests(); +} |