github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/fpsig_nested.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 program verifies that application floating point state is restored 16 // correctly after a signal handler returns. It also verifies that this works 17 // with nested signals. 18 #include <sys/time.h> 19 20 #include "gtest/gtest.h" 21 #include "test/util/test_util.h" 22 #include "test/util/thread_util.h" 23 24 namespace gvisor { 25 namespace testing { 26 27 namespace { 28 29 #ifdef __x86_64__ 30 #define GET_XMM(__var, __xmm) \ 31 asm volatile("movq %%" #__xmm ", %0" : "=r"(__var)) 32 #define SET_XMM(__var, __xmm) asm volatile("movq %0, %%" #__xmm : : "r"(__var)) 33 #define GET_FP0(__var) GET_XMM(__var, xmm0) 34 #define SET_FP0(__var) SET_XMM(__var, xmm0) 35 #elif __aarch64__ 36 #define __stringify_1(x...) #x 37 #define __stringify(x...) __stringify_1(x) 38 #define GET_FPREG(var, regname) \ 39 asm volatile("str " __stringify(regname) ", %0" : "=m"(var)) 40 #define SET_FPREG(var, regname) \ 41 asm volatile("ldr " __stringify(regname) ", %0" : "=m"(var)) 42 #define GET_FP0(var) GET_FPREG(var, d0) 43 #define SET_FP0(var) SET_FPREG(var, d0) 44 #endif 45 46 int pid; 47 int tid; 48 49 volatile uint64_t entryxmm[2] = {~0UL, ~0UL}; 50 volatile uint64_t exitxmm[2]; 51 52 void sigusr2(int s, siginfo_t* siginfo, void* _uc) { 53 uint64_t val = SIGUSR2; 54 55 // Record the value of %xmm0 on entry and then clobber it. 56 GET_FP0(entryxmm[1]); 57 SET_FP0(val); 58 GET_FP0(exitxmm[1]); 59 } 60 61 void sigusr1(int s, siginfo_t* siginfo, void* _uc) { 62 uint64_t val = SIGUSR1; 63 64 // Record the value of %xmm0 on entry and then clobber it. 65 GET_FP0(entryxmm[0]); 66 SET_FP0(val); 67 68 // Send a SIGUSR2 to ourself. The signal mask is configured such that 69 // the SIGUSR2 handler will run before this handler returns. 70 #ifdef __x86_64__ 71 asm volatile( 72 "movl %[killnr], %%eax;" 73 "movl %[pid], %%edi;" 74 "movl %[tid], %%esi;" 75 "movl %[sig], %%edx;" 76 "syscall;" 77 : 78 : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid), 79 [ sig ] "i"(SIGUSR2) 80 : "rax", "rdi", "rsi", "rdx", 81 // Clobbered by syscall. 82 "rcx", "r11"); 83 #elif __aarch64__ 84 asm volatile( 85 "mov x8, %0\n" 86 "mov x0, %1\n" 87 "mov x1, %2\n" 88 "mov x2, %3\n" 89 "svc #0\n" ::"r"(__NR_tgkill), 90 "r"(pid), "r"(tid), "r"(SIGUSR2)); 91 #endif 92 93 // Record value of %xmm0 again to verify that the nested signal handler 94 // does not clobber it. 95 GET_FP0(exitxmm[0]); 96 } 97 98 TEST(FPSigTest, NestedSignals) { 99 pid = getpid(); 100 tid = gettid(); 101 102 struct sigaction sa = {}; 103 sigemptyset(&sa.sa_mask); 104 sa.sa_flags = SA_SIGINFO; 105 sa.sa_sigaction = sigusr1; 106 ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); 107 108 sa.sa_sigaction = sigusr2; 109 ASSERT_THAT(sigaction(SIGUSR2, &sa, nullptr), SyscallSucceeds()); 110 111 // The amd64 ABI specifies that the XMM register set is caller-saved. This 112 // implies that if there is any function call between SET_XMM and GET_XMM the 113 // compiler might save/restore xmm0 implicitly. This defeats the entire 114 // purpose of the test which is to verify that fpstate is restored by 115 // sigreturn(2). 116 // 117 // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented 118 // in inline assembly below. 119 // 120 // If the OS is broken and registers are clobbered by the signal, using tgkill 121 // to signal the current thread ensures that this is the clobbered thread. 122 123 uint64_t expected = 0xdeadbeeffacefeed; 124 SET_FP0(expected); 125 126 #ifdef __x86_64__ 127 asm volatile( 128 "movl %[killnr], %%eax;" 129 "movl %[pid], %%edi;" 130 "movl %[tid], %%esi;" 131 "movl %[sig], %%edx;" 132 "syscall;" 133 : 134 : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid), 135 [ sig ] "i"(SIGUSR1) 136 : "rax", "rdi", "rsi", "rdx", 137 // Clobbered by syscall. 138 "rcx", "r11"); 139 #elif __aarch64__ 140 asm volatile( 141 "mov x8, %0\n" 142 "mov x0, %1\n" 143 "mov x1, %2\n" 144 "mov x2, %3\n" 145 "svc #0\n" ::"r"(__NR_tgkill), 146 "r"(pid), "r"(tid), "r"(SIGUSR1)); 147 #endif 148 149 uint64_t got; 150 GET_FP0(got); 151 152 // 153 // The checks below verifies the following: 154 // - signal handlers must called with a clean fpu state. 155 // - sigreturn(2) must restore fpstate of the interrupted context. 156 // 157 EXPECT_EQ(expected, got); 158 EXPECT_EQ(entryxmm[0], 0); 159 EXPECT_EQ(entryxmm[1], 0); 160 EXPECT_EQ(exitxmm[0], SIGUSR1); 161 EXPECT_EQ(exitxmm[1], SIGUSR2); 162 } 163 164 } // namespace 165 166 } // namespace testing 167 } // namespace gvisor