Files
visor/pkg/sentry/platform/kvm/virtual_map_test.go
Konstantin BogomolovandgVisor bot f1a883bdde Detect if vvar is not mappable into the guest on ARM.
This adds a check if vvar cannot be mapped into the guest in a throw-away VM on
potentially problematic host kernels.

Linux commits 2a8dfab and 0c67288 make our mapping of vvar into the guest fail
as soon as we try to create it, because it is a VM_PFNMAP region.

Not mapping vvar into the guest severely hinders gVisor performance, because if
we don't map vvar then any time-related host syscall will always force a VM
exit, for example. However, given the alternative if/until a backport is merged
into the affected kernels is to just crash, we don't have much choice.

Note that this also revealed two errors:
- in KERNEL_ENTRY_FROM_EL1, which assumed R18 would not be relevant because
  Go never uses it -- this is no longer the case with the sentry faulting while
  in the VDSO code.
- in the interrupt stack size which was sized too small and thus corrupted
  errorCode and errorType variables when it overflowed. This is a problem with
  this patch specifically, because we now generate a data abort exception every
  time we use vvar, which exposes the bug more frequently.

  This also led to a closer inspection of the EXCEPTION_EL1 macro, which
  revealed a relatively harmless bug of vector not being saved properly.

PiperOrigin-RevId: 968804355
2026-08-21 18:23:37 -07:00

96 lines
2.6 KiB
Go

// Copyright 2018 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.
package kvm
import (
"testing"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/hostarch"
)
type checker struct {
ok bool
accessType hostarch.AccessType
}
func (c *checker) Containing(addr uintptr) func(virtualRegion) bool {
c.ok = false // Reset for below calls.
return func(vr virtualRegion) bool {
if vr.virtual <= addr && addr < vr.virtual+vr.length {
c.ok = true
c.accessType = vr.accessType
return true
}
return false
}
}
func TestParseMaps(t *testing.T) {
c := new(checker)
// Simple test.
if err := applyVirtualRegions(c.Containing(0)); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// MMap a new page.
addr, _, errno := unix.RawSyscall6(
unix.SYS_MMAP, 0, hostarch.PageSize,
unix.PROT_READ|unix.PROT_WRITE,
unix.MAP_ANONYMOUS|unix.MAP_PRIVATE, 0, 0)
if errno != 0 {
t.Fatalf("unexpected map error: %v", errno)
}
// Re-parse maps.
if err := applyVirtualRegions(c.Containing(addr)); err != nil {
unix.RawSyscall(unix.SYS_MUNMAP, addr, hostarch.PageSize, 0)
t.Fatalf("unexpected error: %v", err)
}
// Assert that it now does contain the region.
if !c.ok {
unix.RawSyscall(unix.SYS_MUNMAP, addr, hostarch.PageSize, 0)
t.Fatalf("updated map does not contain 0x%08x, expected true", addr)
}
// Map the region as PROT_NONE.
newAddr, _, errno := unix.RawSyscall6(
unix.SYS_MMAP, addr, hostarch.PageSize,
unix.PROT_NONE,
unix.MAP_ANONYMOUS|unix.MAP_FIXED|unix.MAP_PRIVATE, 0, 0)
if errno != 0 {
t.Fatalf("unexpected map error: %v", errno)
}
if newAddr != addr {
t.Fatalf("unable to remap address: got 0x%08x, wanted 0x%08x", newAddr, addr)
}
// Re-parse maps.
if err := applyVirtualRegions(c.Containing(addr)); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !c.ok {
t.Fatalf("final map does not contain 0x%08x, expected true", addr)
}
if c.accessType.Read || c.accessType.Write || c.accessType.Execute {
t.Fatalf("final map has incorrect permissions for 0x%08x", addr)
}
// Unmap the region.
unix.RawSyscall(unix.SYS_MUNMAP, addr, hostarch.PageSize, 0)
}