2019-12-09 19:21:01 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2020-01-22 00:16:51 +00:00
|
|
|
int sys_rseq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) {
|
2019-12-09 19:21:01 +00:00
|
|
|
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 = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Registration can't be done twice.
|
|
|
|
int TestDoubleRegister() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-08-19 21:44:42 +00:00
|
|
|
ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != EBUSY) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Registration can be done again after unregister.
|
|
|
|
int TestRegisterUnregister() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
|
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-08-19 21:44:42 +00:00
|
|
|
ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-08-19 21:44:42 +00:00
|
|
|
ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// The pointer to rseq must match on register/unregister.
|
|
|
|
int TestUnregisterDifferentPtr() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
|
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq r2 = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
|
|
|
|
ret = sys_rseq(&r2, sizeof(r2), kRseqFlagUnregister, 0);
|
|
|
|
if (sys_errno(ret) != EINVAL) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// The signature must match on register/unregister.
|
|
|
|
int TestUnregisterDifferentSignature() {
|
|
|
|
constexpr int kSignature = 0;
|
|
|
|
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kSignature);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-08-19 21:44:42 +00:00
|
|
|
ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, kSignature + 1);
|
|
|
|
if (sys_errno(ret) != EPERM) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// The CPU ID is initialized.
|
|
|
|
int TestCPU() {
|
|
|
|
struct rseq r = {};
|
|
|
|
r.cpu_id = kRseqCPUIDUninitialized;
|
|
|
|
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, 0);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
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;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Critical section is eventually aborted.
|
|
|
|
int TestAbort() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq_cs cs = {};
|
|
|
|
cs.version = 0;
|
|
|
|
cs.flags = 0;
|
2020-01-22 00:16:51 +00:00
|
|
|
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);
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Loops until abort. If this returns then abort occurred.
|
|
|
|
rseq_loop(&r, &cs);
|
|
|
|
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Abort may be before the critical section.
|
|
|
|
int TestAbortBefore() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq_cs cs = {};
|
|
|
|
cs.version = 0;
|
|
|
|
cs.flags = 0;
|
2020-01-22 00:16:51 +00:00
|
|
|
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);
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Loops until abort. If this returns then abort occurred.
|
|
|
|
rseq_loop(&r, &cs);
|
|
|
|
|
|
|
|
return 0;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Signature must match.
|
|
|
|
int TestAbortSignature() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq_cs cs = {};
|
|
|
|
cs.version = 0;
|
|
|
|
cs.flags = 0;
|
2020-01-22 00:16:51 +00:00
|
|
|
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);
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Loops until abort. This should SIGSEGV on abort.
|
|
|
|
rseq_loop(&r, &cs);
|
|
|
|
|
|
|
|
return 1;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Abort must not be in the critical section.
|
|
|
|
int TestAbortPreCommit() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq_cs cs = {};
|
|
|
|
cs.version = 0;
|
|
|
|
cs.flags = 0;
|
2020-01-22 00:16:51 +00:00
|
|
|
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);
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// Loops until abort. This should SIGSEGV on abort.
|
|
|
|
rseq_loop(&r, &cs);
|
|
|
|
|
|
|
|
return 1;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// rseq.rseq_cs is cleared on abort.
|
|
|
|
int TestAbortClearsCS() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq_cs cs = {};
|
|
|
|
cs.version = 0;
|
|
|
|
cs.flags = 0;
|
2020-01-22 00:16:51 +00:00
|
|
|
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);
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// 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;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// rseq.rseq_cs is cleared on abort outside of critical section.
|
|
|
|
int TestInvalidAbortClearsCS() {
|
|
|
|
struct rseq r = {};
|
2020-08-19 21:44:42 +00:00
|
|
|
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
|
|
|
|
if (sys_errno(ret) != 0) {
|
2019-12-09 19:21:01 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rseq_cs cs = {};
|
|
|
|
cs.version = 0;
|
|
|
|
cs.flags = 0;
|
2020-01-22 00:16:51 +00:00
|
|
|
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);
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
__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;
|
2020-08-19 21:44:42 +00:00
|
|
|
}
|
2019-12-09 19:21:01 +00:00
|
|
|
|
|
|
|
// 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
|