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