gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/rseq/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 "test/syscalls/linux/rseq/critical.h" 16 #include "test/syscalls/linux/rseq/syscalls.h" 17 #include "test/syscalls/linux/rseq/test.h" 18 #include "test/syscalls/linux/rseq/types.h" 19 #include "test/syscalls/linux/rseq/uapi.h" 20 21 namespace gvisor { 22 namespace testing { 23 24 extern "C" int main(int argc, char** argv, char** envp); 25 26 // Standalone initialization before calling main(). 27 extern "C" void __init(uintptr_t* sp) { 28 int argc = sp[0]; 29 char** argv = reinterpret_cast<char**>(&sp[1]); 30 char** envp = &argv[argc + 1]; 31 32 // Call main() and exit. 33 sys_exit_group(main(argc, argv, envp)); 34 35 // sys_exit_group does not return 36 } 37 38 int strcmp(const char* s1, const char* s2) { 39 const unsigned char* p1 = reinterpret_cast<const unsigned char*>(s1); 40 const unsigned char* p2 = reinterpret_cast<const unsigned char*>(s2); 41 42 while (*p1 == *p2) { 43 if (!*p1) { 44 return 0; 45 } 46 ++p1; 47 ++p2; 48 } 49 return static_cast<int>(*p1) - static_cast<int>(*p2); 50 } 51 52 int sys_rseq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) { 53 return raw_syscall(kRseqSyscall, rseq, rseq_len, flags, sig); 54 } 55 56 // Test that rseq must be aligned. 57 int TestUnaligned() { 58 constexpr uintptr_t kRequiredAlignment = alignof(rseq); 59 60 char buf[2 * kRequiredAlignment] = {}; 61 uintptr_t ptr = reinterpret_cast<uintptr_t>(&buf[0]); 62 if ((ptr & (kRequiredAlignment - 1)) == 0) { 63 // buf is already aligned. Misalign it. 64 ptr++; 65 } 66 67 int ret = sys_rseq(reinterpret_cast<rseq*>(ptr), sizeof(rseq), 0, 0); 68 if (sys_errno(ret) != EINVAL) { 69 return 1; 70 } 71 return 0; 72 } 73 74 // Sanity test that registration works. 75 int TestRegister() { 76 struct rseq r = {}; 77 int ret = sys_rseq(&r, sizeof(r), 0, 0); 78 if (sys_errno(ret) != 0) { 79 return 1; 80 } 81 return 0; 82 } 83 84 // Registration can't be done twice. 85 int TestDoubleRegister() { 86 struct rseq r = {}; 87 int ret = sys_rseq(&r, sizeof(r), 0, 0); 88 if (sys_errno(ret) != 0) { 89 return 1; 90 } 91 92 ret = sys_rseq(&r, sizeof(r), 0, 0); 93 if (sys_errno(ret) != EBUSY) { 94 return 1; 95 } 96 97 return 0; 98 } 99 100 // Registration can be done again after unregister. 101 int TestRegisterUnregister() { 102 struct rseq r = {}; 103 104 int ret = sys_rseq(&r, sizeof(r), 0, 0); 105 if (sys_errno(ret) != 0) { 106 return 1; 107 } 108 109 ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, 0); 110 if (sys_errno(ret) != 0) { 111 return 1; 112 } 113 114 ret = sys_rseq(&r, sizeof(r), 0, 0); 115 if (sys_errno(ret) != 0) { 116 return 1; 117 } 118 119 return 0; 120 } 121 122 // The pointer to rseq must match on register/unregister. 123 int TestUnregisterDifferentPtr() { 124 struct rseq r = {}; 125 126 int ret = sys_rseq(&r, sizeof(r), 0, 0); 127 if (sys_errno(ret) != 0) { 128 return 1; 129 } 130 131 struct rseq r2 = {}; 132 133 ret = sys_rseq(&r2, sizeof(r2), kRseqFlagUnregister, 0); 134 if (sys_errno(ret) != EINVAL) { 135 return 1; 136 } 137 138 return 0; 139 } 140 141 // The signature must match on register/unregister. 142 int TestUnregisterDifferentSignature() { 143 constexpr int kSignature = 0; 144 145 struct rseq r = {}; 146 int ret = sys_rseq(&r, sizeof(r), 0, kSignature); 147 if (sys_errno(ret) != 0) { 148 return 1; 149 } 150 151 ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, kSignature + 1); 152 if (sys_errno(ret) != EPERM) { 153 return 1; 154 } 155 156 return 0; 157 } 158 159 // The CPU ID is initialized. 160 int TestCPU() { 161 struct rseq r = {}; 162 r.cpu_id = kRseqCPUIDUninitialized; 163 164 int ret = sys_rseq(&r, sizeof(r), 0, 0); 165 if (sys_errno(ret) != 0) { 166 return 1; 167 } 168 169 if (__atomic_load_n(&r.cpu_id, __ATOMIC_RELAXED) < 0) { 170 return 1; 171 } 172 if (__atomic_load_n(&r.cpu_id_start, __ATOMIC_RELAXED) < 0) { 173 return 1; 174 } 175 176 return 0; 177 } 178 179 // Critical section is eventually aborted. 180 int TestAbort() { 181 struct rseq r = {}; 182 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); 183 if (sys_errno(ret) != 0) { 184 return 1; 185 } 186 187 struct rseq_cs cs = {}; 188 cs.version = 0; 189 cs.flags = 0; 190 cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); 191 cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - 192 reinterpret_cast<uint64_t>(&rseq_loop_start); 193 cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); 194 195 // Loops until abort. If this returns then abort occurred. 196 rseq_loop(&r, &cs); 197 198 return 0; 199 } 200 201 // Abort may be before the critical section. 202 int TestAbortBefore() { 203 struct rseq r = {}; 204 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); 205 if (sys_errno(ret) != 0) { 206 return 1; 207 } 208 209 struct rseq_cs cs = {}; 210 cs.version = 0; 211 cs.flags = 0; 212 cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); 213 cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - 214 reinterpret_cast<uint64_t>(&rseq_loop_start); 215 cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_early_abort); 216 217 // Loops until abort. If this returns then abort occurred. 218 rseq_loop(&r, &cs); 219 220 return 0; 221 } 222 223 // Signature must match. 224 int TestAbortSignature() { 225 struct rseq r = {}; 226 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1); 227 if (sys_errno(ret) != 0) { 228 return 1; 229 } 230 231 struct rseq_cs cs = {}; 232 cs.version = 0; 233 cs.flags = 0; 234 cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); 235 cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - 236 reinterpret_cast<uint64_t>(&rseq_loop_start); 237 cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); 238 239 // Loops until abort. This should SIGSEGV on abort. 240 rseq_loop(&r, &cs); 241 242 return 1; 243 } 244 245 // Abort must not be in the critical section. 246 int TestAbortPreCommit() { 247 struct rseq r = {}; 248 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1); 249 if (sys_errno(ret) != 0) { 250 return 1; 251 } 252 253 struct rseq_cs cs = {}; 254 cs.version = 0; 255 cs.flags = 0; 256 cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); 257 cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - 258 reinterpret_cast<uint64_t>(&rseq_loop_start); 259 cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_pre_commit); 260 261 // Loops until abort. This should SIGSEGV on abort. 262 rseq_loop(&r, &cs); 263 264 return 1; 265 } 266 267 // rseq.rseq_cs is cleared on abort. 268 int TestAbortClearsCS() { 269 struct rseq r = {}; 270 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); 271 if (sys_errno(ret) != 0) { 272 return 1; 273 } 274 275 struct rseq_cs cs = {}; 276 cs.version = 0; 277 cs.flags = 0; 278 cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); 279 cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - 280 reinterpret_cast<uint64_t>(&rseq_loop_start); 281 cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); 282 283 // Loops until abort. If this returns then abort occurred. 284 rseq_loop(&r, &cs); 285 286 if (__atomic_load_n(&r.rseq_cs, __ATOMIC_RELAXED)) { 287 return 1; 288 } 289 290 return 0; 291 } 292 293 // rseq.rseq_cs is cleared on abort outside of critical section. 294 int TestInvalidAbortClearsCS() { 295 struct rseq r = {}; 296 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); 297 if (sys_errno(ret) != 0) { 298 return 1; 299 } 300 301 struct rseq_cs cs = {}; 302 cs.version = 0; 303 cs.flags = 0; 304 cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); 305 cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - 306 reinterpret_cast<uint64_t>(&rseq_loop_start); 307 cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); 308 309 __atomic_store_n(&r.rseq_cs, &cs, __ATOMIC_RELAXED); 310 311 // When the next abort condition occurs, the kernel will clear cs once it 312 // determines we aren't in the critical section. 313 while (1) { 314 if (!__atomic_load_n(&r.rseq_cs, __ATOMIC_RELAXED)) { 315 break; 316 } 317 } 318 319 return 0; 320 } 321 322 // rseq.cpu_id_start is overwritten by RSEQ fence. 323 int TestMembarrierResetsCpuIdStart() { 324 struct rseq r = {}; 325 int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); 326 if (sys_errno(ret) != 0) { 327 return 1; 328 } 329 ret = sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0); 330 if (sys_errno(ret) != 0) { 331 return 1; 332 } 333 constexpr size_t kStackSize = 2 << 20; // 2 MB 334 uintptr_t child_stack_start = 335 sys_mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, 336 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 337 if (sys_errno(child_stack_start) != 0) { 338 return 1; 339 } 340 uintptr_t child_stack_end = child_stack_start + kStackSize; 341 342 // Set cpu_id_start to a negative value. 343 __atomic_store_n(&r.cpu_id_start, 0xffffffff, __ATOMIC_RELAXED); 344 345 // Start a thread to invoke the RSEQ fence. 346 uint32_t child_cleartid = 0; 347 bool fenced = false; 348 auto tid = clone( 349 +[](void* arg) { 350 int ret = sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, 0); 351 if (sys_errno(ret) != 0) { 352 sys_exit_group(1); 353 } 354 __atomic_store_n(static_cast<bool*>(arg), true, __ATOMIC_RELAXED); 355 return 0; 356 }, 357 child_stack_end, 358 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | 359 CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID, 360 &fenced, &child_cleartid); 361 if (sys_errno(tid) != 0) { 362 return 1; 363 } 364 365 // Expect that cpu_id_start will be overwritten with a real CPU number when 366 // the thread invokes the RSEQ fence. 367 while (true) { 368 if (__atomic_load_n(&fenced, __ATOMIC_ACQUIRE)) { 369 if (static_cast<int32_t>( 370 __atomic_load_n(&r.cpu_id_start, __ATOMIC_RELAXED)) < 0) { 371 return 1; 372 } 373 break; 374 } 375 } 376 377 // Wait for the thread to exit. 378 while (true) { 379 uint32_t cur_child_cleartid = 380 __atomic_load_n(&child_cleartid, __ATOMIC_RELAXED); 381 if (cur_child_cleartid == 0) { 382 break; 383 } 384 auto ret = 385 sys_futex(&child_cleartid, FUTEX_WAIT, cur_child_cleartid, nullptr); 386 if (ret != 0 && sys_errno(ret) != EAGAIN && sys_errno(ret) != EINTR) { 387 return 1; 388 } 389 } 390 391 return 0; 392 } 393 394 // Exit codes: 395 // 0 - Pass 396 // 1 - Fail 397 // 2 - Missing argument 398 // 3 - Unknown test case 399 extern "C" int main(int argc, char** argv, char** envp) { 400 if (argc != 2) { 401 // Usage: rseq <test case> 402 return 2; 403 } 404 405 if (strcmp(argv[1], kRseqTestUnaligned) == 0) { 406 return TestUnaligned(); 407 } 408 if (strcmp(argv[1], kRseqTestRegister) == 0) { 409 return TestRegister(); 410 } 411 if (strcmp(argv[1], kRseqTestDoubleRegister) == 0) { 412 return TestDoubleRegister(); 413 } 414 if (strcmp(argv[1], kRseqTestRegisterUnregister) == 0) { 415 return TestRegisterUnregister(); 416 } 417 if (strcmp(argv[1], kRseqTestUnregisterDifferentPtr) == 0) { 418 return TestUnregisterDifferentPtr(); 419 } 420 if (strcmp(argv[1], kRseqTestUnregisterDifferentSignature) == 0) { 421 return TestUnregisterDifferentSignature(); 422 } 423 if (strcmp(argv[1], kRseqTestCPU) == 0) { 424 return TestCPU(); 425 } 426 if (strcmp(argv[1], kRseqTestAbort) == 0) { 427 return TestAbort(); 428 } 429 if (strcmp(argv[1], kRseqTestAbortBefore) == 0) { 430 return TestAbortBefore(); 431 } 432 if (strcmp(argv[1], kRseqTestAbortSignature) == 0) { 433 return TestAbortSignature(); 434 } 435 if (strcmp(argv[1], kRseqTestAbortPreCommit) == 0) { 436 return TestAbortPreCommit(); 437 } 438 if (strcmp(argv[1], kRseqTestAbortClearsCS) == 0) { 439 return TestAbortClearsCS(); 440 } 441 if (strcmp(argv[1], kRseqTestInvalidAbortClearsCS) == 0) { 442 return TestInvalidAbortClearsCS(); 443 } 444 if (strcmp(argv[1], kRseqTestMembarrierResetsCpuIdStart) == 0) { 445 return TestMembarrierResetsCpuIdStart(); 446 } 447 448 return 3; 449 } 450 451 } // namespace testing 452 } // namespace gvisor