snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для неё не нужна. Module path github.com/sagernet/gvisor сохранён намеренно: на него опирается replace-директива суперпроекта. Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show и переносилась на новый пин копированием. SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
This commit is contained in:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
127
pkg/hostarch/access_type.go
Normal file
127
pkg/hostarch/access_type.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// 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 hostarch
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// AccessType specifies memory access types. This is used for
|
||||
// setting mapping permissions, as well as communicating faults.
|
||||
//
|
||||
// +stateify savable
|
||||
type AccessType struct {
|
||||
// Read is read access.
|
||||
Read bool
|
||||
|
||||
// Write is write access.
|
||||
Write bool
|
||||
|
||||
// Execute is executable access.
|
||||
Execute bool
|
||||
}
|
||||
|
||||
// String returns a pretty representation of access. This looks like the
|
||||
// familiar r-x, rw-, etc. and can be relied on as such.
|
||||
func (a AccessType) String() string {
|
||||
bits := [3]byte{'-', '-', '-'}
|
||||
if a.Read {
|
||||
bits[0] = 'r'
|
||||
}
|
||||
if a.Write {
|
||||
bits[1] = 'w'
|
||||
}
|
||||
if a.Execute {
|
||||
bits[2] = 'x'
|
||||
}
|
||||
return string(bits[:])
|
||||
}
|
||||
|
||||
// Any returns true iff at least one of Read, Write or Execute is true.
|
||||
func (a AccessType) Any() bool {
|
||||
return a.Read || a.Write || a.Execute
|
||||
}
|
||||
|
||||
// Prot returns the system prot (unix.PROT_READ, etc.) for this access.
|
||||
func (a AccessType) Prot() int {
|
||||
var prot int
|
||||
if a.Read {
|
||||
prot |= unix.PROT_READ
|
||||
}
|
||||
if a.Write {
|
||||
prot |= unix.PROT_WRITE
|
||||
}
|
||||
if a.Execute {
|
||||
prot |= unix.PROT_EXEC
|
||||
}
|
||||
return prot
|
||||
}
|
||||
|
||||
// SupersetOf returns true iff the access types in a are a superset of the
|
||||
// access types in other.
|
||||
func (a AccessType) SupersetOf(other AccessType) bool {
|
||||
if !a.Read && other.Read {
|
||||
return false
|
||||
}
|
||||
if !a.Write && other.Write {
|
||||
return false
|
||||
}
|
||||
if !a.Execute && other.Execute {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersect returns the access types set in both a and other.
|
||||
func (a AccessType) Intersect(other AccessType) AccessType {
|
||||
return AccessType{
|
||||
Read: a.Read && other.Read,
|
||||
Write: a.Write && other.Write,
|
||||
Execute: a.Execute && other.Execute,
|
||||
}
|
||||
}
|
||||
|
||||
// Union returns the access types set in either a or other.
|
||||
func (a AccessType) Union(other AccessType) AccessType {
|
||||
return AccessType{
|
||||
Read: a.Read || other.Read,
|
||||
Write: a.Write || other.Write,
|
||||
Execute: a.Execute || other.Execute,
|
||||
}
|
||||
}
|
||||
|
||||
// Effective returns the set of effective access types allowed by a, even if
|
||||
// some types are not explicitly allowed.
|
||||
func (a AccessType) Effective() AccessType {
|
||||
// In Linux, Write and Execute access generally imply Read access. See
|
||||
// mm/mmap.c:protection_map.
|
||||
//
|
||||
// The notable exception is get_user_pages, which only checks against
|
||||
// the original vma flags. That said, most user memory accesses do not
|
||||
// use GUP.
|
||||
if a.Write || a.Execute {
|
||||
a.Read = true
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Convenient access types.
|
||||
var (
|
||||
NoAccess = AccessType{}
|
||||
Read = AccessType{Read: true}
|
||||
Write = AccessType{Write: true}
|
||||
Execute = AccessType{Execute: true}
|
||||
ReadWrite = AccessType{Read: true, Write: true}
|
||||
ReadExecute = AccessType{Read: true, Execute: true}
|
||||
AnyAccess = AccessType{Read: true, Write: true, Execute: true}
|
||||
)
|
||||
119
pkg/hostarch/addr.go
Normal file
119
pkg/hostarch/addr.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// 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 hostarch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Addr represents an address in an unspecified address space.
|
||||
//
|
||||
// +stateify savable
|
||||
type Addr uintptr
|
||||
|
||||
// AddLength adds the given length to start and returns the result. ok is true
|
||||
// iff adding the length did not overflow the range of Addr.
|
||||
//
|
||||
// Note: This function is usually used to get the end of an address range
|
||||
// defined by its start address and length. Since the resulting end is
|
||||
// exclusive, end == 0 is technically valid, and corresponds to a range that
|
||||
// extends to the end of the address space, but ok will be false. This isn't
|
||||
// expected to ever come up in practice.
|
||||
func (v Addr) AddLength(length uint64) (end Addr, ok bool) {
|
||||
end = v + Addr(length)
|
||||
// As of this writing (Go 1.21), addrAtLeast64b is required to prevent the
|
||||
// compiler from generating a tautological `length <= MaxUint64` check on
|
||||
// 64-bit architectures.
|
||||
ok = end >= v && (addrAtLeast64b || length <= uint64(^Addr(0)))
|
||||
return
|
||||
}
|
||||
|
||||
// RoundDown is equivalent to function PageRoundDown.
|
||||
func (v Addr) RoundDown() Addr {
|
||||
return PageRoundDown(v)
|
||||
}
|
||||
|
||||
// RoundUp is equivalent to function PageRoundUp.
|
||||
func (v Addr) RoundUp() (Addr, bool) {
|
||||
return PageRoundUp(v)
|
||||
}
|
||||
|
||||
// MustRoundUp is equivalent to function MustPageRoundUp.
|
||||
func (v Addr) MustRoundUp() Addr {
|
||||
return MustPageRoundUp(v)
|
||||
}
|
||||
|
||||
// HugeRoundDown is equivalent to function HugePageRoundDown.
|
||||
func (v Addr) HugeRoundDown() Addr {
|
||||
return HugePageRoundDown(v)
|
||||
}
|
||||
|
||||
// HugeRoundUp is equivalent to function HugePageRoundUp.
|
||||
func (v Addr) HugeRoundUp() (Addr, bool) {
|
||||
return HugePageRoundUp(v)
|
||||
}
|
||||
|
||||
// MustHugeRoundUp is equivalent to function MustHugePageRoundUp.
|
||||
func (v Addr) MustHugeRoundUp() Addr {
|
||||
return MustHugePageRoundUp(v)
|
||||
}
|
||||
|
||||
// PageOffset is equivalent to function PageOffset, except that it casts the
|
||||
// result to uint64.
|
||||
func (v Addr) PageOffset() uint64 {
|
||||
return uint64(PageOffset(v))
|
||||
}
|
||||
|
||||
// IsPageAligned is equivalent to function IsPageAligned.
|
||||
func (v Addr) IsPageAligned() bool {
|
||||
return IsPageAligned(v)
|
||||
}
|
||||
|
||||
// HugePageOffset is equivalent to function HugePageOffset.
|
||||
func (v Addr) HugePageOffset() uint64 {
|
||||
return uint64(HugePageOffset(v))
|
||||
}
|
||||
|
||||
// IsHugePageAligned is equivalent to function IsHugePageAligned.
|
||||
func (v Addr) IsHugePageAligned() bool {
|
||||
return IsHugePageAligned(v)
|
||||
}
|
||||
|
||||
// AddrRange is a range of Addrs.
|
||||
//
|
||||
// type AddrRange <generated by go_generics>
|
||||
|
||||
// ToRange returns [v, v+length).
|
||||
func (v Addr) ToRange(length uint64) (AddrRange, bool) {
|
||||
end, ok := v.AddLength(length)
|
||||
return AddrRange{v, end}, ok
|
||||
}
|
||||
|
||||
// IsPageAligned returns true if ar.Start.IsPageAligned() and
|
||||
// ar.End.IsPageAligned().
|
||||
func (ar AddrRange) IsPageAligned() bool {
|
||||
return ar.Start.IsPageAligned() && ar.End.IsPageAligned()
|
||||
}
|
||||
|
||||
// IsHugePageAligned returns true if ar.Start.IsHugePageAligned() and
|
||||
// ar.End.IsHugePageAligned().
|
||||
func (ar AddrRange) IsHugePageAligned() bool {
|
||||
return ar.Start.IsHugePageAligned() && ar.End.IsHugePageAligned()
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.String.
|
||||
func (ar AddrRange) String() string {
|
||||
return fmt.Sprintf("[%#x, %#x)", ar.Start, ar.End)
|
||||
}
|
||||
76
pkg/hostarch/addr_range.go
Normal file
76
pkg/hostarch/addr_range.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package hostarch
|
||||
|
||||
// A Range represents a contiguous range of T.
|
||||
//
|
||||
// +stateify savable
|
||||
type AddrRange struct {
|
||||
// Start is the inclusive start of the range.
|
||||
Start Addr
|
||||
|
||||
// End is the exclusive end of the range.
|
||||
End Addr
|
||||
}
|
||||
|
||||
// WellFormed returns true if r.Start <= r.End. All other methods on a Range
|
||||
// require that the Range is well-formed.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) WellFormed() bool {
|
||||
return r.Start <= r.End
|
||||
}
|
||||
|
||||
// Length returns the length of the range.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) Length() Addr {
|
||||
return r.End - r.Start
|
||||
}
|
||||
|
||||
// Contains returns true if r contains x.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) Contains(x Addr) bool {
|
||||
return r.Start <= x && x < r.End
|
||||
}
|
||||
|
||||
// Overlaps returns true if r and r2 overlap.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) Overlaps(r2 AddrRange) bool {
|
||||
return r.Start < r2.End && r2.Start < r.End
|
||||
}
|
||||
|
||||
// IsSupersetOf returns true if r is a superset of r2; that is, the range r2 is
|
||||
// contained within r.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) IsSupersetOf(r2 AddrRange) bool {
|
||||
return r.Start <= r2.Start && r.End >= r2.End
|
||||
}
|
||||
|
||||
// Intersect returns a range consisting of the intersection between r and r2.
|
||||
// If r and r2 do not overlap, Intersect returns a range with unspecified
|
||||
// bounds, but for which Length() == 0.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) Intersect(r2 AddrRange) AddrRange {
|
||||
if r.Start < r2.Start {
|
||||
r.Start = r2.Start
|
||||
}
|
||||
if r.End > r2.End {
|
||||
r.End = r2.End
|
||||
}
|
||||
if r.End < r.Start {
|
||||
r.End = r.Start
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// CanSplitAt returns true if it is legal to split a segment spanning the range
|
||||
// r at x; that is, splitting at x would produce two ranges, both of which have
|
||||
// non-zero length.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r AddrRange) CanSplitAt(x Addr) bool {
|
||||
return r.Contains(x) && r.Start < x
|
||||
}
|
||||
277
pkg/hostarch/addr_range_seq_unsafe.go
Normal file
277
pkg/hostarch/addr_range_seq_unsafe.go
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
// 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 hostarch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/gohacks"
|
||||
)
|
||||
|
||||
// An AddrRangeSeq represents a sequence of AddrRanges.
|
||||
//
|
||||
// AddrRangeSeqs are immutable and may be copied by value. The zero value of
|
||||
// AddrRangeSeq represents an empty sequence.
|
||||
//
|
||||
// An AddrRangeSeq may contain AddrRanges with a length of 0. This is necessary
|
||||
// since zero-length AddrRanges are significant to MM bounds checks.
|
||||
type AddrRangeSeq struct {
|
||||
// If length is 0, then the AddrRangeSeq represents no AddrRanges.
|
||||
// Invariants: data == 0; offset == 0; limit == 0.
|
||||
//
|
||||
// If length is 1, then the AddrRangeSeq represents the single
|
||||
// AddrRange{offset, offset+limit}. Invariants: data == 0.
|
||||
//
|
||||
// Otherwise, length >= 2, and the AddrRangeSeq represents the `length`
|
||||
// AddrRanges in the array of AddrRanges starting at address `data`,
|
||||
// starting at `offset` bytes into the first AddrRange and limited to the
|
||||
// following `limit` bytes. (AddrRanges after `limit` are still iterated,
|
||||
// but are truncated to a length of 0.) Invariants: data != 0; offset <=
|
||||
// data[0].Length(); limit > 0; offset+limit <= the combined length of all
|
||||
// AddrRanges in the array.
|
||||
data unsafe.Pointer
|
||||
length int
|
||||
offset Addr
|
||||
limit Addr
|
||||
}
|
||||
|
||||
// AddrRangeSeqOf returns an AddrRangeSeq representing the single AddrRange ar.
|
||||
func AddrRangeSeqOf(ar AddrRange) AddrRangeSeq {
|
||||
return AddrRangeSeq{
|
||||
length: 1,
|
||||
offset: ar.Start,
|
||||
limit: ar.Length(),
|
||||
}
|
||||
}
|
||||
|
||||
// AddrRangeSeqFromSlice returns an AddrRangeSeq representing all AddrRanges in
|
||||
// slice.
|
||||
//
|
||||
// Whether the returned AddrRangeSeq shares memory with slice is unspecified;
|
||||
// clients should avoid mutating slices passed to AddrRangeSeqFromSlice.
|
||||
//
|
||||
// Preconditions: The combined length of all AddrRanges in slice <=
|
||||
// math.MaxInt64.
|
||||
func AddrRangeSeqFromSlice(slice []AddrRange) AddrRangeSeq {
|
||||
var limit int64
|
||||
for _, ar := range slice {
|
||||
len64 := int64(ar.Length())
|
||||
if len64 < 0 {
|
||||
panic(fmt.Sprintf("Length of AddrRange %v overflows int64", ar))
|
||||
}
|
||||
sum := limit + len64
|
||||
if sum < limit {
|
||||
panic(fmt.Sprintf("Total length of AddrRanges %v overflows int64", slice))
|
||||
}
|
||||
limit = sum
|
||||
}
|
||||
return addrRangeSeqFromSliceLimited(slice, limit)
|
||||
}
|
||||
|
||||
// Preconditions:
|
||||
// - The combined length of all AddrRanges in slice <= limit.
|
||||
// - limit >= 0.
|
||||
// - If len(slice) != 0, then limit > 0.
|
||||
func addrRangeSeqFromSliceLimited(slice []AddrRange, limit int64) AddrRangeSeq {
|
||||
switch len(slice) {
|
||||
case 0:
|
||||
return AddrRangeSeq{}
|
||||
case 1:
|
||||
return AddrRangeSeq{
|
||||
length: 1,
|
||||
offset: slice[0].Start,
|
||||
limit: Addr(limit),
|
||||
}
|
||||
default:
|
||||
return AddrRangeSeq{
|
||||
data: unsafe.Pointer(&slice[0]),
|
||||
length: len(slice),
|
||||
limit: Addr(limit),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if ars.NumRanges() == 0.
|
||||
//
|
||||
// Note that since AddrRangeSeq may contain AddrRanges with a length of zero,
|
||||
// an AddrRange representing 0 bytes (AddrRangeSeq.NumBytes() == 0) is not
|
||||
// necessarily empty.
|
||||
func (ars AddrRangeSeq) IsEmpty() bool {
|
||||
return ars.length == 0
|
||||
}
|
||||
|
||||
// NumRanges returns the number of AddrRanges in ars.
|
||||
func (ars AddrRangeSeq) NumRanges() int {
|
||||
return ars.length
|
||||
}
|
||||
|
||||
// NumBytes returns the number of bytes represented by ars.
|
||||
func (ars AddrRangeSeq) NumBytes() int64 {
|
||||
return int64(ars.limit)
|
||||
}
|
||||
|
||||
// Head returns the first AddrRange in ars.
|
||||
//
|
||||
// Preconditions: !ars.IsEmpty().
|
||||
func (ars AddrRangeSeq) Head() AddrRange {
|
||||
if ars.length == 0 {
|
||||
panic("empty AddrRangeSeq")
|
||||
}
|
||||
if ars.length == 1 {
|
||||
return AddrRange{ars.offset, ars.offset + ars.limit}
|
||||
}
|
||||
ar := *(*AddrRange)(ars.data)
|
||||
ar.Start += ars.offset
|
||||
if ar.Length() > ars.limit {
|
||||
ar.End = ar.Start + ars.limit
|
||||
}
|
||||
return ar
|
||||
}
|
||||
|
||||
// Tail returns an AddrRangeSeq consisting of all AddrRanges in ars after the
|
||||
// first.
|
||||
//
|
||||
// Preconditions: !ars.IsEmpty().
|
||||
func (ars AddrRangeSeq) Tail() AddrRangeSeq {
|
||||
if ars.length == 0 {
|
||||
panic("empty AddrRangeSeq")
|
||||
}
|
||||
if ars.length == 1 {
|
||||
return AddrRangeSeq{}
|
||||
}
|
||||
return ars.externalTail()
|
||||
}
|
||||
|
||||
// Preconditions: ars.length >= 2.
|
||||
func (ars AddrRangeSeq) externalTail() AddrRangeSeq {
|
||||
data := (*AddrRange)(ars.data)
|
||||
headLen := data.Length() - ars.offset
|
||||
var tailLimit int64
|
||||
if ars.limit > headLen {
|
||||
tailLimit = int64(ars.limit - headLen)
|
||||
}
|
||||
extSlice := gohacks.Slice(data, ars.length)
|
||||
return addrRangeSeqFromSliceLimited(extSlice[1:], tailLimit)
|
||||
}
|
||||
|
||||
// DropFirst returns an AddrRangeSeq equivalent to ars, but with the first n
|
||||
// bytes omitted. If n > ars.NumBytes(), DropFirst returns an empty
|
||||
// AddrRangeSeq.
|
||||
//
|
||||
// If !ars.IsEmpty() and ars.Head().Length() == 0, DropFirst will always omit
|
||||
// at least ars.Head(), even if n == 0. This guarantees that the basic pattern
|
||||
// of:
|
||||
//
|
||||
// for !ars.IsEmpty() {
|
||||
// n, err = doIOWith(ars.Head())
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// ars = ars.DropFirst(n)
|
||||
// }
|
||||
//
|
||||
// works even in the presence of zero-length AddrRanges.
|
||||
//
|
||||
// Preconditions: n >= 0.
|
||||
func (ars AddrRangeSeq) DropFirst(n int) AddrRangeSeq {
|
||||
if n < 0 {
|
||||
panic(fmt.Sprintf("invalid n: %d", n))
|
||||
}
|
||||
return ars.DropFirst64(int64(n))
|
||||
}
|
||||
|
||||
// DropFirst64 is equivalent to DropFirst but takes an int64.
|
||||
func (ars AddrRangeSeq) DropFirst64(n int64) AddrRangeSeq {
|
||||
if n < 0 {
|
||||
panic(fmt.Sprintf("invalid n: %d", n))
|
||||
}
|
||||
if Addr(n) > ars.limit {
|
||||
return AddrRangeSeq{}
|
||||
}
|
||||
// Handle initial empty AddrRange.
|
||||
switch ars.length {
|
||||
case 0:
|
||||
return AddrRangeSeq{}
|
||||
case 1:
|
||||
if ars.limit == 0 {
|
||||
return AddrRangeSeq{}
|
||||
}
|
||||
default:
|
||||
if rawHeadLen := (*AddrRange)(ars.data).Length(); ars.offset == rawHeadLen {
|
||||
ars = ars.externalTail()
|
||||
}
|
||||
}
|
||||
for n != 0 {
|
||||
// Calling ars.Head() here is surprisingly expensive, so inline getting
|
||||
// the head's length.
|
||||
var headLen Addr
|
||||
if ars.length == 1 {
|
||||
headLen = ars.limit
|
||||
} else {
|
||||
headLen = (*AddrRange)(ars.data).Length() - ars.offset
|
||||
}
|
||||
if Addr(n) < headLen {
|
||||
// Dropping ends partway through the head AddrRange.
|
||||
ars.offset += Addr(n)
|
||||
ars.limit -= Addr(n)
|
||||
return ars
|
||||
}
|
||||
n -= int64(headLen)
|
||||
ars = ars.Tail()
|
||||
}
|
||||
return ars
|
||||
}
|
||||
|
||||
// TakeFirst returns an AddrRangeSeq equivalent to ars, but iterating at most n
|
||||
// bytes. TakeFirst never removes AddrRanges from ars; AddrRanges beyond the
|
||||
// first n bytes are reduced to a length of zero, but will still be iterated.
|
||||
//
|
||||
// Preconditions: n >= 0.
|
||||
func (ars AddrRangeSeq) TakeFirst(n int) AddrRangeSeq {
|
||||
if n < 0 {
|
||||
panic(fmt.Sprintf("invalid n: %d", n))
|
||||
}
|
||||
return ars.TakeFirst64(int64(n))
|
||||
}
|
||||
|
||||
// TakeFirst64 is equivalent to TakeFirst but takes an int64.
|
||||
func (ars AddrRangeSeq) TakeFirst64(n int64) AddrRangeSeq {
|
||||
if n < 0 {
|
||||
panic(fmt.Sprintf("invalid n: %d", n))
|
||||
}
|
||||
if ars.limit > Addr(n) {
|
||||
ars.limit = Addr(n)
|
||||
}
|
||||
return ars
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.String.
|
||||
func (ars AddrRangeSeq) String() string {
|
||||
// This is deliberately chosen to be the same as fmt's automatic stringer
|
||||
// for []AddrRange.
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('[')
|
||||
var sep string
|
||||
for !ars.IsEmpty() {
|
||||
buf.WriteString(sep)
|
||||
sep = " "
|
||||
buf.WriteString(ars.Head().String())
|
||||
ars = ars.Tail()
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
}
|
||||
22
pkg/hostarch/addr_unsafe.go
Normal file
22
pkg/hostarch/addr_unsafe.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2023 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 hostarch
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This is used in addr.go:Addr.AddLength().
|
||||
const addrAtLeast64b = unsafe.Sizeof(Addr(0)) >= 8
|
||||
8
pkg/hostarch/hostarch.go
Normal file
8
pkg/hostarch/hostarch.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// Package hostarch contains host arch address operations for user memory.
|
||||
package hostarch
|
||||
98
pkg/hostarch/hostarch_arm64.go
Normal file
98
pkg/hostarch/hostarch_arm64.go
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2019 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.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package hostarch
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// PageSize is the system page size.
|
||||
// arm64 support 4K/16K/64K page size,
|
||||
// which can be get by unix.Getpagesize().
|
||||
// Currently, only 4K page size is supported.
|
||||
PageSize = 1 << PageShift
|
||||
|
||||
// HugePageSize is the system huge page size.
|
||||
HugePageSize = 1 << HugePageShift
|
||||
|
||||
// CacheLineSize is the size of the cache line.
|
||||
CacheLineSize = 1 << CacheLineShift
|
||||
|
||||
// PageShift is the binary log of the system page size.
|
||||
PageShift = 12
|
||||
|
||||
// HugePageShift is the binary log of the system huge page size.
|
||||
// Should be calculated by "PageShift + (PageShift - 3)"
|
||||
// when multiple page size support is ready.
|
||||
HugePageShift = 21
|
||||
|
||||
// CacheLineShift is the binary log of the cache line size.
|
||||
CacheLineShift = 6
|
||||
)
|
||||
|
||||
// ByteOrder is the native byte order (little endian).
|
||||
var ByteOrder = binary.LittleEndian
|
||||
|
||||
// Arm64: Exception Syndrome Register EL1.
|
||||
const (
|
||||
_ESR_ELx_EC_SHIFT = 26
|
||||
_ESR_ELx_EC_MASK = 0x3F << _ESR_ELx_EC_SHIFT
|
||||
|
||||
_ESR_ELx_EC_IABT_LOW = 0x20
|
||||
_ESR_ELx_EC_DABT_LOW = 0x24
|
||||
|
||||
_ESR_ELx_WNR = 1 << 6
|
||||
_ESR_ELx_CM = 1 << 8
|
||||
)
|
||||
|
||||
// ESRAccessType returns the memory access type for the given ESR (Exception
|
||||
// Syndrome Register) code. If code does not represent an invalid memory
|
||||
// access from a lower exception level, ESRAccessType returns NoAccess.
|
||||
//
|
||||
//go:nosplit
|
||||
func ESRAccessType(code uint64) AccessType {
|
||||
switch (code & _ESR_ELx_EC_MASK) >> _ESR_ELx_EC_SHIFT {
|
||||
case _ESR_ELx_EC_IABT_LOW:
|
||||
return Execute
|
||||
case _ESR_ELx_EC_DABT_LOW:
|
||||
// For faults on cache maintenance and address translation
|
||||
// instructions, _ESR_ELx_WNR is always set.
|
||||
if code&(_ESR_ELx_WNR|_ESR_ELx_CM) == _ESR_ELx_WNR {
|
||||
return Write
|
||||
}
|
||||
return Read
|
||||
default:
|
||||
return NoAccess
|
||||
}
|
||||
}
|
||||
|
||||
// UntaggedUserAddr clears the tag from the address pointer. Top-Byte-Ignore (TBI0)
|
||||
// is enabled in Linux, so bits[63:56] of user space addresses are ignored.
|
||||
func UntaggedUserAddr(addr Addr) Addr {
|
||||
return Addr(int64(addr<<8) >> 8)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Make sure the page size is 4K on arm64 platform.
|
||||
if size := unix.Getpagesize(); size != PageSize {
|
||||
panic("Only 4K page size is supported on arm64!")
|
||||
}
|
||||
}
|
||||
6
pkg/hostarch/hostarch_arm64_state_autogen.go
Normal file
6
pkg/hostarch/hostarch_arm64_state_autogen.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package hostarch
|
||||
82
pkg/hostarch/hostarch_state_autogen.go
Normal file
82
pkg/hostarch/hostarch_state_autogen.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package hostarch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (a *AccessType) StateTypeName() string {
|
||||
return "pkg/hostarch.AccessType"
|
||||
}
|
||||
|
||||
func (a *AccessType) StateFields() []string {
|
||||
return []string{
|
||||
"Read",
|
||||
"Write",
|
||||
"Execute",
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AccessType) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (a *AccessType) StateSave(stateSinkObject state.Sink) {
|
||||
a.beforeSave()
|
||||
stateSinkObject.Save(0, &a.Read)
|
||||
stateSinkObject.Save(1, &a.Write)
|
||||
stateSinkObject.Save(2, &a.Execute)
|
||||
}
|
||||
|
||||
func (a *AccessType) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (a *AccessType) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &a.Read)
|
||||
stateSourceObject.Load(1, &a.Write)
|
||||
stateSourceObject.Load(2, &a.Execute)
|
||||
}
|
||||
|
||||
func (v *Addr) StateTypeName() string {
|
||||
return "pkg/hostarch.Addr"
|
||||
}
|
||||
|
||||
func (v *Addr) StateFields() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AddrRange) StateTypeName() string {
|
||||
return "pkg/hostarch.AddrRange"
|
||||
}
|
||||
|
||||
func (r *AddrRange) StateFields() []string {
|
||||
return []string{
|
||||
"Start",
|
||||
"End",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *AddrRange) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *AddrRange) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.Start)
|
||||
stateSinkObject.Save(1, &r.End)
|
||||
}
|
||||
|
||||
func (r *AddrRange) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *AddrRange) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.Start)
|
||||
stateSourceObject.Load(1, &r.End)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*AccessType)(nil))
|
||||
state.Register((*Addr)(nil))
|
||||
state.Register((*AddrRange)(nil))
|
||||
}
|
||||
3
pkg/hostarch/hostarch_unsafe_state_autogen.go
Normal file
3
pkg/hostarch/hostarch_unsafe_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package hostarch
|
||||
48
pkg/hostarch/hostarch_x86.go
Normal file
48
pkg/hostarch/hostarch_x86.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// 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.
|
||||
|
||||
//go:build amd64 || 386
|
||||
// +build amd64 386
|
||||
|
||||
package hostarch
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
// PageSize is the system page size.
|
||||
PageSize = 1 << PageShift
|
||||
|
||||
// HugePageSize is the system huge page size.
|
||||
HugePageSize = 1 << HugePageShift
|
||||
|
||||
// CacheLineSize is the size of the cache line.
|
||||
CacheLineSize = 1 << CacheLineShift
|
||||
|
||||
// PageShift is the binary log of the system page size.
|
||||
PageShift = 12
|
||||
|
||||
// HugePageShift is the binary log of the system huge page size.
|
||||
HugePageShift = 21
|
||||
|
||||
// CacheLineShift is the binary log of the cache line size.
|
||||
CacheLineShift = 6
|
||||
)
|
||||
|
||||
// ByteOrder is the native byte order (little endian).
|
||||
var ByteOrder = binary.LittleEndian
|
||||
|
||||
// UntaggedUserAddr is no-op on x86.
|
||||
func UntaggedUserAddr(addr Addr) Addr {
|
||||
return addr
|
||||
}
|
||||
6
pkg/hostarch/hostarch_x86_state_autogen.go
Normal file
6
pkg/hostarch/hostarch_x86_state_autogen.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
//go:build amd64 || 386
|
||||
// +build amd64 386
|
||||
|
||||
package hostarch
|
||||
84
pkg/hostarch/memory_type.go
Normal file
84
pkg/hostarch/memory_type.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2025 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 hostarch
|
||||
|
||||
import "fmt"
|
||||
|
||||
// MemoryType specifies CPU memory access behavior.
|
||||
type MemoryType uint8
|
||||
|
||||
const (
|
||||
// MemoryTypeWriteBack is equivalent to Linux's default pgprot, or the
|
||||
// following architectural memory types:
|
||||
//
|
||||
// - x86: Write-back (WB)
|
||||
//
|
||||
// - ARM64: Normal write-back cacheable
|
||||
//
|
||||
// This memory type is appropriate for typical application memory and must
|
||||
// be the zero value for MemoryType.
|
||||
MemoryTypeWriteBack MemoryType = iota
|
||||
|
||||
// MemoryTypeWriteCombine is equivalent to Linux's pgprot_writecombine(),
|
||||
// or the following architectural memory types:
|
||||
//
|
||||
// - x86: Write-combining (WC)
|
||||
//
|
||||
// - ARM64: Normal non-cacheable
|
||||
MemoryTypeWriteCombine
|
||||
|
||||
// MemoryTypeUncached is equivalent to Linux's pgprot_noncached(), or the
|
||||
// following architectural memory types:
|
||||
//
|
||||
// - x86: Strong Uncacheable (UC) or Uncacheable (UC-); these differ in
|
||||
// that UC- may be "downgraded" to WC by a setting of WC or (Intel only) WP
|
||||
// in MTRR or EPT/NPT, but gVisor does not use MTRRs and KVM never sets WC
|
||||
// or WP in EPT/NPT.
|
||||
//
|
||||
// - ARM64: Device-nGnRnE
|
||||
MemoryTypeUncached
|
||||
|
||||
// NumMemoryTypes is the number of memory types.
|
||||
NumMemoryTypes
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.String.
|
||||
func (mt MemoryType) String() string {
|
||||
switch mt {
|
||||
case MemoryTypeWriteBack:
|
||||
return "WriteBack"
|
||||
case MemoryTypeWriteCombine:
|
||||
return "WriteCombine"
|
||||
case MemoryTypeUncached:
|
||||
return "Uncached"
|
||||
default:
|
||||
return fmt.Sprintf("%d", mt)
|
||||
}
|
||||
}
|
||||
|
||||
// ShortString returns a two-character string compactly representing the
|
||||
// MemoryType.
|
||||
func (mt MemoryType) ShortString() string {
|
||||
switch mt {
|
||||
case MemoryTypeWriteBack:
|
||||
return "WB"
|
||||
case MemoryTypeWriteCombine:
|
||||
return "WC"
|
||||
case MemoryTypeUncached:
|
||||
return "UC"
|
||||
default:
|
||||
return fmt.Sprintf("%02d", mt)
|
||||
}
|
||||
}
|
||||
114
pkg/hostarch/sizes_util.go
Normal file
114
pkg/hostarch/sizes_util.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
package hostarch
|
||||
|
||||
// Masks often used when working with alignment in constant expressions.
|
||||
const (
|
||||
PageMask = PageSize - 1
|
||||
HugePageMask = HugePageSize - 1
|
||||
CacheLineMask = CacheLineSize - 1
|
||||
)
|
||||
|
||||
type bytecount interface {
|
||||
~uint | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
type hugebytecount interface {
|
||||
~uint | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// PageRoundDown returns x rounded down to the nearest multiple of PageSize.
|
||||
func PageRoundDown[T bytecount](x T) T {
|
||||
return x &^ PageMask
|
||||
}
|
||||
|
||||
// PageRoundUp returns x rounded up to the nearest multiple of PageSize. ok is
|
||||
// true iff rounding up does not overflow the range of T.
|
||||
func PageRoundUp[T bytecount](x T) (val T, ok bool) {
|
||||
val = PageRoundDown(x + PageMask)
|
||||
ok = val >= x
|
||||
return
|
||||
}
|
||||
|
||||
// MustPageRoundUp is equivalent to PageRoundUp, but panics if rounding up
|
||||
// overflows.
|
||||
func MustPageRoundUp[T bytecount](x T) T {
|
||||
val, ok := PageRoundUp(x)
|
||||
if !ok {
|
||||
panic("PageRoundUp overflows")
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// PageOffset returns the offset of x into its containing page.
|
||||
func PageOffset[T bytecount](x T) T {
|
||||
return x & PageMask
|
||||
}
|
||||
|
||||
// IsPageAligned returns true if x is a multiple of PageSize.
|
||||
func IsPageAligned[T bytecount](x T) bool {
|
||||
return PageOffset(x) == 0
|
||||
}
|
||||
|
||||
// ToPagesRoundUp returns (the number of pages equal to x bytes rounded up,
|
||||
// true). If rounding x up to a multiple of PageSize overflows the range of T,
|
||||
// ToPagesRoundUp returns (unspecified, false).
|
||||
func ToPagesRoundUp[T bytecount](x T) (T, bool) {
|
||||
y := x + PageMask
|
||||
if y < x {
|
||||
return x, false
|
||||
}
|
||||
return y / PageSize, true
|
||||
}
|
||||
|
||||
// HugePageRoundDown returns x rounded down to the nearest multiple of
|
||||
// HugePageSize.
|
||||
func HugePageRoundDown[T hugebytecount](x T) T {
|
||||
return x &^ HugePageMask
|
||||
}
|
||||
|
||||
// HugePageRoundUp returns x rounded up to the nearest multiple of
|
||||
// HugePageSize. ok is true iff rounding up does not overflow the range of T.
|
||||
func HugePageRoundUp[T hugebytecount](x T) (val T, ok bool) {
|
||||
val = HugePageRoundDown(x + HugePageMask)
|
||||
ok = val >= x
|
||||
return
|
||||
}
|
||||
|
||||
// MustHugePageRoundUp is equivalent to HugePageRoundUp, but panics if rounding
|
||||
// up overflows.
|
||||
func MustHugePageRoundUp[T hugebytecount](x T) T {
|
||||
val, ok := HugePageRoundUp(x)
|
||||
if !ok {
|
||||
panic("HugePageRoundUp overflows")
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// HugePageOffset returns the offset of x into its containing page.
|
||||
func HugePageOffset[T hugebytecount](x T) T {
|
||||
return x & HugePageMask
|
||||
}
|
||||
|
||||
// IsHugePageAligned returns true if x is a multiple of HugePageSize.
|
||||
func IsHugePageAligned[T hugebytecount](x T) bool {
|
||||
return HugePageOffset(x) == 0
|
||||
}
|
||||
|
||||
// CacheLineRoundDown returns the offset rounded down to the nearest multiple
|
||||
// of CacheLineSize.
|
||||
func CacheLineRoundDown[T bytecount](x T) T {
|
||||
return x &^ CacheLineMask
|
||||
}
|
||||
|
||||
// CacheLineRoundUp returns the offset rounded up to the nearest multiple of
|
||||
// CacheLineSize. ok is true iff rounding up does not overflow the range of T.
|
||||
func CacheLineRoundUp[T bytecount](x T) (val T, ok bool) {
|
||||
val = CacheLineRoundDown(x + CacheLineMask)
|
||||
ok = val >= x
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue