github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/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  	"github.com/ttpreport/gvisor-ligolo/pkg/abi/linux"
    24  	"github.com/ttpreport/gvisor-ligolo/pkg/context"
    25  	"github.com/ttpreport/gvisor-ligolo/pkg/cpuid"
    26  	"github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr"
    27  	"github.com/ttpreport/gvisor-ligolo/pkg/hostarch"
    28  	"github.com/ttpreport/gvisor-ligolo/pkg/marshal/primitive"
    29  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/arch/fpu"
    30  	"github.com/ttpreport/gvisor-ligolo/pkg/usermem"
    31  	"golang.org/x/sys/unix"
    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  }