summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdin Scannell <ascannell@google.com>2018-10-31 10:07:06 -0700
committerShentubot <shentubot@google.com>2018-10-31 10:08:06 -0700
commite9dbd5ab67bc31e59910930e6c1b551c0fd05ee6 (patch)
tree40dd7a93d8ad16864387f001bd4d16b34e207c1c
parent0692ad72ef826c4b8cf0f74c5dbf8d1c3083d299 (diff)
kvm: avoid siginfo allocations.
PiperOrigin-RevId: 219492587 Change-Id: I47f6fc0b74a4907ab0aff03d5f26453bdb983bb5
-rw-r--r--pkg/sentry/platform/kvm/context.go7
-rw-r--r--pkg/sentry/platform/kvm/kvm_test.go88
-rw-r--r--pkg/sentry/platform/kvm/machine_amd64.go65
3 files changed, 89 insertions, 71 deletions
diff --git a/pkg/sentry/platform/kvm/context.go b/pkg/sentry/platform/kvm/context.go
index be902be88..c75a4b415 100644
--- a/pkg/sentry/platform/kvm/context.go
+++ b/pkg/sentry/platform/kvm/context.go
@@ -29,6 +29,9 @@ type context struct {
// machine is the parent machine, and is immutable.
machine *machine
+ // info is the arch.SignalInfo cached for this context.
+ info arch.SignalInfo
+
// interrupt is the interrupt context.
interrupt interrupt.Forwarder
}
@@ -65,7 +68,7 @@ func (c *context) Switch(as platform.AddressSpace, ac arch.Context, _ int32) (*a
}
// Take the blue pill.
- si, at, err := cpu.SwitchToUser(switchOpts)
+ at, err := cpu.SwitchToUser(switchOpts, &c.info)
// Clear the address space.
cpu.active.set(nil)
@@ -75,7 +78,7 @@ func (c *context) Switch(as platform.AddressSpace, ac arch.Context, _ int32) (*a
// All done.
c.interrupt.Disable()
- return si, at, err
+ return &c.info, at, err
}
// Interrupt interrupts the running context.
diff --git a/pkg/sentry/platform/kvm/kvm_test.go b/pkg/sentry/platform/kvm/kvm_test.go
index 45eeb96ff..fff463a6e 100644
--- a/pkg/sentry/platform/kvm/kvm_test.go
+++ b/pkg/sentry/platform/kvm/kvm_test.go
@@ -156,12 +156,13 @@ func applicationTest(t testHarness, useHostMappings bool, target func(), fn func
func TestApplicationSyscall(t *testing.T) {
applicationTest(t, true, testutil.SyscallLoop, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
FullRestore: true,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
return true // Retry.
} else if err != nil {
t.Errorf("application syscall with full restore failed: %v", err)
@@ -169,11 +170,12 @@ func TestApplicationSyscall(t *testing.T) {
return false
})
applicationTest(t, true, testutil.SyscallLoop, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
return true // Retry.
} else if err != nil {
t.Errorf("application syscall with partial restore failed: %v", err)
@@ -185,27 +187,29 @@ func TestApplicationSyscall(t *testing.T) {
func TestApplicationFault(t *testing.T) {
applicationTest(t, true, testutil.Touch, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
testutil.SetTouchTarget(regs, nil) // Cause fault.
- if si, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
FullRestore: true,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
return true // Retry.
- } else if err != platform.ErrContextSignal || (si != nil && si.Signo != int32(syscall.SIGSEGV)) {
+ } else if err != platform.ErrContextSignal || si.Signo != int32(syscall.SIGSEGV) {
t.Errorf("application fault with full restore got (%v, %v), expected (%v, SIGSEGV)", err, si, platform.ErrContextSignal)
}
return false
})
applicationTest(t, true, testutil.Touch, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
testutil.SetTouchTarget(regs, nil) // Cause fault.
- if si, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
return true // Retry.
- } else if err != platform.ErrContextSignal || (si != nil && si.Signo != int32(syscall.SIGSEGV)) {
+ } else if err != platform.ErrContextSignal || si.Signo != int32(syscall.SIGSEGV) {
t.Errorf("application fault with partial restore got (%v, %v), expected (%v, SIGSEGV)", err, si, platform.ErrContextSignal)
}
return false
@@ -216,11 +220,12 @@ func TestRegistersSyscall(t *testing.T) {
applicationTest(t, true, testutil.TwiddleRegsSyscall, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
testutil.SetTestRegs(regs) // Fill values for all registers.
for {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
continue // Retry.
} else if err != nil {
t.Errorf("application register check with partial restore got unexpected error: %v", err)
@@ -238,12 +243,13 @@ func TestRegistersFault(t *testing.T) {
applicationTest(t, true, testutil.TwiddleRegsFault, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
testutil.SetTestRegs(regs) // Fill values for all registers.
for {
- if si, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
FullRestore: true,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
continue // Retry.
} else if err != platform.ErrContextSignal || si.Signo != int32(syscall.SIGSEGV) {
t.Errorf("application register check with full restore got unexpected error: %v", err)
@@ -261,12 +267,13 @@ func TestSegments(t *testing.T) {
applicationTest(t, true, testutil.TwiddleSegments, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
testutil.SetTestSegments(regs)
for {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
FullRestore: true,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
continue // Retry.
} else if err != nil {
t.Errorf("application segment check with full restore got unexpected error: %v", err)
@@ -286,11 +293,12 @@ func TestBounce(t *testing.T) {
time.Sleep(time.Millisecond)
c.BounceToKernel()
}()
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err != platform.ErrContextInterrupt {
+ }, &si); err != platform.ErrContextInterrupt {
t.Errorf("application partial restore: got %v, wanted %v", err, platform.ErrContextInterrupt)
}
return false
@@ -300,12 +308,13 @@ func TestBounce(t *testing.T) {
time.Sleep(time.Millisecond)
c.BounceToKernel()
}()
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
FullRestore: true,
- }); err != platform.ErrContextInterrupt {
+ }, &si); err != platform.ErrContextInterrupt {
t.Errorf("application full restore: got %v, wanted %v", err, platform.ErrContextInterrupt)
}
return false
@@ -331,11 +340,12 @@ func TestBounceStress(t *testing.T) {
c.BounceToKernel()
}()
randomSleep()
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err != platform.ErrContextInterrupt {
+ }, &si); err != platform.ErrContextInterrupt {
t.Errorf("application partial restore: got %v, wanted %v", err, platform.ErrContextInterrupt)
}
c.unlock()
@@ -351,11 +361,12 @@ func TestInvalidate(t *testing.T) {
applicationTest(t, true, testutil.Touch, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
testutil.SetTouchTarget(regs, &data) // Read legitimate value.
for {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
continue // Retry.
} else if err != nil {
t.Errorf("application partial restore: got %v, wanted nil", err)
@@ -365,12 +376,13 @@ func TestInvalidate(t *testing.T) {
// Unmap the page containing data & invalidate.
pt.Unmap(usermem.Addr(reflect.ValueOf(&data).Pointer() & ^uintptr(usermem.PageSize-1)), usermem.PageSize)
for {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
Flush: true,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
continue // Retry.
} else if err != platform.ErrContextSignal {
t.Errorf("application partial restore: got %v, wanted %v", err, platform.ErrContextSignal)
@@ -388,27 +400,29 @@ func IsFault(err error, si *arch.SignalInfo) bool {
func TestEmptyAddressSpace(t *testing.T) {
applicationTest(t, false, testutil.SyscallLoop, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
- if si, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
return true // Retry.
- } else if !IsFault(err, si) {
+ } else if !IsFault(err, &si) {
t.Errorf("first fault with partial restore failed got %v", err)
t.Logf("registers: %#v", &regs)
}
return false
})
applicationTest(t, false, testutil.SyscallLoop, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
- if si, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
FullRestore: true,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
return true // Retry.
- } else if !IsFault(err, si) {
+ } else if !IsFault(err, &si) {
t.Errorf("first fault with full restore failed got %v", err)
t.Logf("registers: %#v", &regs)
}
@@ -459,11 +473,12 @@ func BenchmarkApplicationSyscall(b *testing.B) {
a int // Count for ErrContextInterrupt.
)
applicationTest(b, true, testutil.SyscallLoop, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
a++
return true // Ignore.
} else if err != nil {
@@ -495,11 +510,12 @@ func BenchmarkWorldSwitchToUserRoundtrip(b *testing.B) {
a int
)
applicationTest(b, true, testutil.SyscallLoop, func(c *vCPU, regs *syscall.PtraceRegs, pt *pagetables.PageTables) bool {
- if _, _, err := c.SwitchToUser(ring0.SwitchOpts{
+ var si arch.SignalInfo
+ if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: dummyFPState,
PageTables: pt,
- }); err == platform.ErrContextInterrupt {
+ }, &si); err == platform.ErrContextInterrupt {
a++
return true // Ignore.
} else if err != nil {
diff --git a/pkg/sentry/platform/kvm/machine_amd64.go b/pkg/sentry/platform/kvm/machine_amd64.go
index e0aec42b8..c03792a1b 100644
--- a/pkg/sentry/platform/kvm/machine_amd64.go
+++ b/pkg/sentry/platform/kvm/machine_amd64.go
@@ -156,19 +156,19 @@ func (c *vCPU) initArchState() error {
// nonCanonical generates a canonical address return.
//
//go:nosplit
-func nonCanonical(addr uint64, signal int32) (*arch.SignalInfo, usermem.AccessType, error) {
- info := &arch.SignalInfo{
+func nonCanonical(addr uint64, signal int32, info *arch.SignalInfo) (usermem.AccessType, error) {
+ *info = arch.SignalInfo{
Signo: signal,
Code: arch.SignalInfoKernel,
}
info.SetAddr(addr) // Include address.
- return info, usermem.NoAccess, platform.ErrContextSignal
+ return usermem.NoAccess, platform.ErrContextSignal
}
// fault generates an appropriate fault return.
//
//go:nosplit
-func (c *vCPU) fault(signal int32) (*arch.SignalInfo, usermem.AccessType, error) {
+func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (usermem.AccessType, error) {
bluepill(c) // Probably no-op, but may not be.
faultAddr := ring0.ReadCR2()
code, user := c.ErrorCode()
@@ -176,11 +176,10 @@ func (c *vCPU) fault(signal int32) (*arch.SignalInfo, usermem.AccessType, error)
// The last fault serviced by this CPU was not a user
// fault, so we can't reliably trust the faultAddr or
// the code provided here. We need to re-execute.
- return nil, usermem.NoAccess, platform.ErrContextInterrupt
- }
- info := &arch.SignalInfo{
- Signo: signal,
+ return usermem.NoAccess, platform.ErrContextInterrupt
}
+ // Reset the pointed SignalInfo.
+ *info = arch.SignalInfo{Signo: signal}
info.SetAddr(uint64(faultAddr))
accessType := usermem.AccessType{
Read: code&(1<<1) == 0,
@@ -192,20 +191,20 @@ func (c *vCPU) fault(signal int32) (*arch.SignalInfo, usermem.AccessType, error)
} else {
info.Code = 2 // SEGV_ACCERR.
}
- return info, accessType, platform.ErrContextSignal
+ return accessType, platform.ErrContextSignal
}
// SwitchToUser unpacks architectural-details.
-func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts) (*arch.SignalInfo, usermem.AccessType, error) {
+func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo) (usermem.AccessType, error) {
// Check for canonical addresses.
if regs := switchOpts.Registers; !ring0.IsCanonical(regs.Rip) {
- return nonCanonical(regs.Rip, int32(syscall.SIGSEGV))
+ return nonCanonical(regs.Rip, int32(syscall.SIGSEGV), info)
} else if !ring0.IsCanonical(regs.Rsp) {
- return nonCanonical(regs.Rsp, int32(syscall.SIGBUS))
+ return nonCanonical(regs.Rsp, int32(syscall.SIGBUS), info)
} else if !ring0.IsCanonical(regs.Fs_base) {
- return nonCanonical(regs.Fs_base, int32(syscall.SIGBUS))
+ return nonCanonical(regs.Fs_base, int32(syscall.SIGBUS), info)
} else if !ring0.IsCanonical(regs.Gs_base) {
- return nonCanonical(regs.Gs_base, int32(syscall.SIGBUS))
+ return nonCanonical(regs.Gs_base, int32(syscall.SIGBUS), info)
}
// Assign PCIDs.
@@ -231,25 +230,25 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts) (*arch.SignalInfo, user
switch vector {
case ring0.Syscall, ring0.SyscallInt80:
// Fast path: system call executed.
- return nil, usermem.NoAccess, nil
+ return usermem.NoAccess, nil
case ring0.PageFault:
- return c.fault(int32(syscall.SIGSEGV))
+ return c.fault(int32(syscall.SIGSEGV), info)
case ring0.Debug, ring0.Breakpoint:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGTRAP),
Code: 1, // TRAP_BRKPT (breakpoint).
}
info.SetAddr(switchOpts.Registers.Rip) // Include address.
- return info, usermem.AccessType{}, platform.ErrContextSignal
+ return usermem.AccessType{}, platform.ErrContextSignal
case ring0.GeneralProtectionFault,
ring0.SegmentNotPresent,
ring0.BoundRangeExceeded,
ring0.InvalidTSS,
ring0.StackSegmentFault:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGSEGV),
Code: arch.SignalInfoKernel,
}
@@ -258,52 +257,52 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts) (*arch.SignalInfo, user
// When CPUID faulting is enabled, we will generate a #GP(0) when
// userspace executes a CPUID instruction. This is handled above,
// because we need to be able to map and read user memory.
- return info, usermem.AccessType{}, platform.ErrContextSignalCPUID
+ return usermem.AccessType{}, platform.ErrContextSignalCPUID
}
- return info, usermem.AccessType{}, platform.ErrContextSignal
+ return usermem.AccessType{}, platform.ErrContextSignal
case ring0.InvalidOpcode:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGILL),
Code: 1, // ILL_ILLOPC (illegal opcode).
}
info.SetAddr(switchOpts.Registers.Rip) // Include address.
- return info, usermem.AccessType{}, platform.ErrContextSignal
+ return usermem.AccessType{}, platform.ErrContextSignal
case ring0.DivideByZero:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGFPE),
Code: 1, // FPE_INTDIV (divide by zero).
}
info.SetAddr(switchOpts.Registers.Rip) // Include address.
- return info, usermem.AccessType{}, platform.ErrContextSignal
+ return usermem.AccessType{}, platform.ErrContextSignal
case ring0.Overflow:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGFPE),
Code: 1, // FPE_INTOVF (integer overflow).
}
info.SetAddr(switchOpts.Registers.Rip) // Include address.
- return info, usermem.AccessType{}, platform.ErrContextSignal
+ return usermem.AccessType{}, platform.ErrContextSignal
case ring0.X87FloatingPointException,
ring0.SIMDFloatingPointException:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGFPE),
Code: 7, // FPE_FLTINV (invalid operation).
}
info.SetAddr(switchOpts.Registers.Rip) // Include address.
- return info, usermem.AccessType{}, platform.ErrContextSignal
+ return usermem.AccessType{}, platform.ErrContextSignal
case ring0.Vector(bounce): // ring0.VirtualizationException
- return nil, usermem.NoAccess, platform.ErrContextInterrupt
+ return usermem.NoAccess, platform.ErrContextInterrupt
case ring0.AlignmentCheck:
- info := &arch.SignalInfo{
+ *info = arch.SignalInfo{
Signo: int32(syscall.SIGBUS),
Code: 2, // BUS_ADRERR (physical address does not exist).
}
- return info, usermem.NoAccess, platform.ErrContextSignal
+ return usermem.NoAccess, platform.ErrContextSignal
case ring0.NMI:
// An NMI is generated only when a fault is not servicable by
@@ -311,7 +310,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts) (*arch.SignalInfo, user
// really not. This could happen, e.g. if some file is
// truncated (and would generate a SIGBUS) and we map it
// directly into the instance.
- return c.fault(int32(syscall.SIGBUS))
+ return c.fault(int32(syscall.SIGBUS), info)
case ring0.DeviceNotAvailable,
ring0.DoubleFault,