diff options
Diffstat (limited to 'pkg/sentry/platform')
-rw-r--r-- | pkg/sentry/platform/kvm/address_space.go | 24 | ||||
-rw-r--r-- | pkg/sentry/platform/kvm/machine_amd64.go | 23 | ||||
-rw-r--r-- | pkg/sentry/platform/ring0/defs_amd64.go | 8 |
3 files changed, 49 insertions, 6 deletions
diff --git a/pkg/sentry/platform/kvm/address_space.go b/pkg/sentry/platform/kvm/address_space.go index c2f4559a0..f74c98dd0 100644 --- a/pkg/sentry/platform/kvm/address_space.go +++ b/pkg/sentry/platform/kvm/address_space.go @@ -102,11 +102,18 @@ func (as *addressSpace) mapHost(addr usermem.Addr, m hostMapEntry, at usermem.Ac // important; if the pagetable mappings were installed before // ensuring the physical pages were available, then some other // thread could theoretically access them. - prev := as.pageTables.Map(addr, length, pagetables.MapOpts{ - AccessType: at, - User: true, - }, physical) - inv = inv || prev + // + // Due to the way KVM's shadow paging implementation works, + // modifications to the page tables while in host mode may not + // be trapped, leading to the shadow pages being out of sync. + // Therefore, we need to ensure that we are in guest mode for + // page table modifications. See the call to bluepill, below. + as.machine.retryInGuest(func() { + inv = as.pageTables.Map(addr, length, pagetables.MapOpts{ + AccessType: at, + User: true, + }, physical) || inv + }) m.addr += length m.length -= length addr += usermem.Addr(length) @@ -214,7 +221,12 @@ func (as *addressSpace) Unmap(addr usermem.Addr, length uint64) { as.mu.Lock() defer as.mu.Unlock() - if prev := as.pageTables.Unmap(addr, uintptr(length)); prev { + // See above re: retryInGuest. + var prev bool + as.machine.retryInGuest(func() { + prev = as.pageTables.Unmap(addr, uintptr(length)) || prev + }) + if prev { as.invalidate() as.files.DeleteMapping(usermem.AddrRange{ Start: addr, diff --git a/pkg/sentry/platform/kvm/machine_amd64.go b/pkg/sentry/platform/kvm/machine_amd64.go index 6afae5cae..7fcb7451f 100644 --- a/pkg/sentry/platform/kvm/machine_amd64.go +++ b/pkg/sentry/platform/kvm/machine_amd64.go @@ -227,3 +227,26 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts) (*arch.SignalInfo, user panic(fmt.Sprintf("unexpected vector: 0x%x", vector)) } } + +// retryInGuest runs the given function in guest mode. +// +// If the function does not complete in guest mode (due to execution of a +// system call due to a GC stall, for example), then it will be retried. The +// given function must be idempotent as a result of the retry mechanism. +func (m *machine) retryInGuest(fn func()) { + c := m.Get() + defer m.Put(c) + for { + c.ClearErrorCode() // See below. + bluepill(c) // Force guest mode. + fn() // Execute the given function. + _, user := c.ErrorCode() + if user { + // If user is set, then we haven't bailed back to host + // mode via a kernel exception or system call. We + // consider the full function to have executed in guest + // mode and we can return. + break + } + } +} diff --git a/pkg/sentry/platform/ring0/defs_amd64.go b/pkg/sentry/platform/ring0/defs_amd64.go index 0d068c00a..84819f132 100644 --- a/pkg/sentry/platform/ring0/defs_amd64.go +++ b/pkg/sentry/platform/ring0/defs_amd64.go @@ -104,6 +104,14 @@ func (c *CPU) ErrorCode() (value uintptr, user bool) { return c.errorCode, c.errorType != 0 } +// ClearErrorCode resets the error code. +// +//go:nosplit +func (c *CPU) ClearErrorCode() { + c.errorCode = 0 // No code. + c.errorType = 1 // User mode. +} + // SwitchArchOpts are embedded in SwitchOpts. type SwitchArchOpts struct { // UserPCID indicates that the application PCID to be used on switch, |