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:
Leadaxe 2026-08-04 15:50:08 +03:00
commit 2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions

127
pkg/hostarch/access_type.go Normal file
View 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
View 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)
}

View 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
}

View 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()
}

View 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
View 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

View 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!")
}
}

View file

@ -0,0 +1,6 @@
// automatically generated by stateify.
//go:build arm64
// +build arm64
package hostarch

View 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))
}

View file

@ -0,0 +1,3 @@
// automatically generated by stateify.
package hostarch

View 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
}

View file

@ -0,0 +1,6 @@
// automatically generated by stateify.
//go:build amd64 || 386
// +build amd64 386
package hostarch

View 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
View 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
}