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