summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2019-04-25 17:45:56 -0700
committerShentubot <shentubot@google.com>2019-04-25 17:47:05 -0700
commitf17cfa4d53742923b5c91b149b82a05bcda3ea20 (patch)
tree3f945f2f0db9b1f87e5173e832498998047fbd65 /pkg/sentry
parent6b76c172b48ecb2c342882c0fe6474b2b973dad0 (diff)
Perform explicit CPUID and FP state compatibility checks on restore
PiperOrigin-RevId: 245341004 Change-Id: Ic4d581039d034a8ae944b43e45e84eb2c3973657
Diffstat (limited to 'pkg/sentry')
-rw-r--r--pkg/sentry/arch/arch_state_x86.go59
-rw-r--r--pkg/sentry/kernel/kernel.go30
2 files changed, 76 insertions, 13 deletions
diff --git a/pkg/sentry/arch/arch_state_x86.go b/pkg/sentry/arch/arch_state_x86.go
index 604bd08a6..01949049d 100644
--- a/pkg/sentry/arch/arch_state_x86.go
+++ b/pkg/sentry/arch/arch_state_x86.go
@@ -15,14 +15,31 @@
package arch
import (
- "sync"
+ "fmt"
"syscall"
- "gvisor.googlesource.com/gvisor/pkg/log"
+ "gvisor.googlesource.com/gvisor/pkg/cpuid"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
)
-// warnOnce is used to warn about truncated state only once.
-var warnOnce sync.Once
+// ErrFloatingPoint indicates a failed restore due to unusable floating point
+// state.
+type ErrFloatingPoint struct {
+ // supported is the supported floating point state.
+ supported uint64
+
+ // saved is the saved floating point state.
+ saved uint64
+}
+
+// Error returns a sensible description of the restore error.
+func (e ErrFloatingPoint) Error() string {
+ return fmt.Sprintf("floating point state contains unsupported features; supported: %#x saved: %#x", e.supported, e.saved)
+}
+
+// XSTATE_BV does not exist if FXSAVE is used, but FXSAVE implicitly saves x87
+// and SSE state, so this is the equivalent XSTATE_BV value.
+const fxsaveBV uint64 = cpuid.XSAVEFeatureX87 | cpuid.XSAVEFeatureSSE
// afterLoad is invoked by stateify.
func (s *State) afterLoad() {
@@ -33,7 +50,8 @@ func (s *State) afterLoad() {
// state that may be saved by the new CPU. Even if extraneous new state
// is saved, the state we care about is guaranteed to be a subset of
// new state. Later optimizations can use less space when using a
- // smaller state component bitmap. Intel SDM section 13 has more info.
+ // smaller state component bitmap. Intel SDM Volume 1 Chapter 13 has
+ // more info.
s.x86FPState = newX86FPState()
// x86FPState always contains all the FP state supported by the host.
@@ -41,15 +59,30 @@ func (s *State) afterLoad() {
// which we cannot restore.
//
// The x86 FP state areas are backwards compatible, so we can simply
- // truncate the additional floating point state. Applications should
- // not depend on the truncated state because it should relate only to
- // features that were not exposed in the app FeatureSet.
+ // truncate the additional floating point state.
+ //
+ // Applications should not depend on the truncated state because it
+ // should relate only to features that were not exposed in the app
+ // FeatureSet. However, because we do not *prevent* them from using
+ // this state, we must verify here that there is no in-use state
+ // (according to XSTATE_BV) which we do not support.
if len(s.x86FPState) < len(old) {
- warnOnce.Do(func() {
- // This will occur on every instance of state, don't
- // bother warning more than once.
- log.Infof("dropping %d bytes of floating point state; the application should not depend on this state", len(old)-len(s.x86FPState))
- })
+ // What do we support?
+ supportedBV := fxsaveBV
+ if fs := cpuid.HostFeatureSet(); fs.UseXsave() {
+ supportedBV = fs.ValidXCR0Mask()
+ }
+
+ // What was in use?
+ savedBV := fxsaveBV
+ if len(old) >= xstateBVOffset+8 {
+ savedBV = usermem.ByteOrder.Uint64(old[xstateBVOffset:])
+ }
+
+ // Supported features must be a superset of saved features.
+ if savedBV&^supportedBV != 0 {
+ panic(ErrFloatingPoint{supported: supportedBV, saved: savedBV})
+ }
}
// Copy to the new, aligned location.
diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go
index ee6334509..a1b2d7161 100644
--- a/pkg/sentry/kernel/kernel.go
+++ b/pkg/sentry/kernel/kernel.go
@@ -337,6 +337,17 @@ func (k *Kernel) SaveTo(w io.Writer) error {
return fmt.Errorf("failed to invalidate unsavable mappings: %v", err)
}
+ // Save the CPUID FeatureSet before the rest of the kernel so we can
+ // verify its compatibility on restore before attempting to restore the
+ // entire kernel, which may fail on an incompatible machine.
+ //
+ // N.B. This will also be saved along with the full kernel save below.
+ cpuidStart := time.Now()
+ if err := state.Save(w, k.FeatureSet(), nil); err != nil {
+ return err
+ }
+ log.Infof("CPUID save took [%s].", time.Since(cpuidStart))
+
// Save the kernel state.
kernelStart := time.Now()
var stats state.Stats
@@ -469,6 +480,25 @@ func (k *Kernel) LoadFrom(r io.Reader, net inet.Stack) error {
initAppCores := k.applicationCores
+ // Load the pre-saved CPUID FeatureSet.
+ //
+ // N.B. This was also saved along with the full kernel below, so we
+ // don't need to explicitly install it in the Kernel.
+ cpuidStart := time.Now()
+ var features cpuid.FeatureSet
+ if err := state.Load(r, &features, nil); err != nil {
+ return err
+ }
+ log.Infof("CPUID load took [%s].", time.Since(cpuidStart))
+
+ // Verify that the FeatureSet is usable on this host. We do this before
+ // Kernel load so that the explicit CPUID mismatch error has priority
+ // over floating point state restore errors that may occur on load on
+ // an incompatible machine.
+ if err := features.CheckHostCompatible(); err != nil {
+ return err
+ }
+
// Load the kernel state.
kernelStart := time.Now()
var stats state.Stats