gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/fpsig_fork.cc (about)

     1  // Copyright 2018 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  // This test verifies that fork(2) in a signal handler will correctly
    16  // restore floating point state after the signal handler returns in both
    17  // the child and parent.
    18  #include <sys/time.h>
    19  
    20  #include "gtest/gtest.h"
    21  #include "test/util/logging.h"
    22  #include "test/util/test_util.h"
    23  #include "test/util/thread_util.h"
    24  
    25  namespace gvisor {
    26  namespace testing {
    27  
    28  namespace {
    29  
    30  #ifdef __x86_64__
    31  #define GET_XMM(__var, __xmm) \
    32    asm volatile("movq %%" #__xmm ", %0" : "=r"(__var))
    33  #define SET_XMM(__var, __xmm) asm volatile("movq %0, %%" #__xmm : : "r"(__var))
    34  #define GET_FP0(__var) GET_XMM(__var, xmm0)
    35  #define SET_FP0(__var) SET_XMM(__var, xmm0)
    36  #elif __aarch64__
    37  #define __stringify_1(x...) #x
    38  #define __stringify(x...) __stringify_1(x)
    39  #define GET_FPREG(var, regname) \
    40    asm volatile("str " __stringify(regname) ", %0" : "=m"(var))
    41  #define SET_FPREG(var, regname) \
    42    asm volatile("ldr " __stringify(regname) ", %0" : "=m"(var))
    43  #define GET_FP0(var) GET_FPREG(var, d0)
    44  #define SET_FP0(var) SET_FPREG(var, d0)
    45  #endif
    46  
    47  #define DEFAULT_MXCSR 0x1f80
    48  
    49  int parent, child;
    50  
    51  void sigusr1(int s, siginfo_t* siginfo, void* _uc) {
    52    // Fork and clobber %xmm0. The fpstate should be restored by sigreturn(2)
    53    // in both parent and child.
    54    child = fork();
    55    TEST_CHECK_MSG(child >= 0, "fork failed");
    56  
    57    uint64_t val = SIGUSR1;
    58    SET_FP0(val);
    59    uint64_t got;
    60    GET_FP0(got);
    61    TEST_CHECK_MSG(val == got, "Basic FP check failed in sigusr1()");
    62  
    63  #ifdef __x86_64
    64    uint32_t mxcsr;
    65    __asm__("STMXCSR %0" : "=m"(mxcsr));
    66    TEST_CHECK_MSG(mxcsr == DEFAULT_MXCSR, "Unexpected mxcsr");
    67  #endif
    68  }
    69  
    70  TEST(FPSigTest, Fork) {
    71    // NOTE(b/238773725): This test fails on ptrace platform due to a kernel bug.
    72    SKIP_IF(GvisorPlatform() == Platform::kPtrace);
    73    parent = getpid();
    74    pid_t parent_tid = gettid();
    75  
    76    struct sigaction sa = {};
    77    sigemptyset(&sa.sa_mask);
    78    sa.sa_flags = SA_SIGINFO;
    79    sa.sa_sigaction = sigusr1;
    80    ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds());
    81  
    82    // The amd64 ABI specifies that the XMM register set is caller-saved. This
    83    // implies that if there is any function call between SET_XMM and GET_XMM the
    84    // compiler might save/restore xmm0 implicitly. This defeats the entire
    85    // purpose of the test which is to verify that fpstate is restored by
    86    // sigreturn(2).
    87    //
    88    // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented
    89    // in inline assembly below.
    90    //
    91    // If the OS is broken and registers are clobbered by the child, using tgkill
    92    // to signal the current thread increases the likelihood that this thread will
    93    // be the one clobbered.
    94  
    95    uint64_t expected = 0xdeadbeeffacefeed;
    96    SET_FP0(expected);
    97  
    98    int64_t ret;
    99  #ifdef __x86_64__
   100    asm volatile("syscall;"
   101                 : "=a"(ret)
   102                 : "a"(__NR_tgkill), "D"(parent), "S"(parent_tid), "d"(SIGUSR1)
   103                 :  // Clobbered by syscall.
   104                 "rcx", "r11");
   105  #elif __aarch64__
   106    register uint64_t x8 __asm__("x8") = __NR_tgkill;
   107    register uint64_t x0 __asm__("x0") = parent;
   108    register uint64_t x1 __asm__("x1") = parent_tid;
   109    register uint64_t x2 __asm__("x2") = SIGUSR1;
   110    asm volatile("svc #0\n" : "=r"(x0) : "r"(x0), "r"(x1), "r"(x2), "r"(x8) :);
   111    ret = x0;
   112  #endif
   113    EXPECT_EQ(ret, 0);
   114  
   115    uint64_t got;
   116    GET_FP0(got);
   117  
   118    if (getpid() == parent) {  // Parent.
   119      int status;
   120      ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child));
   121      EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
   122    }
   123  
   124    // TEST_CHECK_MSG since this may run in the child.
   125    TEST_CHECK_MSG(expected == got, "Bad xmm0 value");
   126  
   127    if (getpid() != parent) {  // Child.
   128      _exit(0);
   129    }
   130  }
   131  
   132  #ifdef __x86_64__
   133  TEST(FPSigTest, ForkWithZeroMxcsr) {
   134    // NOTE(b/238773725): This test fails on ptrace platform due to a kernel bug.
   135    SKIP_IF(GvisorPlatform() == Platform::kPtrace);
   136    parent = getpid();
   137    pid_t parent_tid = gettid();
   138  
   139    struct sigaction sa = {};
   140    sigemptyset(&sa.sa_mask);
   141    sa.sa_flags = SA_SIGINFO;
   142    sa.sa_sigaction = sigusr1;
   143    ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds());
   144  
   145    // The control bits of the MXCSR register are callee-saved (preserved across
   146    // calls), while the status bits are caller-saved (not preserved).
   147    uint32_t expected = 0, origin;
   148    __asm__("STMXCSR %0" : "=m"(origin));
   149    __asm__("LDMXCSR %0" : : "m"(expected));
   150  
   151    asm volatile(
   152        "movl %[killnr], %%eax;"
   153        "movl %[parent], %%edi;"
   154        "movl %[tid], %%esi;"
   155        "movl %[sig], %%edx;"
   156        "syscall;"
   157        :
   158        : [killnr] "i"(__NR_tgkill), [parent] "rm"(parent),
   159          [tid] "rm"(parent_tid), [sig] "i"(SIGUSR1)
   160        : "rax", "rdi", "rsi", "rdx",
   161          // Clobbered by syscall.
   162          "rcx", "r11");
   163  
   164    uint32_t got;
   165    __asm__("STMXCSR %0" : "=m"(got));
   166    __asm__("LDMXCSR %0" : : "m"(origin));
   167  
   168    if (getpid() == parent) {  // Parent.
   169      int status;
   170      ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child));
   171      EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
   172    }
   173  
   174    // TEST_CHECK_MSG since this may run in the child.
   175    TEST_CHECK_MSG(expected == got, "Bad mxcsr value");
   176  
   177    if (getpid() != parent) {  // Child.
   178      _exit(0);
   179    }
   180  }
   181  #endif
   182  
   183  }  // namespace
   184  
   185  }  // namespace testing
   186  }  // namespace gvisor