gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/rseq.cc (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <errno.h> 16 #include <linux/membarrier.h> 17 #include <signal.h> 18 #include <sys/syscall.h> 19 #include <sys/types.h> 20 #include <sys/wait.h> 21 #include <unistd.h> 22 23 #include "gtest/gtest.h" 24 #include "test/syscalls/linux/rseq/test.h" 25 #include "test/syscalls/linux/rseq/uapi.h" 26 #include "test/util/logging.h" 27 #include "test/util/multiprocess_util.h" 28 #include "test/util/posix_error.h" 29 #include "test/util/test_util.h" 30 31 namespace gvisor { 32 namespace testing { 33 34 namespace { 35 36 using ::testing::AnyOf; 37 using ::testing::Eq; 38 39 // Syscall test for rseq (restartable sequences). 40 // 41 // We must be very careful about how these tests are written. Each thread may 42 // only have one struct rseq registration, which may be done automatically at 43 // thread start (as of 2019-11-13, glibc does *not* support rseq and thus does 44 // not do so, but other libraries do). 45 // 46 // Testing of rseq is thus done primarily in a child process with no 47 // registration. This means exec'ing a nostdlib binary, as rseq registration can 48 // only be cleared by execve (or knowing the old rseq address), and glibc (based 49 // on the current unmerged patches) register rseq before calling main()). 50 51 int RSeq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) { 52 return syscall(kRseqSyscall, rseq, rseq_len, flags, sig); 53 } 54 55 // Returns true if this kernel supports the rseq syscall. 56 PosixErrorOr<bool> RSeqSupported() { 57 // We have to be careful here, there are three possible cases: 58 // 59 // 1. rseq is not supported -> ENOSYS 60 // 2. rseq is supported and not registered -> success, but we should 61 // unregister. 62 // 3. rseq is supported and registered -> EINVAL (most likely). 63 64 // The only validation done on new registrations is that rseq is aligned and 65 // writable. 66 rseq rseq = {}; 67 int ret = RSeq(&rseq, sizeof(rseq), 0, 0); 68 if (ret == 0) { 69 // Successfully registered, rseq is supported. Unregister. 70 ret = RSeq(&rseq, sizeof(rseq), kRseqFlagUnregister, 0); 71 if (ret != 0) { 72 return PosixError(errno); 73 } 74 return true; 75 } 76 77 switch (errno) { 78 case ENOSYS: 79 // Not supported. 80 return false; 81 case EINVAL: 82 // Supported, but already registered. EINVAL returned because we provided 83 // a different address. 84 return true; 85 default: 86 // Unknown error. 87 return PosixError(errno); 88 } 89 } 90 91 constexpr char kRseqBinary[] = "test/syscalls/linux/rseq/rseq"; 92 93 void RunChildTest(std::string test_case, int want_status) { 94 std::string path = RunfilePath(kRseqBinary); 95 96 pid_t child_pid = -1; 97 int execve_errno = 0; 98 auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( 99 ForkAndExec(path, {path, test_case}, {}, &child_pid, &execve_errno)); 100 101 ASSERT_GT(child_pid, 0); 102 ASSERT_EQ(execve_errno, 0); 103 104 int status = 0; 105 ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); 106 ASSERT_THAT(status, AnyOf(Eq(want_status), Eq(128 + want_status))); 107 } 108 109 // Test that rseq must be aligned. 110 TEST(RseqTest, Unaligned) { 111 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 112 113 RunChildTest(kRseqTestUnaligned, 0); 114 } 115 116 // Sanity test that registration works. 117 TEST(RseqTest, Register) { 118 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 119 120 RunChildTest(kRseqTestRegister, 0); 121 } 122 123 // Registration can't be done twice. 124 TEST(RseqTest, DoubleRegister) { 125 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 126 127 RunChildTest(kRseqTestDoubleRegister, 0); 128 } 129 130 // Registration can be done again after unregister. 131 TEST(RseqTest, RegisterUnregister) { 132 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 133 134 RunChildTest(kRseqTestRegisterUnregister, 0); 135 } 136 137 // The pointer to rseq must match on register/unregister. 138 TEST(RseqTest, UnregisterDifferentPtr) { 139 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 140 141 RunChildTest(kRseqTestUnregisterDifferentPtr, 0); 142 } 143 144 // The signature must match on register/unregister. 145 TEST(RseqTest, UnregisterDifferentSignature) { 146 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 147 148 RunChildTest(kRseqTestUnregisterDifferentSignature, 0); 149 } 150 151 // The CPU ID is initialized. 152 TEST(RseqTest, CPU) { 153 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 154 155 RunChildTest(kRseqTestCPU, 0); 156 } 157 158 // Critical section is eventually aborted. 159 TEST(RseqTest, Abort) { 160 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 161 162 RunChildTest(kRseqTestAbort, 0); 163 } 164 165 // Abort may be before the critical section. 166 TEST(RseqTest, AbortBefore) { 167 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 168 169 RunChildTest(kRseqTestAbortBefore, 0); 170 } 171 172 // Signature must match. 173 TEST(RseqTest, AbortSignature) { 174 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 175 176 RunChildTest(kRseqTestAbortSignature, SIGSEGV); 177 } 178 179 // Abort must not be in the critical section. 180 TEST(RseqTest, AbortPreCommit) { 181 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 182 183 RunChildTest(kRseqTestAbortPreCommit, SIGSEGV); 184 } 185 186 // rseq.rseq_cs is cleared on abort. 187 TEST(RseqTest, AbortClearsCS) { 188 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 189 190 RunChildTest(kRseqTestAbortClearsCS, 0); 191 } 192 193 // rseq.rseq_cs is cleared on abort outside of critical section. 194 TEST(RseqTest, InvalidAbortClearsCS) { 195 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 196 197 RunChildTest(kRseqTestInvalidAbortClearsCS, 0); 198 } 199 200 // NOTE(b/326665974): Needs implementation of clone() in rseq/start_arm64.S. 201 #if defined(__x86_64__) 202 TEST(RseqTest, MembarrierResetsCpuIdStart) { 203 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); 204 int cmds = syscall(SYS_membarrier, MEMBARRIER_CMD_QUERY, 0); 205 SKIP_IF(cmds < 0); 206 SKIP_IF((cmds & MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ) == 0); 207 208 RunChildTest(kRseqTestMembarrierResetsCpuIdStart, 0); 209 } 210 #endif 211 212 } // namespace 213 214 } // namespace testing 215 } // namespace gvisor