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