mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
fpu: avoid user stack corruptions
When we reserve space for an FPU state in a signal frame, we need to use the size of the current fpu state. The kernel cpuid can report a smaller size. On restore, we need to read the size of an fpu state from a signal frame. After S/R, it can't be different from the current fpu size. PiperOrigin-RevId: 546972243
This commit is contained in:
@@ -61,6 +61,9 @@ const (
|
||||
// XFEATURE_MASK_FPSSE is xsave features that are always enabled in
|
||||
// signal frame fpstate.
|
||||
XFEATURE_MASK_FPSSE = 0x3
|
||||
|
||||
// FXSAVE_AREA_SIZE is the size of the FXSAVE area.
|
||||
FXSAVE_AREA_SIZE = 512
|
||||
)
|
||||
|
||||
// initX86FPState (defined in asm files) sets up initial state.
|
||||
@@ -323,7 +326,7 @@ const fxsaveBV uint64 = cpuid.XSAVEFeatureX87 | cpuid.XSAVEFeatureSSE
|
||||
// AfterLoad converts the loaded state to the format that compatible with the
|
||||
// current processor.
|
||||
func (s *State) AfterLoad() {
|
||||
old := *s
|
||||
old := s.Slice()
|
||||
|
||||
// Recreate the slice. This is done to ensure that it is aligned
|
||||
// appropriately in memory, and large enough to accommodate any new
|
||||
@@ -348,8 +351,9 @@ func (s *State) AfterLoad() {
|
||||
// (according to XSTATE_BV) which we do not support.
|
||||
// What do we support?
|
||||
supportedBV := fxsaveBV
|
||||
if fs := cpuid.HostFeatureSet(); fs.UseXsave() {
|
||||
supportedBV = fs.ValidXCR0Mask()
|
||||
hostFeatureSet := cpuid.HostFeatureSet()
|
||||
if hostFeatureSet.UseXsave() {
|
||||
supportedBV = hostFeatureSet.ValidXCR0Mask()
|
||||
}
|
||||
|
||||
// What was in use?
|
||||
@@ -372,7 +376,7 @@ func (s *State) AfterLoad() {
|
||||
if mxcsrBefore != mxcsrAfter {
|
||||
panic(fmt.Sprintf("incompatible mxcsr value: %x (%x)", mxcsrBefore, mxcsrAfter))
|
||||
}
|
||||
if fs := cpuid.HostFeatureSet(); fs.UseXsave() {
|
||||
if hostFeatureSet.UseXsave() {
|
||||
if err := safecopy.CheckXstate(s.BytePointer()); err != nil {
|
||||
panic(fmt.Sprintf("incompatible state: %s (%#v)", err, *s))
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/cpuid"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
|
||||
@@ -101,12 +102,9 @@ func (c *Context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.Sig
|
||||
}
|
||||
|
||||
// Allocate space for floating point state on the stack.
|
||||
fpSize, fpAlign := featureSet.ExtendedStateSize()
|
||||
if fpSize < 512 {
|
||||
// We expect support for at least FXSAVE.
|
||||
fpSize = 512
|
||||
}
|
||||
fpSize += fpu.FP_XSTATE_MAGIC2_SIZE
|
||||
_, fpAlign := featureSet.ExtendedStateSize()
|
||||
fpState := c.fpState.Slice()
|
||||
fpSize := len(fpState) + fpu.FP_XSTATE_MAGIC2_SIZE
|
||||
fpStart := (sp - hostarch.Addr(fpSize)) & ^hostarch.Addr(fpAlign-1)
|
||||
|
||||
// Construct the UContext64 now since we need its size.
|
||||
@@ -167,7 +165,6 @@ func (c *Context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.Sig
|
||||
return unix.EFAULT
|
||||
}
|
||||
|
||||
fpState := c.fpState.Slice()
|
||||
// Set up floating point state on the stack. Compare Linux's
|
||||
// arch/x86/kernel/fpu/signal.c:copy_fpstate_to_sigframe().
|
||||
if _, err := st.IO.CopyOut(context.Background(), fpStart, fpState[:fpu.FP_SW_FRAME_OFFSET], usermem.IOOpts{}); err != nil {
|
||||
@@ -277,7 +274,27 @@ func (c *Context64) SignalRestore(st *Stack, rt bool, featureSet cpuid.FeatureSe
|
||||
if uc.MContext.Fpstate == 0 {
|
||||
c.fpState.Reset()
|
||||
} else {
|
||||
fpsw := fpu.FPSoftwareFrame{}
|
||||
st.Bottom = hostarch.Addr(uc.MContext.Fpstate + fpu.FP_SW_FRAME_OFFSET)
|
||||
if _, err := fpsw.CopyIn(st, StackBottomMagic); err != nil {
|
||||
c.fpState.Reset()
|
||||
return 0, linux.SignalStack{}, err
|
||||
}
|
||||
if fpsw.Magic1 != fpu.FP_XSTATE_MAGIC1 ||
|
||||
fpsw.XstateSize < fpu.FXSAVE_AREA_SIZE ||
|
||||
fpsw.XstateSize > fpsw.ExtendedSize {
|
||||
c.fpState.Reset()
|
||||
return 0, linux.SignalStack{}, linuxerr.EFAULT
|
||||
}
|
||||
|
||||
fpState := c.fpState.Slice()
|
||||
fpSize := fpsw.XstateSize
|
||||
if int(fpSize) < len(fpState) {
|
||||
// The signal frame FPU state is smaller than expected. This can happen after S/R.
|
||||
c.fpState.Reset()
|
||||
fpState = fpState[:fpSize]
|
||||
}
|
||||
|
||||
if _, err := st.IO.CopyIn(context.Background(), hostarch.Addr(uc.MContext.Fpstate), fpState, usermem.IOOpts{}); err != nil {
|
||||
c.fpState.Reset()
|
||||
return 0, linux.SignalStack{}, err
|
||||
|
||||
Reference in New Issue
Block a user