github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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    parent = getpid();
    72    pid_t parent_tid = gettid();
    73  
    74    struct sigaction sa = {};
    75    sigemptyset(&sa.sa_mask);
    76    sa.sa_flags = SA_SIGINFO;
    77    sa.sa_sigaction = sigusr1;
    78    ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds());
    79  
    80    // The amd64 ABI specifies that the XMM register set is caller-saved. This
    81    // implies that if there is any function call between SET_XMM and GET_XMM the
    82    // compiler might save/restore xmm0 implicitly. This defeats the entire
    83    // purpose of the test which is to verify that fpstate is restored by
    84    // sigreturn(2).
    85    //
    86    // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented
    87    // in inline assembly below.
    88    //
    89    // If the OS is broken and registers are clobbered by the child, using tgkill
    90    // to signal the current thread increases the likelihood that this thread will
    91    // be the one clobbered.
    92  
    93    uint64_t expected = 0xdeadbeeffacefeed;
    94    SET_FP0(expected);
    95  
    96  #ifdef __x86_64__
    97    asm volatile(
    98        "movl %[killnr], %%eax;"
    99        "movl %[parent], %%edi;"
   100        "movl %[tid], %%esi;"
   101        "movl %[sig], %%edx;"
   102        "syscall;"
   103        :
   104        : [ killnr ] "i"(__NR_tgkill), [ parent ] "rm"(parent),
   105          [ tid ] "rm"(parent_tid), [ sig ] "i"(SIGUSR1)
   106        : "rax", "rdi", "rsi", "rdx",
   107          // Clobbered by syscall.
   108          "rcx", "r11");
   109  #elif __aarch64__
   110    asm volatile(
   111        "mov x8, %0\n"
   112        "mov x0, %1\n"
   113        "mov x1, %2\n"
   114        "mov x2, %3\n"
   115        "svc #0\n" ::"r"(__NR_tgkill),
   116        "r"(parent), "r"(parent_tid), "r"(SIGUSR1));
   117  #endif
   118  
   119    uint64_t got;
   120    GET_FP0(got);
   121  
   122    if (getpid() == parent) {  // Parent.
   123      int status;
   124      ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child));
   125      EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
   126    }
   127  
   128    // TEST_CHECK_MSG since this may run in the child.
   129    TEST_CHECK_MSG(expected == got, "Bad xmm0 value");
   130  
   131    if (getpid() != parent) {  // Child.
   132      _exit(0);
   133    }
   134  }
   135  
   136  #ifdef __x86_64__
   137  TEST(FPSigTest, ForkWithZeroMxcsr) {
   138    parent = getpid();
   139    pid_t parent_tid = gettid();
   140  
   141    struct sigaction sa = {};
   142    sigemptyset(&sa.sa_mask);
   143    sa.sa_flags = SA_SIGINFO;
   144    sa.sa_sigaction = sigusr1;
   145    ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds());
   146  
   147    // The control bits of the MXCSR register are callee-saved (preserved across
   148    // calls), while the status bits are caller-saved (not preserved).
   149    uint32_t expected = 0, origin;
   150    __asm__("STMXCSR %0" : "=m"(origin));
   151    __asm__("LDMXCSR %0" : : "m"(expected));
   152  
   153    asm volatile(
   154        "movl %[killnr], %%eax;"
   155        "movl %[parent], %%edi;"
   156        "movl %[tid], %%esi;"
   157        "movl %[sig], %%edx;"
   158        "syscall;"
   159        :
   160        : [killnr] "i"(__NR_tgkill), [parent] "rm"(parent),
   161          [tid] "rm"(parent_tid), [sig] "i"(SIGUSR1)
   162        : "rax", "rdi", "rsi", "rdx",
   163          // Clobbered by syscall.
   164          "rcx", "r11");
   165  
   166    uint32_t got;
   167    __asm__("STMXCSR %0" : "=m"(got));
   168    __asm__("LDMXCSR %0" : : "m"(origin));
   169  
   170    if (getpid() == parent) {  // Parent.
   171      int status;
   172      ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child));
   173      EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
   174    }
   175  
   176    // TEST_CHECK_MSG since this may run in the child.
   177    TEST_CHECK_MSG(expected == got, "Bad mxcsr value");
   178  
   179    if (getpid() != parent) {  // Child.
   180      _exit(0);
   181    }
   182  }
   183  #endif
   184  
   185  }  // namespace
   186  
   187  }  // namespace testing
   188  }  // namespace gvisor