gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/fpsig_mut_amd64.cc (about) 1 // Copyright 2022 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 visible in 16 // signal frames, and that changes to said state is visible after the signal 17 // handler returns. 18 #include <sys/time.h> 19 #include <sys/ucontext.h> 20 21 #include "gtest/gtest.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 #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 34 int pid; 35 int tid; 36 37 volatile uint64_t handlerxmm = ~0UL; 38 volatile uint64_t framexmm = ~0UL; 39 40 constexpr uint64_t kOldFPRegValue = 0xdeadbeeffacefeed; 41 constexpr uint64_t kNewFPRegValue = 0xfacefeedbaad1dea; 42 43 void sigusr1(int s, siginfo_t* siginfo, void* _uc) { 44 uint64_t val = SIGUSR1; 45 46 // Record the value of %xmm0 on entry and then clobber it. 47 GET_XMM(handlerxmm, xmm0); 48 SET_XMM(val, xmm0); 49 50 // Record the value of %xmm0 stored in _uc and then replace it. 51 ucontext_t* uc = reinterpret_cast<ucontext_t*>(_uc); 52 auto* uc_xmm0 = &uc->uc_mcontext.fpregs->_xmm[0]; 53 framexmm = (static_cast<uint64_t>(uc_xmm0->element[1]) << 32) | 54 static_cast<uint64_t>(uc_xmm0->element[0]); 55 uc_xmm0->element[1] = static_cast<uint32_t>(kNewFPRegValue >> 32); 56 uc_xmm0->element[0] = static_cast<uint32_t>(kNewFPRegValue); 57 } 58 59 TEST(FPSigTest, StateInFrame) { 60 pid = getpid(); 61 tid = gettid(); 62 63 struct sigaction sa = {}; 64 sigemptyset(&sa.sa_mask); 65 sa.sa_flags = SA_SIGINFO; 66 sa.sa_sigaction = sigusr1; 67 ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); 68 69 // The amd64 ABI specifies that the XMM register set is caller-saved. This 70 // implies that if there is any function call between SET_XMM and GET_XMM the 71 // compiler might save/restore xmm0 implicitly. This defeats the entire 72 // purpose of the test which is to verify that fpstate is restored by 73 // sigreturn(2). 74 // 75 // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented 76 // in inline assembly below. 77 // 78 // If the OS is broken and registers are clobbered by the signal, using tgkill 79 // to signal the current thread ensures that this is the clobbered thread. 80 SET_XMM(kOldFPRegValue, xmm0); 81 82 asm volatile( 83 "movl %[killnr], %%eax;" 84 "movl %[pid], %%edi;" 85 "movl %[tid], %%esi;" 86 "movl %[sig], %%edx;" 87 "syscall;" 88 : 89 : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid), 90 [sig] "i"(SIGUSR1) 91 : "rax", "rdi", "rsi", "rdx", 92 // Clobbered by syscall. 93 "rcx", "r11"); 94 95 uint64_t got; 96 GET_XMM(got, xmm0); 97 98 // 99 // The checks below verifies the following: 100 // - signal handlers must called with a clean fpu state. 101 // - sigreturn(2) must restore fpstate of the interrupted context. 102 // 103 EXPECT_EQ(handlerxmm, 0); 104 EXPECT_EQ(framexmm, kOldFPRegValue); 105 EXPECT_EQ(got, kNewFPRegValue); 106 } 107 108 } // namespace 109 110 } // namespace testing 111 } // namespace gvisor