Files
Andrei VaginandgVisor bot b113aa7639 kvm: check CPL to find out if the Sentry is in VM
RFLAGS cannot be used because the IF flag (interrupt flag) is cleared when
a goroutine suspended on the host side is resumed in the VM. This means it
can start switching into the vm when it is already in the VM. It only works
because the rt_sigprocmask syscall is triggered before constructing a signal
frame, returning execution back to the host side, and then it switches into
the VM again.

Fixes: 6a0edeac0f ("platform/kvm: rewriting bluepill()...")
PiperOrigin-RevId: 673621750
2024-09-11 18:52:36 -07:00

135 lines
3.6 KiB
Go

// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build amd64
// +build amd64
package kvm
import (
"testing"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/platform"
"gvisor.dev/gvisor/pkg/sentry/platform/kvm/testutil"
)
func TestGetCS(t *testing.T) {
cs := getcs()
if cs != 0x33 {
t.Fatalf("cs = 0x%x", cs)
}
}
func TestSegments(t *testing.T) {
applicationTest(t, true, testutil.AddrOfTwiddleSegments(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTestSegments(regs)
for {
var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
PageTables: pt,
FullRestore: true,
}, &si); err == platform.ErrContextInterrupt {
continue // Retry.
} else if err != nil {
t.Errorf("application segment check with full restore got unexpected error: %v", err)
}
if err := testutil.CheckTestSegments(regs); err != nil {
t.Errorf("application segment check with full restore failed: %v", err)
}
break // Done.
}
return false
})
}
func vmcall(ax uintptr) uintptr
func vmmcall(ax uintptr) uintptr
func BenchmarkVMCall(b *testing.B) {
vmcallIsSupported := cpuid.HostFeatureSet().Intel()
kvmTest(b, nil, func(c *vCPU) bool {
for i := 0; i < b.N; i++ {
bluepill(c)
if vmcallIsSupported {
vmcall(0)
} else {
vmmcall(0)
}
}
return false
})
}
// stmxcsr reads the MXCSR control and status register.
func stmxcsr(addr *uint32)
func TestMXCSR(t *testing.T) {
applicationTest(t, true, testutil.AddrOfSyscallLoop(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
var si linux.SignalInfo
switchOpts := ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
PageTables: pt,
FullRestore: true,
}
const mxcsrControllMask = uint32(0x1f80)
mxcsrBefore := uint32(0)
mxcsrAfter := uint32(0)
stmxcsr(&mxcsrBefore)
if mxcsrBefore == 0 {
// goruntime sets mxcsr to 0x1f80 and it never changes
// the control configuration.
panic("mxcsr is zero")
}
switchOpts.FloatingPointState.SetMXCSR(0)
if _, err := c.SwitchToUser(
switchOpts, &si); err == platform.ErrContextInterrupt {
return true // Retry.
} else if err != nil {
t.Errorf("application syscall failed: %v", err)
}
stmxcsr(&mxcsrAfter)
if mxcsrAfter&mxcsrControllMask != mxcsrBefore&mxcsrControllMask {
t.Errorf("mxcsr = %x (expected %x)", mxcsrBefore, mxcsrAfter)
}
return false
})
}
//go:nosplit
func nestedVirtIsOn(c *vCPU, fs *cpuid.FeatureSet) bool {
bluepill(c)
return fs.HasFeature(cpuid.X86FeatureVMX) || fs.HasFeature(cpuid.X86FeatureSVM)
}
func TestKernelCPUID(t *testing.T) {
bluepillTest(t, func(c *vCPU) {
fs := cpuid.FeatureSet{
Function: &cpuid.Native{},
}
if nestedVirtIsOn(c, &fs) {
t.Fatalf("Nested virtualization is enabled")
}
})
}