summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry
diff options
context:
space:
mode:
authorAdin Scannell <ascannell@google.com>2018-10-31 15:49:10 -0700
committerShentubot <shentubot@google.com>2018-10-31 15:50:10 -0700
commitc4bbb54168a9014048d2144110e70daf5a5b8211 (patch)
treefa82c830fa173e68d10772430be165f278123c91 /pkg/sentry
parentccc3d7ca11a2a623587c651a6690aaa46d2c2665 (diff)
kvm: add detailed traces on vCPU errors.
This improves debuggability greatly. PiperOrigin-RevId: 219551560 Change-Id: I2ecaffdd1c17b0d9f25911538ea6f693e2bc699f
Diffstat (limited to 'pkg/sentry')
-rw-r--r--pkg/sentry/platform/kvm/bluepill.go48
-rw-r--r--pkg/sentry/platform/kvm/bluepill_amd64.s6
-rw-r--r--pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go28
-rw-r--r--pkg/sentry/platform/kvm/bluepill_unsafe.go29
-rw-r--r--pkg/sentry/platform/kvm/kvm_const.go1
-rw-r--r--pkg/sentry/platform/kvm/machine.go3
-rw-r--r--pkg/sentry/platform/kvm/machine_amd64_unsafe.go24
-rw-r--r--pkg/sentry/platform/kvm/machine_unsafe.go40
8 files changed, 142 insertions, 37 deletions
diff --git a/pkg/sentry/platform/kvm/bluepill.go b/pkg/sentry/platform/kvm/bluepill.go
index 9f1c9510b..d98ec8377 100644
--- a/pkg/sentry/platform/kvm/bluepill.go
+++ b/pkg/sentry/platform/kvm/bluepill.go
@@ -19,6 +19,7 @@ import (
"reflect"
"syscall"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform/safecopy"
)
@@ -28,14 +29,55 @@ func bluepill(*vCPU)
// sighandler is the signal entry point.
func sighandler()
-// savedHandler is a pointer to the previous handler.
+// dieTrampoline is the assembly trampoline. This calls dieHandler.
//
-// This is called by bluepillHandler.
-var savedHandler uintptr
+// This uses an architecture-specific calling convention, documented in
+// dieArchSetup and the assembly implementation for dieTrampoline.
+func dieTrampoline()
+
+var (
+ // savedHandler is a pointer to the previous handler.
+ //
+ // This is called by bluepillHandler.
+ savedHandler uintptr
+
+ // dieTrampolineAddr is the address of dieTrampoline.
+ dieTrampolineAddr uintptr
+)
+
+// dieHandler is called by dieTrampoline.
+//
+//go:nosplit
+func dieHandler(c *vCPU) {
+ throw(c.dieMessage)
+}
+
+// die is called to set the vCPU up to panic.
+//
+// This loads vCPU state, and sets up a call for the trampoline.
+//
+//go:nosplit
+func (c *vCPU) die(context *arch.SignalContext64, msg string) {
+ // Save the death message, which will be thrown.
+ c.dieMessage = msg
+
+ // Reload all registers to have an accurate stack trace when we return
+ // to host mode. This means that the stack should be unwound correctly.
+ var guestRegs userRegs
+ if errno := c.getUserRegisters(&guestRegs); errno != 0 {
+ throw(msg)
+ }
+
+ // Setup the trampoline.
+ dieArchSetup(c, context, &guestRegs)
+}
func init() {
// Install the handler.
if err := safecopy.ReplaceSignalHandler(syscall.SIGSEGV, reflect.ValueOf(sighandler).Pointer(), &savedHandler); err != nil {
panic(fmt.Sprintf("Unable to set handler for signal %d: %v", syscall.SIGSEGV, err))
}
+
+ // Extract the address for the trampoline.
+ dieTrampolineAddr = reflect.ValueOf(dieTrampoline).Pointer()
}
diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.s b/pkg/sentry/platform/kvm/bluepill_amd64.s
index ec017f6c2..65b01f358 100644
--- a/pkg/sentry/platform/kvm/bluepill_amd64.s
+++ b/pkg/sentry/platform/kvm/bluepill_amd64.s
@@ -85,3 +85,9 @@ fallback:
XORQ CX, CX
MOVQ ·savedHandler(SB), AX
JMP AX
+
+// dieTrampoline: see bluepill.go, bluepill_amd64_unsafe.go for documentation.
+TEXT ·dieTrampoline(SB),NOSPLIT,$0
+ PUSHQ BX // First argument (vCPU).
+ PUSHQ AX // Fake the old RIP as caller.
+ JMP ·dieHandler(SB)
diff --git a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go
index cd00a47f2..21de2488e 100644
--- a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go
+++ b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go
@@ -20,9 +20,37 @@ import (
"unsafe"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/platform/ring0"
)
// bluepillArchContext returns the arch-specific context.
+//
+//go:nosplit
func bluepillArchContext(context unsafe.Pointer) *arch.SignalContext64 {
return &((*arch.UContext64)(context).MContext)
}
+
+// dieArchSetup initialies the state for dieTrampoline.
+//
+// The amd64 dieTrampoline requires the vCPU to be set in BX, and the last RIP
+// to be in AX. The trampoline then simulates a call to dieHandler from the
+// provided RIP.
+//
+//go:nosplit
+func dieArchSetup(c *vCPU, context *arch.SignalContext64, guestRegs *userRegs) {
+ // If the vCPU is in user mode, we set the stack to the stored stack
+ // value in the vCPU itself. We don't want to unwind the user stack.
+ if guestRegs.RFLAGS&ring0.UserFlagsSet == ring0.UserFlagsSet {
+ regs := c.CPU.Registers()
+ context.Rax = regs.Rax
+ context.Rsp = regs.Rsp
+ context.Rbp = regs.Rbp
+ } else {
+ context.Rax = guestRegs.RIP
+ context.Rsp = guestRegs.RSP
+ context.Rbp = guestRegs.RBP
+ context.Eflags = guestRegs.RFLAGS
+ }
+ context.Rbx = uint64(uintptr(unsafe.Pointer(c)))
+ context.Rip = uint64(dieTrampolineAddr)
+}
diff --git a/pkg/sentry/platform/kvm/bluepill_unsafe.go b/pkg/sentry/platform/kvm/bluepill_unsafe.go
index 747a95997..77cf7e800 100644
--- a/pkg/sentry/platform/kvm/bluepill_unsafe.go
+++ b/pkg/sentry/platform/kvm/bluepill_unsafe.go
@@ -113,9 +113,11 @@ func bluepillHandler(context unsafe.Pointer) {
switch c.runData.exitReason {
case _KVM_EXIT_EXCEPTION:
- throw("exception")
+ c.die(bluepillArchContext(context), "exception")
+ return
case _KVM_EXIT_IO:
- throw("I/O")
+ c.die(bluepillArchContext(context), "I/O")
+ return
case _KVM_EXIT_INTERNAL_ERROR:
// An internal error is typically thrown when emulation
// fails. This can occur via the MMIO path below (and
@@ -123,9 +125,11 @@ func bluepillHandler(context unsafe.Pointer) {
// are not mapped). We would actually prefer that no
// emulation occur, and don't mind at all if it fails.
case _KVM_EXIT_HYPERCALL:
- throw("hypercall")
+ c.die(bluepillArchContext(context), "hypercall")
+ return
case _KVM_EXIT_DEBUG:
- throw("debug")
+ c.die(bluepillArchContext(context), "debug")
+ return
case _KVM_EXIT_HLT:
// Copy out registers.
bluepillArchExit(c, bluepillArchContext(context))
@@ -145,9 +149,11 @@ func bluepillHandler(context unsafe.Pointer) {
atomic.AddUint32(&c.faults, 1)
// For MMIO, the physical address is the first data item.
- virtual, ok := handleBluepillFault(c.machine, uintptr(c.runData.data[0]))
+ physical := uintptr(c.runData.data[0])
+ virtual, ok := handleBluepillFault(c.machine, physical)
if !ok {
- throw("physical address not valid")
+ c.die(bluepillArchContext(context), "invalid physical address")
+ return
}
// We now need to fill in the data appropriately. KVM
@@ -158,7 +164,7 @@ func bluepillHandler(context unsafe.Pointer) {
// not create invalid page table mappings.
data := (*[8]byte)(unsafe.Pointer(&c.runData.data[1]))
length := (uintptr)((uint32)(c.runData.data[2]))
- write := (uint8)((c.runData.data[2] >> 32 & 0xff)) != 0
+ write := (uint8)(((c.runData.data[2] >> 32) & 0xff)) != 0
for i := uintptr(0); i < length; i++ {
b := bytePtr(uintptr(virtual) + i)
if write {
@@ -182,11 +188,14 @@ func bluepillHandler(context unsafe.Pointer) {
// Clear previous injection request.
c.runData.requestInterruptWindow = 0
case _KVM_EXIT_SHUTDOWN:
- throw("shutdown")
+ c.die(bluepillArchContext(context), "shutdown")
+ return
case _KVM_EXIT_FAIL_ENTRY:
- throw("entry failed")
+ c.die(bluepillArchContext(context), "entry failed")
+ return
default:
- throw("unknown failure")
+ c.die(bluepillArchContext(context), "unknown")
+ return
}
}
}
diff --git a/pkg/sentry/platform/kvm/kvm_const.go b/pkg/sentry/platform/kvm/kvm_const.go
index 8c53c6f06..cac8d9937 100644
--- a/pkg/sentry/platform/kvm/kvm_const.go
+++ b/pkg/sentry/platform/kvm/kvm_const.go
@@ -31,6 +31,7 @@ const (
_KVM_SET_USER_MEMORY_REGION = 0x4020ae46
_KVM_SET_REGS = 0x4090ae82
_KVM_SET_SREGS = 0x4138ae84
+ _KVM_GET_REGS = 0x8090ae81
_KVM_GET_SUPPORTED_CPUID = 0xc008ae05
_KVM_SET_CPUID2 = 0x4008ae90
_KVM_SET_SIGNAL_MASK = 0x4004ae8b
diff --git a/pkg/sentry/platform/kvm/machine.go b/pkg/sentry/platform/kvm/machine.go
index fc7ad258f..4ba3a185a 100644
--- a/pkg/sentry/platform/kvm/machine.go
+++ b/pkg/sentry/platform/kvm/machine.go
@@ -120,6 +120,9 @@ type vCPU struct {
// vCPUArchState is the architecture-specific state.
vCPUArchState
+
+ // dieMessage is thrown from die.
+ dieMessage string
}
// newVCPU creates a returns a new vCPU.
diff --git a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go
index 50e513f3b..8ebd4ab71 100644
--- a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go
+++ b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go
@@ -73,30 +73,6 @@ func (c *vCPU) loadSegments(tid uint64) {
atomic.StoreUint64(&c.tid, tid)
}
-// setUserRegisters sets user registers in the vCPU.
-func (c *vCPU) setUserRegisters(uregs *userRegs) error {
- if _, _, errno := syscall.RawSyscall(
- syscall.SYS_IOCTL,
- uintptr(c.fd),
- _KVM_SET_REGS,
- uintptr(unsafe.Pointer(uregs))); errno != 0 {
- return fmt.Errorf("error setting user registers: %v", errno)
- }
- return nil
-}
-
-// setSystemRegisters sets system registers.
-func (c *vCPU) setSystemRegisters(sregs *systemRegs) error {
- if _, _, errno := syscall.RawSyscall(
- syscall.SYS_IOCTL,
- uintptr(c.fd),
- _KVM_SET_SREGS,
- uintptr(unsafe.Pointer(sregs))); errno != 0 {
- return fmt.Errorf("error setting system registers: %v", errno)
- }
- return nil
-}
-
// setCPUID sets the CPUID to be used by the guest.
func (c *vCPU) setCPUID() error {
if _, _, errno := syscall.RawSyscall(
diff --git a/pkg/sentry/platform/kvm/machine_unsafe.go b/pkg/sentry/platform/kvm/machine_unsafe.go
index 38c1f102f..22ae60b63 100644
--- a/pkg/sentry/platform/kvm/machine_unsafe.go
+++ b/pkg/sentry/platform/kvm/machine_unsafe.go
@@ -57,6 +57,46 @@ func unmapRunData(r *runData) error {
return nil
}
+// setUserRegisters sets user registers in the vCPU.
+func (c *vCPU) setUserRegisters(uregs *userRegs) error {
+ if _, _, errno := syscall.RawSyscall(
+ syscall.SYS_IOCTL,
+ uintptr(c.fd),
+ _KVM_SET_REGS,
+ uintptr(unsafe.Pointer(uregs))); errno != 0 {
+ return fmt.Errorf("error setting user registers: %v", errno)
+ }
+ return nil
+}
+
+// getUserRegisters reloads user registers in the vCPU.
+//
+// This is safe to call from a nosplit context.
+//
+//go:nosplit
+func (c *vCPU) getUserRegisters(uregs *userRegs) syscall.Errno {
+ if _, _, errno := syscall.RawSyscall(
+ syscall.SYS_IOCTL,
+ uintptr(c.fd),
+ _KVM_GET_REGS,
+ uintptr(unsafe.Pointer(uregs))); errno != 0 {
+ return errno
+ }
+ return 0
+}
+
+// setSystemRegisters sets system registers.
+func (c *vCPU) setSystemRegisters(sregs *systemRegs) error {
+ if _, _, errno := syscall.RawSyscall(
+ syscall.SYS_IOCTL,
+ uintptr(c.fd),
+ _KVM_SET_SREGS,
+ uintptr(unsafe.Pointer(sregs))); errno != 0 {
+ return fmt.Errorf("error setting system registers: %v", errno)
+ }
+ return nil
+}
+
// atomicAddressSpace is an atomic address space pointer.
type atomicAddressSpace struct {
pointer unsafe.Pointer