Add tests for rseq(2)
Add a decent set of syscall tests for rseq(2). These are a bit awkward because of issues with library integration. libc may register rseq on thread start (including before main on the initial thread), precluding much testing. Thus we run tests in a libc-free subprocess. Support for rseq(2) in gVisor will come in a later commit. PiperOrigin-RevId: 284595994
This commit is contained in:
parent
01eadf51ea
commit
498595d543
|
@ -376,6 +376,8 @@ syscall_test(
|
||||||
|
|
||||||
syscall_test(test = "//test/syscalls/linux:rlimits_test")
|
syscall_test(test = "//test/syscalls/linux:rlimits_test")
|
||||||
|
|
||||||
|
syscall_test(test = "//test/syscalls/linux:rseq_test")
|
||||||
|
|
||||||
syscall_test(test = "//test/syscalls/linux:rtsignal_test")
|
syscall_test(test = "//test/syscalls/linux:rtsignal_test")
|
||||||
|
|
||||||
syscall_test(test = "//test/syscalls/linux:sched_test")
|
syscall_test(test = "//test/syscalls/linux:sched_test")
|
||||||
|
|
|
@ -1852,6 +1852,22 @@ cc_binary(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "rseq_test",
|
||||||
|
testonly = 1,
|
||||||
|
srcs = ["rseq.cc"],
|
||||||
|
data = ["//test/syscalls/linux/rseq"],
|
||||||
|
linkstatic = 1,
|
||||||
|
deps = [
|
||||||
|
"//test/syscalls/linux/rseq:lib",
|
||||||
|
"//test/util:logging",
|
||||||
|
"//test/util:multiprocess_util",
|
||||||
|
"//test/util:test_main",
|
||||||
|
"//test/util:test_util",
|
||||||
|
"@com_google_googletest//:gtest",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
name = "rtsignal_test",
|
name = "rtsignal_test",
|
||||||
testonly = 1,
|
testonly = 1,
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
// Copyright 2019 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 <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/syscalls/linux/rseq/test.h"
|
||||||
|
#include "test/syscalls/linux/rseq/uapi.h"
|
||||||
|
#include "test/util/logging.h"
|
||||||
|
#include "test/util/multiprocess_util.h"
|
||||||
|
#include "test/util/test_util.h"
|
||||||
|
|
||||||
|
namespace gvisor {
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Syscall test for rseq (restartable sequences).
|
||||||
|
//
|
||||||
|
// We must be very careful about how these tests are written. Each thread may
|
||||||
|
// only have one struct rseq registration, which may be done automatically at
|
||||||
|
// thread start (as of 2019-11-13, glibc does *not* support rseq and thus does
|
||||||
|
// not do so).
|
||||||
|
//
|
||||||
|
// Testing of rseq is thus done primarily in a child process with no
|
||||||
|
// registration. This means exec'ing a nostdlib binary, as rseq registration can
|
||||||
|
// only be cleared by execve (or knowing the old rseq address), and glibc (based
|
||||||
|
// on the current unmerged patches) register rseq before calling main()).
|
||||||
|
|
||||||
|
int RSeq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) {
|
||||||
|
return syscall(kRseqSyscall, rseq, rseq_len, flags, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if this kernel supports the rseq syscall.
|
||||||
|
PosixErrorOr<bool> RSeqSupported() {
|
||||||
|
// We have to be careful here, there are three possible cases:
|
||||||
|
//
|
||||||
|
// 1. rseq is not supported -> ENOSYS
|
||||||
|
// 2. rseq is supported and not registered -> success, but we should
|
||||||
|
// unregister.
|
||||||
|
// 3. rseq is supported and registered -> EINVAL (most likely).
|
||||||
|
|
||||||
|
// The only validation done on new registrations is that rseq is aligned and
|
||||||
|
// writable.
|
||||||
|
rseq rseq = {};
|
||||||
|
int ret = RSeq(&rseq, sizeof(rseq), 0, 0);
|
||||||
|
if (ret == 0) {
|
||||||
|
// Successfully registered, rseq is supported. Unregister.
|
||||||
|
ret = RSeq(&rseq, sizeof(rseq), kRseqFlagUnregister, 0);
|
||||||
|
if (ret != 0) {
|
||||||
|
return PosixError(errno);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case ENOSYS:
|
||||||
|
// Not supported.
|
||||||
|
return false;
|
||||||
|
case EINVAL:
|
||||||
|
// Supported, but already registered. EINVAL returned because we provided
|
||||||
|
// a different address.
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
// Unknown error.
|
||||||
|
return PosixError(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char kRseqBinary[] = "test/syscalls/linux/rseq/rseq";
|
||||||
|
|
||||||
|
void RunChildTest(std::string test_case, int want_status) {
|
||||||
|
std::string path = RunfilePath(kRseqBinary);
|
||||||
|
|
||||||
|
pid_t child_pid = -1;
|
||||||
|
int execve_errno = 0;
|
||||||
|
auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(
|
||||||
|
ForkAndExec(path, {path, test_case}, {}, &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_EQ(status, want_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that rseq must be aligned.
|
||||||
|
TEST(RseqTest, Unaligned) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestUnaligned, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity test that registration works.
|
||||||
|
TEST(RseqTest, Register) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestRegister, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registration can't be done twice.
|
||||||
|
TEST(RseqTest, DoubleRegister) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestDoubleRegister, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registration can be done again after unregister.
|
||||||
|
TEST(RseqTest, RegisterUnregister) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestRegisterUnregister, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The pointer to rseq must match on register/unregister.
|
||||||
|
TEST(RseqTest, UnregisterDifferentPtr) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestUnregisterDifferentPtr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The signature must match on register/unregister.
|
||||||
|
TEST(RseqTest, UnregisterDifferentSignature) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestUnregisterDifferentSignature, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CPU ID is initialized.
|
||||||
|
TEST(RseqTest, CPU) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestCPU, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Critical section is eventually aborted.
|
||||||
|
TEST(RseqTest, Abort) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestAbort, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort may be before the critical section.
|
||||||
|
TEST(RseqTest, AbortBefore) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestAbortBefore, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature must match.
|
||||||
|
TEST(RseqTest, AbortSignature) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestAbortSignature, SIGSEGV);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort must not be in the critical section.
|
||||||
|
TEST(RseqTest, AbortPreCommit) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestAbortPreCommit, SIGSEGV);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rseq.rseq_cs is cleared on abort.
|
||||||
|
TEST(RseqTest, AbortClearsCS) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestAbortClearsCS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rseq.rseq_cs is cleared on abort outside of critical section.
|
||||||
|
TEST(RseqTest, InvalidAbortClearsCS) {
|
||||||
|
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
|
||||||
|
|
||||||
|
RunChildTest(kRseqTestInvalidAbortClearsCS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace gvisor
|
|
@ -0,0 +1,59 @@
|
||||||
|
# This package contains a standalone rseq test binary. This binary must not
|
||||||
|
# depend on libc, which might use rseq itself.
|
||||||
|
|
||||||
|
load("@bazel_tools//tools/cpp:cc_flags_supplier.bzl", "cc_flags_supplier")
|
||||||
|
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||||
|
|
||||||
|
package(licenses = ["notice"])
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "rseq_binary",
|
||||||
|
srcs = [
|
||||||
|
"critical.h",
|
||||||
|
"critical.S",
|
||||||
|
"rseq.cc",
|
||||||
|
"syscalls.h",
|
||||||
|
"start.S",
|
||||||
|
"test.h",
|
||||||
|
"types.h",
|
||||||
|
"uapi.h",
|
||||||
|
],
|
||||||
|
outs = ["rseq"],
|
||||||
|
cmd = " ".join([
|
||||||
|
"$(CC)",
|
||||||
|
"$(CC_FLAGS) ",
|
||||||
|
"-I.",
|
||||||
|
"-Wall",
|
||||||
|
"-Werror",
|
||||||
|
"-O2",
|
||||||
|
"-std=c++17",
|
||||||
|
"-static",
|
||||||
|
"-nostdlib",
|
||||||
|
"-ffreestanding",
|
||||||
|
"-o",
|
||||||
|
"$(location rseq)",
|
||||||
|
"$(location critical.S)",
|
||||||
|
"$(location rseq.cc)",
|
||||||
|
"$(location start.S)",
|
||||||
|
]),
|
||||||
|
toolchains = [
|
||||||
|
":no_pie_cc_flags",
|
||||||
|
"@bazel_tools//tools/cpp:current_cc_toolchain",
|
||||||
|
],
|
||||||
|
visibility = ["//:sandbox"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_flags_supplier(
|
||||||
|
name = "no_pie_cc_flags",
|
||||||
|
features = ["-pie"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "lib",
|
||||||
|
testonly = 1,
|
||||||
|
hdrs = [
|
||||||
|
"test.h",
|
||||||
|
"uapi.h",
|
||||||
|
],
|
||||||
|
visibility = ["//:sandbox"],
|
||||||
|
)
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
// Restartable sequences critical sections.
|
||||||
|
|
||||||
|
// Loops continuously until aborted.
|
||||||
|
//
|
||||||
|
// void rseq_loop(struct rseq* r, struct rseq_cs* cs)
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl rseq_loop
|
||||||
|
.type rseq_loop, @function
|
||||||
|
|
||||||
|
rseq_loop:
|
||||||
|
jmp begin
|
||||||
|
|
||||||
|
// Abort block before the critical section.
|
||||||
|
// Abort signature is 4 nops for simplicity.
|
||||||
|
.byte 0x90, 0x90, 0x90, 0x90
|
||||||
|
.globl rseq_loop_early_abort
|
||||||
|
rseq_loop_early_abort:
|
||||||
|
ret
|
||||||
|
|
||||||
|
begin:
|
||||||
|
// r->rseq_cs = cs
|
||||||
|
movq %rsi, 8(%rdi)
|
||||||
|
|
||||||
|
// N.B. rseq_cs will be cleared by any preempt, even outside the critical
|
||||||
|
// section. Thus it must be set in or immediately before the critical section
|
||||||
|
// to ensure it is not cleared before the section begins.
|
||||||
|
.globl rseq_loop_start
|
||||||
|
rseq_loop_start:
|
||||||
|
jmp rseq_loop_start
|
||||||
|
|
||||||
|
// "Pre-commit": extra instructions inside the critical section. These are
|
||||||
|
// used as the abort point in TestAbortPreCommit, which is not valid.
|
||||||
|
.globl rseq_loop_pre_commit
|
||||||
|
rseq_loop_pre_commit:
|
||||||
|
// Extra abort signature + nop for TestAbortPostCommit.
|
||||||
|
.byte 0x90, 0x90, 0x90, 0x90
|
||||||
|
nop
|
||||||
|
|
||||||
|
// "Post-commit": never reached in this case.
|
||||||
|
.globl rseq_loop_post_commit
|
||||||
|
rseq_loop_post_commit:
|
||||||
|
|
||||||
|
// Abort signature is 4 nops for simplicity.
|
||||||
|
.byte 0x90, 0x90, 0x90, 0x90
|
||||||
|
|
||||||
|
.globl rseq_loop_abort
|
||||||
|
rseq_loop_abort:
|
||||||
|
ret
|
||||||
|
|
||||||
|
.size rseq_loop,.-rseq_loop
|
||||||
|
.section .note.GNU-stack,"",@progbits
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
#ifndef GVISOR_TEST_SYSCALLS_LINUX_RSEQ_CRITICAL_H_
|
||||||
|
#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_CRITICAL_H_
|
||||||
|
|
||||||
|
#include "test/syscalls/linux/rseq/types.h"
|
||||||
|
#include "test/syscalls/linux/rseq/uapi.h"
|
||||||
|
|
||||||
|
constexpr uint32_t kRseqSignature = 0x90909090;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
extern void rseq_loop(struct rseq* r, struct rseq_cs* cs);
|
||||||
|
extern void* rseq_loop_early_abort;
|
||||||
|
extern void* rseq_loop_start;
|
||||||
|
extern void* rseq_loop_pre_commit;
|
||||||
|
extern void* rseq_loop_post_commit;
|
||||||
|
extern void* rseq_loop_abort;
|
||||||
|
|
||||||
|
extern int rseq_getpid(struct rseq* r, struct rseq_cs* cs);
|
||||||
|
extern void* rseq_getpid_start;
|
||||||
|
extern void* rseq_getpid_post_commit;
|
||||||
|
extern void* rseq_getpid_abort;
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_CRITICAL_H_
|
|
@ -0,0 +1,366 @@
|
||||||
|
// Copyright 2019 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 "test/syscalls/linux/rseq/critical.h"
|
||||||
|
#include "test/syscalls/linux/rseq/syscalls.h"
|
||||||
|
#include "test/syscalls/linux/rseq/test.h"
|
||||||
|
#include "test/syscalls/linux/rseq/types.h"
|
||||||
|
#include "test/syscalls/linux/rseq/uapi.h"
|
||||||
|
|
||||||
|
namespace gvisor {
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
extern "C" int main(int argc, char** argv, char** envp);
|
||||||
|
|
||||||
|
// Standalone initialization before calling main().
|
||||||
|
extern "C" void __init(uintptr_t* sp) {
|
||||||
|
int argc = sp[0];
|
||||||
|
char** argv = reinterpret_cast<char**>(&sp[1]);
|
||||||
|
char** envp = &argv[argc + 1];
|
||||||
|
|
||||||
|
// Call main() and exit.
|
||||||
|
sys_exit_group(main(argc, argv, envp));
|
||||||
|
|
||||||
|
// sys_exit_group does not return
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcmp(const char* s1, const char* s2) {
|
||||||
|
const unsigned char* p1 = reinterpret_cast<const unsigned char*>(s1);
|
||||||
|
const unsigned char* p2 = reinterpret_cast<const unsigned char*>(s2);
|
||||||
|
|
||||||
|
while (*p1 == *p2) {
|
||||||
|
if (!*p1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
++p1;
|
||||||
|
++p2;
|
||||||
|
}
|
||||||
|
return static_cast<int>(*p1) - static_cast<int>(*p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sys_rseq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) {
|
||||||
|
return raw_syscall(kRseqSyscall, rseq, rseq_len, flags, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that rseq must be aligned.
|
||||||
|
int TestUnaligned() {
|
||||||
|
constexpr uintptr_t kRequiredAlignment = alignof(rseq);
|
||||||
|
|
||||||
|
char buf[2 * kRequiredAlignment] = {};
|
||||||
|
uintptr_t ptr = reinterpret_cast<uintptr_t>(&buf[0]);
|
||||||
|
if ((ptr & (kRequiredAlignment - 1)) == 0) {
|
||||||
|
// buf is already aligned. Misalign it.
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = sys_rseq(reinterpret_cast<rseq*>(ptr), sizeof(rseq), 0, 0);
|
||||||
|
if (sys_errno(ret) != EINVAL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity test that registration works.
|
||||||
|
int TestRegister() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registration can't be done twice.
|
||||||
|
int TestDoubleRegister() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != EBUSY) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registration can be done again after unregister.
|
||||||
|
int TestRegisterUnregister() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, 0);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The pointer to rseq must match on register/unregister.
|
||||||
|
int TestUnregisterDifferentPtr() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq r2 = {};
|
||||||
|
if (int ret = sys_rseq(&r2, sizeof(r2), kRseqFlagUnregister, 0);
|
||||||
|
sys_errno(ret) != EINVAL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The signature must match on register/unregister.
|
||||||
|
int TestUnregisterDifferentSignature() {
|
||||||
|
constexpr int kSignature = 0;
|
||||||
|
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kSignature); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, kSignature + 1);
|
||||||
|
sys_errno(ret) != EPERM) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The CPU ID is initialized.
|
||||||
|
int TestCPU() {
|
||||||
|
struct rseq r = {};
|
||||||
|
r.cpu_id = kRseqCPUIDUninitialized;
|
||||||
|
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, 0); sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__atomic_load_n(&r.cpu_id, __ATOMIC_RELAXED) < 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (__atomic_load_n(&r.cpu_id_start, __ATOMIC_RELAXED) < 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Critical section is eventually aborted.
|
||||||
|
int TestAbort() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq_cs cs = {};
|
||||||
|
cs.version = 0;
|
||||||
|
cs.flags = 0;
|
||||||
|
cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) -
|
||||||
|
reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort);
|
||||||
|
|
||||||
|
// Loops until abort. If this returns then abort occurred.
|
||||||
|
rseq_loop(&r, &cs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Abort may be before the critical section.
|
||||||
|
int TestAbortBefore() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq_cs cs = {};
|
||||||
|
cs.version = 0;
|
||||||
|
cs.flags = 0;
|
||||||
|
cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) -
|
||||||
|
reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_early_abort);
|
||||||
|
|
||||||
|
// Loops until abort. If this returns then abort occurred.
|
||||||
|
rseq_loop(&r, &cs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signature must match.
|
||||||
|
int TestAbortSignature() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq_cs cs = {};
|
||||||
|
cs.version = 0;
|
||||||
|
cs.flags = 0;
|
||||||
|
cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) -
|
||||||
|
reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort);
|
||||||
|
|
||||||
|
// Loops until abort. This should SIGSEGV on abort.
|
||||||
|
rseq_loop(&r, &cs);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Abort must not be in the critical section.
|
||||||
|
int TestAbortPreCommit() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq_cs cs = {};
|
||||||
|
cs.version = 0;
|
||||||
|
cs.flags = 0;
|
||||||
|
cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) -
|
||||||
|
reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_pre_commit);
|
||||||
|
|
||||||
|
// Loops until abort. This should SIGSEGV on abort.
|
||||||
|
rseq_loop(&r, &cs);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// rseq.rseq_cs is cleared on abort.
|
||||||
|
int TestAbortClearsCS() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq_cs cs = {};
|
||||||
|
cs.version = 0;
|
||||||
|
cs.flags = 0;
|
||||||
|
cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) -
|
||||||
|
reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort);
|
||||||
|
|
||||||
|
// Loops until abort. If this returns then abort occurred.
|
||||||
|
rseq_loop(&r, &cs);
|
||||||
|
|
||||||
|
if (__atomic_load_n(&r.rseq_cs, __ATOMIC_RELAXED)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// rseq.rseq_cs is cleared on abort outside of critical section.
|
||||||
|
int TestInvalidAbortClearsCS() {
|
||||||
|
struct rseq r = {};
|
||||||
|
if (int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
||||||
|
sys_errno(ret) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rseq_cs cs = {};
|
||||||
|
cs.version = 0;
|
||||||
|
cs.flags = 0;
|
||||||
|
cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) -
|
||||||
|
reinterpret_cast<uint64_t>(&rseq_loop_start);
|
||||||
|
cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort);
|
||||||
|
|
||||||
|
__atomic_store_n(&r.rseq_cs, &cs, __ATOMIC_RELAXED);
|
||||||
|
|
||||||
|
// When the next abort condition occurs, the kernel will clear cs once it
|
||||||
|
// determines we aren't in the critical section.
|
||||||
|
while (1) {
|
||||||
|
if (!__atomic_load_n(&r.rseq_cs, __ATOMIC_RELAXED)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exit codes:
|
||||||
|
// 0 - Pass
|
||||||
|
// 1 - Fail
|
||||||
|
// 2 - Missing argument
|
||||||
|
// 3 - Unknown test case
|
||||||
|
extern "C" int main(int argc, char** argv, char** envp) {
|
||||||
|
if (argc != 2) {
|
||||||
|
// Usage: rseq <test case>
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(argv[1], kRseqTestUnaligned) == 0) {
|
||||||
|
return TestUnaligned();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestRegister) == 0) {
|
||||||
|
return TestRegister();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestDoubleRegister) == 0) {
|
||||||
|
return TestDoubleRegister();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestRegisterUnregister) == 0) {
|
||||||
|
return TestRegisterUnregister();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestUnregisterDifferentPtr) == 0) {
|
||||||
|
return TestUnregisterDifferentPtr();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestUnregisterDifferentSignature) == 0) {
|
||||||
|
return TestUnregisterDifferentSignature();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestCPU) == 0) {
|
||||||
|
return TestCPU();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestAbort) == 0) {
|
||||||
|
return TestAbort();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestAbortBefore) == 0) {
|
||||||
|
return TestAbortBefore();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestAbortSignature) == 0) {
|
||||||
|
return TestAbortSignature();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestAbortPreCommit) == 0) {
|
||||||
|
return TestAbortPreCommit();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestAbortClearsCS) == 0) {
|
||||||
|
return TestAbortClearsCS();
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], kRseqTestInvalidAbortClearsCS) == 0) {
|
||||||
|
return TestInvalidAbortClearsCS();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace gvisor
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
|
||||||
|
.text
|
||||||
|
.align 4
|
||||||
|
.type _start,@function
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
movq %rsp,%rdi
|
||||||
|
call __init
|
||||||
|
hlt
|
||||||
|
|
||||||
|
.size _start,.-_start
|
||||||
|
.section .note.GNU-stack,"",@progbits
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl raw_syscall
|
||||||
|
.type raw_syscall, @function
|
||||||
|
|
||||||
|
raw_syscall:
|
||||||
|
mov %rdi,%rax // syscall #
|
||||||
|
mov %rsi,%rdi // arg0
|
||||||
|
mov %rdx,%rsi // arg1
|
||||||
|
mov %rcx,%rdx // arg2
|
||||||
|
mov %r8,%r10 // arg3 (goes in r10 instead of rcx for system calls)
|
||||||
|
mov %r9,%r8 // arg4
|
||||||
|
mov 0x8(%rsp),%r9 // arg5
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
.size raw_syscall,.-raw_syscall
|
||||||
|
.section .note.GNU-stack,"",@progbits
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
#ifndef GVISOR_TEST_SYSCALLS_LINUX_RSEQ_SYSCALLS_H_
|
||||||
|
#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_SYSCALLS_H_
|
||||||
|
|
||||||
|
#include "test/syscalls/linux/rseq/types.h"
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
// Syscall numbers.
|
||||||
|
constexpr int kGetpid = 39;
|
||||||
|
constexpr int kExitGroup = 231;
|
||||||
|
#else
|
||||||
|
#error "Unknown architecture"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gvisor {
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// Standalone system call interfaces.
|
||||||
|
// Note that these are all "raw" system call interfaces which encode
|
||||||
|
// errors by setting the return value to a small negative number.
|
||||||
|
// Use sys_errno() to check system call return values for errors.
|
||||||
|
|
||||||
|
// Maximum Linux error number.
|
||||||
|
constexpr int kMaxErrno = 4095;
|
||||||
|
|
||||||
|
// Errno values.
|
||||||
|
#define EPERM 1
|
||||||
|
#define EFAULT 14
|
||||||
|
#define EBUSY 16
|
||||||
|
#define EINVAL 22
|
||||||
|
|
||||||
|
// Get the error number from a raw system call return value.
|
||||||
|
// Returns a positive error number or 0 if there was no error.
|
||||||
|
static inline int sys_errno(uintptr_t rval) {
|
||||||
|
if (rval >= static_cast<uintptr_t>(-kMaxErrno)) {
|
||||||
|
return -static_cast<int>(rval);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" uintptr_t raw_syscall(int number, ...);
|
||||||
|
|
||||||
|
static inline void sys_exit_group(int status) {
|
||||||
|
raw_syscall(kExitGroup, status);
|
||||||
|
}
|
||||||
|
static inline int sys_getpid() {
|
||||||
|
return static_cast<int>(raw_syscall(kGetpid));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace gvisor
|
||||||
|
|
||||||
|
#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_SYSCALLS_H_
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
#ifndef GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TEST_H_
|
||||||
|
#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TEST_H_
|
||||||
|
|
||||||
|
namespace gvisor {
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// Test cases supported by rseq binary.
|
||||||
|
|
||||||
|
inline constexpr char kRseqTestUnaligned[] = "unaligned";
|
||||||
|
inline constexpr char kRseqTestRegister[] = "register";
|
||||||
|
inline constexpr char kRseqTestDoubleRegister[] = "double-register";
|
||||||
|
inline constexpr char kRseqTestRegisterUnregister[] = "register-unregister";
|
||||||
|
inline constexpr char kRseqTestUnregisterDifferentPtr[] =
|
||||||
|
"unregister-different-ptr";
|
||||||
|
inline constexpr char kRseqTestUnregisterDifferentSignature[] =
|
||||||
|
"unregister-different-signature";
|
||||||
|
inline constexpr char kRseqTestCPU[] = "cpu";
|
||||||
|
inline constexpr char kRseqTestAbort[] = "abort";
|
||||||
|
inline constexpr char kRseqTestAbortBefore[] = "abort-before";
|
||||||
|
inline constexpr char kRseqTestAbortSignature[] = "abort-signature";
|
||||||
|
inline constexpr char kRseqTestAbortPreCommit[] = "abort-precommit";
|
||||||
|
inline constexpr char kRseqTestAbortClearsCS[] = "abort-clears-cs";
|
||||||
|
inline constexpr char kRseqTestInvalidAbortClearsCS[] =
|
||||||
|
"invalid-abort-clears-cs";
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace gvisor
|
||||||
|
|
||||||
|
#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TEST_H_
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
#ifndef GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TYPES_H_
|
||||||
|
#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TYPES_H_
|
||||||
|
|
||||||
|
using size_t = __SIZE_TYPE__;
|
||||||
|
using uintptr_t = __UINTPTR_TYPE__;
|
||||||
|
|
||||||
|
using uint8_t = __UINT8_TYPE__;
|
||||||
|
using uint16_t = __UINT16_TYPE__;
|
||||||
|
using uint32_t = __UINT32_TYPE__;
|
||||||
|
using uint64_t = __UINT64_TYPE__;
|
||||||
|
|
||||||
|
using int8_t = __INT8_TYPE__;
|
||||||
|
using int16_t = __INT16_TYPE__;
|
||||||
|
using int32_t = __INT32_TYPE__;
|
||||||
|
using int64_t = __INT64_TYPE__;
|
||||||
|
|
||||||
|
#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TYPES_H_
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
#ifndef GVISOR_TEST_SYSCALLS_LINUX_RSEQ_UAPI_H_
|
||||||
|
#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_UAPI_H_
|
||||||
|
|
||||||
|
// User-kernel ABI for restartable sequences.
|
||||||
|
|
||||||
|
// Standard types.
|
||||||
|
//
|
||||||
|
// N.B. This header will be included in targets that do have the standard
|
||||||
|
// library, so we can't shadow the standard type names.
|
||||||
|
using __u32 = __UINT32_TYPE__;
|
||||||
|
using __u64 = __UINT64_TYPE__;
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
// Syscall numbers.
|
||||||
|
constexpr int kRseqSyscall = 334;
|
||||||
|
#else
|
||||||
|
#error "Unknown architecture"
|
||||||
|
#endif // __x86_64__
|
||||||
|
|
||||||
|
struct rseq_cs {
|
||||||
|
__u32 version;
|
||||||
|
__u32 flags;
|
||||||
|
__u64 start_ip;
|
||||||
|
__u64 post_commit_offset;
|
||||||
|
__u64 abort_ip;
|
||||||
|
} __attribute__((aligned(4 * sizeof(__u64))));
|
||||||
|
|
||||||
|
// N.B. alignment is enforced by the kernel.
|
||||||
|
struct rseq {
|
||||||
|
__u32 cpu_id_start;
|
||||||
|
__u32 cpu_id;
|
||||||
|
struct rseq_cs* rseq_cs;
|
||||||
|
__u32 flags;
|
||||||
|
} __attribute__((aligned(4 * sizeof(__u64))));
|
||||||
|
|
||||||
|
constexpr int kRseqFlagUnregister = 1 << 0;
|
||||||
|
|
||||||
|
constexpr int kRseqCPUIDUninitialized = -1;
|
||||||
|
|
||||||
|
#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_UAPI_H_
|
Loading…
Reference in New Issue