github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/arch/signal_amd64.go (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 //go:build amd64 16 // +build amd64 17 18 package arch 19 20 import ( 21 "math" 22 23 "golang.org/x/sys/unix" 24 "github.com/metacubex/gvisor/pkg/abi/linux" 25 "github.com/metacubex/gvisor/pkg/context" 26 "github.com/metacubex/gvisor/pkg/cpuid" 27 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 28 "github.com/metacubex/gvisor/pkg/hostarch" 29 "github.com/metacubex/gvisor/pkg/marshal/primitive" 30 "github.com/metacubex/gvisor/pkg/sentry/arch/fpu" 31 "github.com/metacubex/gvisor/pkg/usermem" 32 ) 33 34 // SignalContext64 is equivalent to struct sigcontext, the type passed as the 35 // second argument to signal handlers set by signal(2). 36 // 37 // +marshal 38 type SignalContext64 struct { 39 R8 uint64 40 R9 uint64 41 R10 uint64 42 R11 uint64 43 R12 uint64 44 R13 uint64 45 R14 uint64 46 R15 uint64 47 Rdi uint64 48 Rsi uint64 49 Rbp uint64 50 Rbx uint64 51 Rdx uint64 52 Rax uint64 53 Rcx uint64 54 Rsp uint64 55 Rip uint64 56 Eflags uint64 57 Cs uint16 58 Gs uint16 // always 0 on amd64. 59 Fs uint16 // always 0 on amd64. 60 Ss uint16 // only restored if _UC_STRICT_RESTORE_SS (unsupported). 61 Err uint64 62 Trapno uint64 63 Oldmask linux.SignalSet 64 Cr2 uint64 65 // Pointer to a struct _fpstate. 66 Fpstate uint64 67 Reserved [8]uint64 68 } 69 70 // Flags for UContext64.Flags. 71 const ( 72 _UC_FP_XSTATE = 1 73 _UC_SIGCONTEXT_SS = 2 74 _UC_STRICT_RESTORE_SS = 4 75 ) 76 77 // UContext64 is equivalent to ucontext_t on 64-bit x86. 78 // 79 // +marshal 80 type UContext64 struct { 81 Flags uint64 82 Link uint64 83 Stack linux.SignalStack 84 MContext SignalContext64 85 Sigset linux.SignalSet 86 } 87 88 // SignalSetup implements Context.SignalSetup. (Compare to Linux's 89 // arch/x86/kernel/signal.c:__setup_rt_frame().) 90 func (c *Context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.SignalInfo, alt *linux.SignalStack, sigset linux.SignalSet, featureSet cpuid.FeatureSet) error { 91 // "The 128-byte area beyond the location pointed to by %rsp is considered 92 // to be reserved and shall not be modified by signal or interrupt 93 // handlers. ... leaf functions may use this area for their entire stack 94 // frame, rather than adjusting the stack pointer in the prologue and 95 // epilogue." - AMD64 ABI 96 // 97 // (But this doesn't apply if we're starting at the top of the signal 98 // stack, in which case there is no following stack frame.) 99 sp := st.Bottom 100 if !(alt.IsEnabled() && sp == alt.Top()) { 101 sp -= 128 102 } 103 104 // Allocate space for floating point state on the stack. 105 _, fpAlign := featureSet.ExtendedStateSize() 106 fpState := c.fpState.Slice() 107 fpSize := len(fpState) + fpu.FP_XSTATE_MAGIC2_SIZE 108 fpStart := (sp - hostarch.Addr(fpSize)) & ^hostarch.Addr(fpAlign-1) 109 110 // Construct the UContext64 now since we need its size. 111 uc := &UContext64{ 112 // No _UC_STRICT_RESTORE_SS: we don't allow SS changes. 113 Flags: _UC_SIGCONTEXT_SS, 114 Stack: *alt, 115 MContext: SignalContext64{ 116 R8: c.Regs.R8, 117 R9: c.Regs.R9, 118 R10: c.Regs.R10, 119 R11: c.Regs.R11, 120 R12: c.Regs.R12, 121 R13: c.Regs.R13, 122 R14: c.Regs.R14, 123 R15: c.Regs.R15, 124 Rdi: c.Regs.Rdi, 125 Rsi: c.Regs.Rsi, 126 Rbp: c.Regs.Rbp, 127 Rbx: c.Regs.Rbx, 128 Rdx: c.Regs.Rdx, 129 Rax: c.Regs.Rax, 130 Rcx: c.Regs.Rcx, 131 Rsp: c.Regs.Rsp, 132 Rip: c.Regs.Rip, 133 Eflags: c.Regs.Eflags, 134 Cs: uint16(c.Regs.Cs), 135 Ss: uint16(c.Regs.Ss), 136 Oldmask: sigset, 137 Fpstate: uint64(fpStart), 138 }, 139 Sigset: sigset, 140 } 141 if featureSet.UseXsave() { 142 uc.Flags |= _UC_FP_XSTATE 143 } 144 145 // TODO(gvisor.dev/issue/159): Set SignalContext64.Err, Trapno, and Cr2 146 // based on the fault that caused the signal. For now, leave Err and 147 // Trapno unset and assume CR2 == info.Addr() for SIGSEGVs and 148 // SIGBUSes. 149 if linux.Signal(info.Signo) == linux.SIGSEGV || linux.Signal(info.Signo) == linux.SIGBUS { 150 uc.MContext.Cr2 = info.Addr() 151 } 152 153 // "... the value (%rsp+8) is always a multiple of 16 (...) when 154 // control is transferred to the function entry point." - AMD64 ABI 155 ucSize := uc.SizeBytes() 156 // st.Arch.Width() is for the restorer address. sizeof(siginfo) == 128. 157 frameSize := int(st.Arch.Width()) + ucSize + 128 158 frameStart := (fpStart-hostarch.Addr(frameSize)) & ^hostarch.Addr(15) - 8 159 frameEnd := frameStart + hostarch.Addr(frameSize) 160 161 // Prior to proceeding, figure out if the frame will exhaust the range 162 // for the signal stack. This is not allowed, and should immediately 163 // force signal delivery (reverting to the default handler). 164 if act.Flags&linux.SA_ONSTACK != 0 && alt.IsEnabled() && !alt.Contains(frameStart) { 165 return unix.EFAULT 166 } 167 168 // Set up floating point state on the stack. Compare Linux's 169 // arch/x86/kernel/fpu/signal.c:copy_fpstate_to_sigframe(). 170 if _, err := st.IO.CopyOut(context.Background(), fpStart, fpState[:fpu.FP_SW_FRAME_OFFSET], usermem.IOOpts{}); err != nil { 171 return err 172 } 173 fpsw := fpu.FPSoftwareFrame{ 174 Magic1: fpu.FP_XSTATE_MAGIC1, 175 ExtendedSize: uint32(fpSize), 176 Xfeatures: fpu.XFEATURE_MASK_FPSSE | featureSet.ValidXCR0Mask(), 177 XstateSize: uint32(fpSize) - fpu.FP_XSTATE_MAGIC2_SIZE, 178 } 179 st.Bottom = fpStart + 512 180 if _, err := fpsw.CopyOut(st, StackBottomMagic); err != nil { 181 return err 182 } 183 if len(fpState) > 512 { 184 if _, err := st.IO.CopyOut(context.Background(), fpStart+512, fpState[512:], usermem.IOOpts{}); err != nil { 185 return err 186 } 187 } 188 st.Bottom = fpStart + hostarch.Addr(fpSize) 189 if _, err := primitive.CopyUint32Out(st, StackBottomMagic, fpu.FP_XSTATE_MAGIC2); err != nil { 190 return err 191 } 192 193 // Adjust the code. 194 info.FixSignalCodeForUser() 195 196 // Set up the stack frame. 197 st.Bottom = frameEnd 198 if _, err := info.CopyOut(st, StackBottomMagic); err != nil { 199 return err 200 } 201 infoAddr := st.Bottom 202 if _, err := uc.CopyOut(st, StackBottomMagic); err != nil { 203 return err 204 } 205 ucAddr := st.Bottom 206 if act.Flags&linux.SA_RESTORER != 0 { 207 // Push the restorer return address. 208 // Note that this doesn't need to be popped. 209 if _, err := primitive.CopyUint64Out(st, StackBottomMagic, act.Restorer); err != nil { 210 return err 211 } 212 } else { 213 // amd64 requires a restorer. 214 return unix.EFAULT 215 } 216 217 // Set up registers. 218 c.Regs.Rip = act.Handler 219 c.Regs.Rsp = uint64(st.Bottom) 220 c.Regs.Rdi = uint64(info.Signo) 221 c.Regs.Rsi = uint64(infoAddr) 222 c.Regs.Rdx = uint64(ucAddr) 223 c.Regs.Rax = 0 224 c.Regs.Eflags &^= eflagsDF | eflagsRF | eflagsTF 225 c.Regs.Ds = userDS 226 c.Regs.Es = userDS 227 c.Regs.Cs = userCS 228 c.Regs.Ss = userDS 229 230 // Clear floating point registers. 231 c.fpState.Reset() 232 233 return nil 234 } 235 236 // SignalRestore implements Context.SignalRestore. (Compare to Linux's 237 // arch/x86/kernel/signal.c:sys_rt_sigreturn().) 238 func (c *Context64) SignalRestore(st *Stack, rt bool, featureSet cpuid.FeatureSet) (linux.SignalSet, linux.SignalStack, error) { 239 // Copy out the stack frame. 240 var uc UContext64 241 if _, err := uc.CopyIn(st, StackBottomMagic); err != nil { 242 return 0, linux.SignalStack{}, err 243 } 244 var info linux.SignalInfo 245 if _, err := info.CopyIn(st, StackBottomMagic); err != nil { 246 return 0, linux.SignalStack{}, err 247 } 248 249 // Restore registers. 250 c.Regs.R8 = uc.MContext.R8 251 c.Regs.R9 = uc.MContext.R9 252 c.Regs.R10 = uc.MContext.R10 253 c.Regs.R11 = uc.MContext.R11 254 c.Regs.R12 = uc.MContext.R12 255 c.Regs.R13 = uc.MContext.R13 256 c.Regs.R14 = uc.MContext.R14 257 c.Regs.R15 = uc.MContext.R15 258 c.Regs.Rdi = uc.MContext.Rdi 259 c.Regs.Rsi = uc.MContext.Rsi 260 c.Regs.Rbp = uc.MContext.Rbp 261 c.Regs.Rbx = uc.MContext.Rbx 262 c.Regs.Rdx = uc.MContext.Rdx 263 c.Regs.Rax = uc.MContext.Rax 264 c.Regs.Rcx = uc.MContext.Rcx 265 c.Regs.Rsp = uc.MContext.Rsp 266 c.Regs.Rip = uc.MContext.Rip 267 c.Regs.Eflags = (c.Regs.Eflags & ^eflagsRestorable) | (uc.MContext.Eflags & eflagsRestorable) 268 c.Regs.Cs = uint64(uc.MContext.Cs) | 3 269 // N.B. _UC_STRICT_RESTORE_SS not supported. 270 c.Regs.Orig_rax = math.MaxUint64 271 272 // Restore floating point state. Compare Linux's 273 // arch/x86/kernel/fpu/signal.c:fpu__restore_sig(). 274 if uc.MContext.Fpstate == 0 { 275 c.fpState.Reset() 276 } else { 277 fpsw := fpu.FPSoftwareFrame{} 278 st.Bottom = hostarch.Addr(uc.MContext.Fpstate + fpu.FP_SW_FRAME_OFFSET) 279 if _, err := fpsw.CopyIn(st, StackBottomMagic); err != nil { 280 c.fpState.Reset() 281 return 0, linux.SignalStack{}, err 282 } 283 if fpsw.Magic1 != fpu.FP_XSTATE_MAGIC1 || 284 fpsw.XstateSize < fpu.FXSAVE_AREA_SIZE || 285 fpsw.XstateSize > fpsw.ExtendedSize { 286 c.fpState.Reset() 287 return 0, linux.SignalStack{}, linuxerr.EFAULT 288 } 289 290 fpState := c.fpState.Slice() 291 fpSize := fpsw.XstateSize 292 if int(fpSize) < len(fpState) { 293 // The signal frame FPU state is smaller than expected. This can happen after S/R. 294 c.fpState.Reset() 295 fpState = fpState[:fpSize] 296 } 297 298 if _, err := st.IO.CopyIn(context.Background(), hostarch.Addr(uc.MContext.Fpstate), fpState, usermem.IOOpts{}); err != nil { 299 c.fpState.Reset() 300 return 0, linux.SignalStack{}, err 301 } 302 c.fpState.SanitizeUser(featureSet) 303 } 304 305 return uc.Sigset, uc.Stack, nil 306 }