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