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:
Andrei Vagin
2023-07-10 13:54:12 -07:00
committed by gVisor bot
parent 1c287f3c68
commit 4adc33ad0d
2 changed files with 32 additions and 11 deletions
+8 -4
View File
@@ -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 -7
View File
@@ -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