summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/arch/arch_x86.go
diff options
context:
space:
mode:
authorAndrei Vagin <avagin@google.com>2021-03-23 18:44:38 -0700
committergVisor bot <gvisor-bot@google.com>2021-03-23 18:46:37 -0700
commit56a9a13976ad800a8a34b194d35f0169d0a0bb23 (patch)
treecb4b7c4352dc90a8c4c4f469c788fd2c5c6fd0dd /pkg/sentry/arch/arch_x86.go
parent960155cdaad49ccea07e45152f124beeb7e7fdcc (diff)
Move the code that manages floating-point state to a separate package
This change is inspired by Adin's cl/355256448. PiperOrigin-RevId: 364695931
Diffstat (limited to 'pkg/sentry/arch/arch_x86.go')
-rw-r--r--pkg/sentry/arch/arch_x86.go217
1 files changed, 8 insertions, 209 deletions
diff --git a/pkg/sentry/arch/arch_x86.go b/pkg/sentry/arch/arch_x86.go
index 641ada92f..e8e52d3a8 100644
--- a/pkg/sentry/arch/arch_x86.go
+++ b/pkg/sentry/arch/arch_x86.go
@@ -24,10 +24,9 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/log"
+ "gvisor.dev/gvisor/pkg/sentry/arch/fpu"
rpb "gvisor.dev/gvisor/pkg/sentry/arch/registers_go_proto"
- "gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserror"
- "gvisor.dev/gvisor/pkg/usermem"
)
// Registers represents the CPU registers for this architecture.
@@ -111,57 +110,6 @@ var (
X86TrapFlag uint64 = (1 << 8)
)
-// x86FPState is x86 floating point state.
-type x86FPState []byte
-
-// initX86FPState (defined in asm files) sets up initial state.
-func initX86FPState(data *FloatingPointData, useXsave bool)
-
-func newX86FPStateSlice() []byte {
- size, align := cpuid.HostFeatureSet().ExtendedStateSize()
- capacity := size
- // Always use at least 4096 bytes.
- //
- // For the KVM platform, this state is a fixed 4096 bytes, so make sure
- // that the underlying array is at _least_ that size otherwise we will
- // corrupt random memory. This is not a pleasant thing to debug.
- if capacity < 4096 {
- capacity = 4096
- }
- return alignedBytes(capacity, align)[:size]
-}
-
-// newX86FPState returns an initialized floating point state.
-//
-// The returned state is large enough to store all floating point state
-// supported by host, even if the app won't use much of it due to a restricted
-// FeatureSet. Since they may still be able to see state not advertised by
-// CPUID we must ensure it does not contain any sentry state.
-func newX86FPState() x86FPState {
- f := x86FPState(newX86FPStateSlice())
- initX86FPState(f.FloatingPointData(), cpuid.HostFeatureSet().UseXsave())
- return f
-}
-
-// fork creates and returns an identical copy of the x86 floating point state.
-func (f x86FPState) fork() x86FPState {
- n := x86FPState(newX86FPStateSlice())
- copy(n, f)
- return n
-}
-
-// FloatingPointData returns the raw data pointer.
-func (f x86FPState) FloatingPointData() *FloatingPointData {
- return (*FloatingPointData)(&f[0])
-}
-
-// NewFloatingPointData returns a new floating point data blob.
-//
-// This is primarily for use in tests.
-func NewFloatingPointData() *FloatingPointData {
- return (*FloatingPointData)(&(newX86FPState()[0]))
-}
-
// Proto returns a protobuf representation of the system registers in State.
func (s State) Proto() *rpb.Registers {
regs := &rpb.AMD64Registers{
@@ -200,7 +148,7 @@ func (s State) Proto() *rpb.Registers {
func (s *State) Fork() State {
return State{
Regs: s.Regs,
- x86FPState: s.x86FPState.fork(),
+ fpState: s.fpState.Fork(),
FeatureSet: s.FeatureSet,
}
}
@@ -393,149 +341,6 @@ func isValidSegmentBase(reg uint64) bool {
return reg < uint64(maxAddr64)
}
-// ptraceFPRegsSize is the size in bytes of Linux's user_i387_struct, the type
-// manipulated by PTRACE_GETFPREGS and PTRACE_SETFPREGS on x86. Equivalently,
-// ptraceFPRegsSize is the size in bytes of the x86 FXSAVE area.
-const ptraceFPRegsSize = 512
-
-// PtraceGetFPRegs implements Context.PtraceGetFPRegs.
-func (s *State) PtraceGetFPRegs(dst io.Writer) (int, error) {
- return dst.Write(s.x86FPState[:ptraceFPRegsSize])
-}
-
-// PtraceSetFPRegs implements Context.PtraceSetFPRegs.
-func (s *State) PtraceSetFPRegs(src io.Reader) (int, error) {
- var f [ptraceFPRegsSize]byte
- n, err := io.ReadFull(src, f[:])
- if err != nil {
- return 0, err
- }
- // Force reserved bits in MXCSR to 0. This is consistent with Linux.
- sanitizeMXCSR(x86FPState(f[:]))
- // N.B. this only copies the beginning of the FP state, which
- // corresponds to the FXSAVE area.
- copy(s.x86FPState, f[:])
- return n, nil
-}
-
-const (
- // mxcsrOffset is the offset in bytes of the MXCSR field from the start of
- // the FXSAVE area. (Intel SDM Vol. 1, Table 10-2 "Format of an FXSAVE
- // Area")
- mxcsrOffset = 24
-
- // mxcsrMaskOffset is the offset in bytes of the MXCSR_MASK field from the
- // start of the FXSAVE area.
- mxcsrMaskOffset = 28
-)
-
-var (
- mxcsrMask uint32
- initMXCSRMask sync.Once
-)
-
-// sanitizeMXCSR coerces reserved bits in the MXCSR field of f to 0. ("FXRSTOR
-// generates a general-protection fault (#GP) in response to an attempt to set
-// any of the reserved bits of the MXCSR register." - Intel SDM Vol. 1, Section
-// 10.5.1.2 "SSE State")
-func sanitizeMXCSR(f x86FPState) {
- mxcsr := usermem.ByteOrder.Uint32(f[mxcsrOffset:])
- initMXCSRMask.Do(func() {
- temp := x86FPState(alignedBytes(uint(ptraceFPRegsSize), 16))
- initX86FPState(temp.FloatingPointData(), false /* useXsave */)
- mxcsrMask = usermem.ByteOrder.Uint32(temp[mxcsrMaskOffset:])
- if mxcsrMask == 0 {
- // "If the value of the MXCSR_MASK field is 00000000H, then the
- // MXCSR_MASK value is the default value of 0000FFBFH." - Intel SDM
- // Vol. 1, Section 11.6.6 "Guidelines for Writing to the MXCSR
- // Register"
- mxcsrMask = 0xffbf
- }
- })
- mxcsr &= mxcsrMask
- usermem.ByteOrder.PutUint32(f[mxcsrOffset:], mxcsr)
-}
-
-const (
- // minXstateBytes is the minimum size in bytes of an x86 XSAVE area, equal
- // to the size of the XSAVE legacy area (512 bytes) plus the size of the
- // XSAVE header (64 bytes). Equivalently, minXstateBytes is GDB's
- // X86_XSTATE_SSE_SIZE.
- minXstateBytes = 512 + 64
-
- // userXstateXCR0Offset is the offset in bytes of the USER_XSTATE_XCR0_WORD
- // field in Linux's struct user_xstateregs, which is the type manipulated
- // by ptrace(PTRACE_GET/SETREGSET, NT_X86_XSTATE). Equivalently,
- // userXstateXCR0Offset is GDB's I386_LINUX_XSAVE_XCR0_OFFSET.
- userXstateXCR0Offset = 464
-
- // xstateBVOffset is the offset in bytes of the XSTATE_BV field in an x86
- // XSAVE area.
- xstateBVOffset = 512
-
- // xsaveHeaderZeroedOffset and xsaveHeaderZeroedBytes indicate parts of the
- // XSAVE header that we coerce to zero: "Bytes 15:8 of the XSAVE header is
- // a state-component bitmap called XCOMP_BV. ... Bytes 63:16 of the XSAVE
- // header are reserved." - Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header".
- // Linux ignores XCOMP_BV, but it's able to recover from XRSTOR #GP
- // exceptions resulting from invalid values; we aren't. Linux also never
- // uses the compacted format when doing XSAVE and doesn't even define the
- // compaction extensions to XSAVE as a CPU feature, so for simplicity we
- // assume no one is using them.
- xsaveHeaderZeroedOffset = 512 + 8
- xsaveHeaderZeroedBytes = 64 - 8
-)
-
-func (s *State) ptraceGetXstateRegs(dst io.Writer, maxlen int) (int, error) {
- // N.B. s.x86FPState may contain more state than the application
- // expects. We only copy the subset that would be in their XSAVE area.
- ess, _ := s.FeatureSet.ExtendedStateSize()
- f := make([]byte, ess)
- copy(f, s.x86FPState)
- // "The XSAVE feature set does not use bytes 511:416; bytes 463:416 are
- // reserved." - Intel SDM Vol 1., Section 13.4.1 "Legacy Region of an XSAVE
- // Area". Linux uses the first 8 bytes of this area to store the OS XSTATE
- // mask. GDB relies on this: see
- // gdb/x86-linux-nat.c:x86_linux_read_description().
- usermem.ByteOrder.PutUint64(f[userXstateXCR0Offset:], s.FeatureSet.ValidXCR0Mask())
- if len(f) > maxlen {
- f = f[:maxlen]
- }
- return dst.Write(f)
-}
-
-func (s *State) ptraceSetXstateRegs(src io.Reader, maxlen int) (int, error) {
- // Allow users to pass an xstate register set smaller than ours (they can
- // mask bits out of XSTATE_BV), as long as it's at least minXstateBytes.
- // Also allow users to pass a register set larger than ours; anything after
- // their ExtendedStateSize will be ignored. (I think Linux technically
- // permits setting a register set smaller than minXstateBytes, but it has
- // the same silent truncation behavior in kernel/ptrace.c:ptrace_regset().)
- if maxlen < minXstateBytes {
- return 0, unix.EFAULT
- }
- ess, _ := s.FeatureSet.ExtendedStateSize()
- if maxlen > int(ess) {
- maxlen = int(ess)
- }
- f := make([]byte, maxlen)
- if _, err := io.ReadFull(src, f); err != nil {
- return 0, err
- }
- // Force reserved bits in MXCSR to 0. This is consistent with Linux.
- sanitizeMXCSR(x86FPState(f))
- // Users can't enable *more* XCR0 bits than what we, and the CPU, support.
- xstateBV := usermem.ByteOrder.Uint64(f[xstateBVOffset:])
- xstateBV &= s.FeatureSet.ValidXCR0Mask()
- usermem.ByteOrder.PutUint64(f[xstateBVOffset:], xstateBV)
- // Force XCOMP_BV and reserved bytes in the XSAVE header to 0.
- reserved := f[xsaveHeaderZeroedOffset : xsaveHeaderZeroedOffset+xsaveHeaderZeroedBytes]
- for i := range reserved {
- reserved[i] = 0
- }
- return copy(s.x86FPState, f), nil
-}
-
// Register sets defined in include/uapi/linux/elf.h.
const (
_NT_PRSTATUS = 1
@@ -552,12 +357,9 @@ func (s *State) PtraceGetRegSet(regset uintptr, dst io.Writer, maxlen int) (int,
}
return s.PtraceGetRegs(dst)
case _NT_PRFPREG:
- if maxlen < ptraceFPRegsSize {
- return 0, syserror.EFAULT
- }
- return s.PtraceGetFPRegs(dst)
+ return s.fpState.PtraceGetFPRegs(dst, maxlen)
case _NT_X86_XSTATE:
- return s.ptraceGetXstateRegs(dst, maxlen)
+ return s.fpState.PtraceGetXstateRegs(dst, maxlen, s.FeatureSet)
default:
return 0, syserror.EINVAL
}
@@ -572,12 +374,9 @@ func (s *State) PtraceSetRegSet(regset uintptr, src io.Reader, maxlen int) (int,
}
return s.PtraceSetRegs(src)
case _NT_PRFPREG:
- if maxlen < ptraceFPRegsSize {
- return 0, syserror.EFAULT
- }
- return s.PtraceSetFPRegs(src)
+ return s.fpState.PtraceSetFPRegs(src, maxlen)
case _NT_X86_XSTATE:
- return s.ptraceSetXstateRegs(src, maxlen)
+ return s.fpState.PtraceSetXstateRegs(src, maxlen, s.FeatureSet)
default:
return 0, syserror.EINVAL
}
@@ -609,10 +408,10 @@ func New(arch Arch, fs *cpuid.FeatureSet) Context {
case AMD64:
return &context64{
State{
- x86FPState: newX86FPState(),
+ fpState: fpu.NewState(),
FeatureSet: fs,
},
- []x86FPState(nil),
+ []fpu.State(nil),
}
}
panic(fmt.Sprintf("unknown architecture %v", arch))