137 lines
4.5 KiB
C++
137 lines
4.5 KiB
C++
// 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();
|
|
}
|