mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
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
146 lines
3.8 KiB
Go
146 lines
3.8 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 (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gvisor.dev/gvisor/pkg/hostarch"
|
|
)
|
|
|
|
type virtualRegion struct {
|
|
region
|
|
accessType hostarch.AccessType
|
|
shared bool
|
|
offset uintptr
|
|
filename string
|
|
}
|
|
|
|
// mapsLine matches a single line from /proc/PID/maps.
|
|
var mapsLine = regexp.MustCompile(`([0-9a-f]+)-([0-9a-f]+) ([r-][w-][x-][sp]) ([0-9a-f]+) [0-9a-f]{2,3}:[0-9a-f]{2,} [0-9]+\s+(.*)`)
|
|
|
|
var excludeVvarRegion bool
|
|
|
|
// excludeVirtualRegion returns true if these regions should be excluded from
|
|
// the physical map. Virtual regions need to be excluded if get_user_pages will
|
|
// fail on those addresses, preventing KVM from satisfying EPT faults.
|
|
//
|
|
// This is called by the physical map functions, not applyVirtualRegions.
|
|
func excludeVirtualRegion(r virtualRegion) bool {
|
|
return excludeVvarRegion && (strings.HasPrefix(r.filename, "[vvar") || strings.HasPrefix(r.filename, "[vdso"))
|
|
}
|
|
|
|
// mmioVirtualRegion returns true if r should be included in the physical map,
|
|
// but not actually mapped in the physical address space. This ensures that
|
|
// accesses to this region cause MMIO exits.
|
|
//
|
|
// This is called by the physical map functions, not applyVirtualRegions.
|
|
func mmioVirtualRegion(r virtualRegion) bool {
|
|
return r.virtual == ioeventfdMMIO.mapping
|
|
}
|
|
|
|
// applyVirtualRegions parses the process maps file.
|
|
//
|
|
// Takes a function that's called on each virtualRegion. If function returns
|
|
// true, stops iteration.
|
|
//
|
|
// Unlike mappedRegions, these are not consistent over time.
|
|
func applyVirtualRegions(fn func(vr virtualRegion) bool) error {
|
|
// Open /proc/self/maps.
|
|
f, err := os.Open("/proc/self/maps")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Parse all entries.
|
|
r := bufio.NewReader(f)
|
|
for {
|
|
b, err := r.ReadBytes('\n')
|
|
if len(b) > 0 {
|
|
m := mapsLine.FindSubmatch(b)
|
|
if m == nil {
|
|
// This should not happen: kernel bug?
|
|
return fmt.Errorf("badly formed line: %v", string(b))
|
|
}
|
|
start, err := strconv.ParseUint(string(m[1]), 16, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("bad start address: %v", string(b))
|
|
}
|
|
end, err := strconv.ParseUint(string(m[2]), 16, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("bad end address: %v", string(b))
|
|
}
|
|
read := m[3][0] == 'r'
|
|
write := m[3][1] == 'w'
|
|
execute := m[3][2] == 'x'
|
|
shared := m[3][3] == 's'
|
|
offset, err := strconv.ParseUint(string(m[4]), 16, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("bad offset: %v", string(b))
|
|
}
|
|
if stop := fn(virtualRegion{
|
|
region: region{
|
|
virtual: uintptr(start),
|
|
length: uintptr(end - start),
|
|
},
|
|
accessType: hostarch.AccessType{
|
|
Read: read,
|
|
Write: write,
|
|
Execute: execute,
|
|
},
|
|
shared: shared,
|
|
offset: uintptr(offset),
|
|
filename: string(m[5]),
|
|
}); stop {
|
|
break
|
|
}
|
|
}
|
|
if err != nil && err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func findRegionStartingWith(name string) (region, error) {
|
|
var r region
|
|
var found bool
|
|
err := applyVirtualRegions(func(vr virtualRegion) bool {
|
|
if strings.HasPrefix(vr.filename, name) {
|
|
r = vr.region
|
|
found = true
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if err != nil {
|
|
return region{}, err
|
|
}
|
|
if !found {
|
|
return region{}, fmt.Errorf("region %s not found", name)
|
|
}
|
|
return r, nil
|
|
}
|