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

496
pkg/cpuid/cpuid_amd64.go Normal file
View file

@ -0,0 +1,496 @@
// 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 amd64
// +build amd64
package cpuid
import (
"context"
"fmt"
"io"
)
// FeatureSet defines features in terms of CPUID leaves and bits.
// The kernel also exposes the presence of features to userspace through
// a set of flags(HWCAP/HWCAP2) bits, exposed in the auxiliary vector, which
// are necessary to read for some features (e.g. FSGSBASE).
//
// Common references:
//
// Intel:
// - Intel SDM Volume 2, Chapter 3.2 "CPUID" (more up-to-date)
// - Intel Application Note 485 (more detailed)
//
// AMD:
// - AMD64 APM Volume 3, Appendix 3 "Obtaining Processor Information ..."
//
// +stateify savable
type FeatureSet struct {
// Function is the underlying CPUID Function.
//
// This is exported to allow direct calls of the underlying CPUID
// function, where required.
Function `state:".(Static)"`
// hwCap stores HWCAP1/2 exposed from the elf auxiliary vector.
hwCap hwCap
}
// saveFunction saves the function as a static query.
func (fs *FeatureSet) saveFunction() Static {
if s, ok := fs.Function.(Static); ok {
return s
}
return fs.ToStatic()
}
// loadFunction saves the function as a static query.
func (fs *FeatureSet) loadFunction(_ context.Context, s Static) {
fs.Function = s
}
// Helper to convert 3 regs into 12-byte vendor ID.
//
//go:nosplit
func vendorIDFromRegs(bx, cx, dx uint32) (r [12]byte) {
for i := uint(0); i < 4; i++ {
b := byte(bx >> (i * 8))
r[i] = b
}
for i := uint(0); i < 4; i++ {
b := byte(dx >> (i * 8))
r[4+i] = b
}
for i := uint(0); i < 4; i++ {
b := byte(cx >> (i * 8))
r[8+i] = b
}
return r
}
// Helper to merge a 12-byte vendor ID back to registers.
//
// Used by static_amd64.go.
func regsFromVendorID(r [12]byte) (bx, cx, dx uint32) {
bx |= uint32(r[0])
bx |= uint32(r[1]) << 8
bx |= uint32(r[2]) << 16
bx |= uint32(r[3]) << 24
cx |= uint32(r[4])
cx |= uint32(r[5]) << 8
cx |= uint32(r[6]) << 16
cx |= uint32(r[7]) << 24
dx |= uint32(r[8])
dx |= uint32(r[9]) << 8
dx |= uint32(r[10]) << 16
dx |= uint32(r[10]) << 24
return
}
// VendorID is the 12-char string returned in ebx:edx:ecx for eax=0.
//
//go:nosplit
func (fs FeatureSet) VendorID() [12]byte {
_, bx, cx, dx := fs.query(vendorID)
return vendorIDFromRegs(bx, cx, dx)
}
// Helper to deconstruct signature dword.
//
//go:nosplit
func signatureSplit(v uint32) (ef, em, pt, f, m, sid uint8) {
sid = uint8(v & 0xf)
m = uint8(v>>4) & 0xf
f = uint8(v>>8) & 0xf
pt = uint8(v>>12) & 0x3
em = uint8(v>>16) & 0xf
ef = uint8(v >> 20)
return
}
// ExtendedFamily is part of the processor signature.
//
//go:nosplit
func (fs FeatureSet) ExtendedFamily() uint8 {
ax, _, _, _ := fs.query(featureInfo)
ef, _, _, _, _, _ := signatureSplit(ax)
return ef
}
// ExtendedModel is part of the processor signature.
//
//go:nosplit
func (fs FeatureSet) ExtendedModel() uint8 {
ax, _, _, _ := fs.query(featureInfo)
_, em, _, _, _, _ := signatureSplit(ax)
return em
}
// ProcessorType is part of the processor signature.
//
//go:nosplit
func (fs FeatureSet) ProcessorType() uint8 {
ax, _, _, _ := fs.query(featureInfo)
_, _, pt, _, _, _ := signatureSplit(ax)
return pt
}
// Family is part of the processor signature.
//
//go:nosplit
func (fs FeatureSet) Family() uint8 {
ax, _, _, _ := fs.query(featureInfo)
_, _, _, f, _, _ := signatureSplit(ax)
return f
}
// Model is part of the processor signature.
//
//go:nosplit
func (fs FeatureSet) Model() uint8 {
ax, _, _, _ := fs.query(featureInfo)
_, _, _, _, m, _ := signatureSplit(ax)
return m
}
// SteppingID is part of the processor signature.
//
//go:nosplit
func (fs FeatureSet) SteppingID() uint8 {
ax, _, _, _ := fs.query(featureInfo)
_, _, _, _, _, sid := signatureSplit(ax)
return sid
}
// VirtualAddressBits returns the number of bits available for virtual
// addresses.
//
//go:nosplit
func (fs FeatureSet) VirtualAddressBits() uint32 {
ax, _, _, _ := fs.query(addressSizes)
return (ax >> 8) & 0xff
}
// PhysicalAddressBits returns the number of bits available for physical
// addresses.
//
//go:nosplit
func (fs FeatureSet) PhysicalAddressBits() uint32 {
ax, _, _, _ := fs.query(addressSizes)
return ax & 0xff
}
// CacheType describes the type of a cache, as returned in eax[4:0] for eax=4.
type CacheType uint8
const (
// cacheNull indicates that there are no more entries.
cacheNull CacheType = iota
// CacheData is a data cache.
CacheData
// CacheInstruction is an instruction cache.
CacheInstruction
// CacheUnified is a unified instruction and data cache.
CacheUnified
)
// Cache describes the parameters of a single cache on the system.
//
// This is returned by the Caches method on FeatureSet.
type Cache struct {
// Level is the hierarchical level of this cache (L1, L2, etc).
Level uint32
// Type is the type of cache.
Type CacheType
// FullyAssociative indicates that entries may be placed in any block.
FullyAssociative bool
// Partitions is the number of physical partitions in the cache.
Partitions uint32
// Ways is the number of ways of associativity in the cache.
Ways uint32
// Sets is the number of sets in the cache.
Sets uint32
// InvalidateHierarchical indicates that WBINVD/INVD from threads
// sharing this cache acts upon lower level caches for threads sharing
// this cache.
InvalidateHierarchical bool
// Inclusive indicates that this cache is inclusive of lower cache
// levels.
Inclusive bool
// DirectMapped indicates that this cache is directly mapped from
// address, rather than using a hash function.
DirectMapped bool
}
// Caches describes the caches on the CPU.
//
// Only supported on Intel; requires allocation.
func (fs FeatureSet) Caches() (caches []Cache) {
if !fs.Intel() {
return
}
// Check against the cache line, which should be consistent.
cacheLine := fs.CacheLine()
for i := uint32(0); ; i++ {
out := fs.Query(In{
Eax: uint32(intelDeterministicCacheParams),
Ecx: i,
})
t := CacheType(out.Eax & 0xf)
if t == cacheNull {
break
}
lineSize := (out.Ebx & 0xfff) + 1
if lineSize != cacheLine {
panic(fmt.Sprintf("Mismatched cache line size: %d vs %d", lineSize, cacheLine))
}
caches = append(caches, Cache{
Type: t,
Level: (out.Eax >> 5) & 0x7,
FullyAssociative: ((out.Eax >> 9) & 1) == 1,
Partitions: ((out.Ebx >> 12) & 0x3ff) + 1,
Ways: ((out.Ebx >> 22) & 0x3ff) + 1,
Sets: out.Ecx + 1,
InvalidateHierarchical: (out.Edx & 1) == 0,
Inclusive: ((out.Edx >> 1) & 1) == 1,
DirectMapped: ((out.Edx >> 2) & 1) == 0,
})
}
return
}
// CacheLine is the size of a cache line in bytes.
//
// All caches use the same line size. This is not enforced in the CPUID
// encoding, but is true on all known x86 processors.
//
//go:nosplit
func (fs FeatureSet) CacheLine() uint32 {
_, bx, _, _ := fs.query(featureInfo)
return 8 * (bx >> 8) & 0xff
}
// HasFeature tests whether or not a feature is in the given feature set.
//
// This function is safe to call from a nosplit context, as long as the
// FeatureSet does not have any masked features.
//
//go:nosplit
func (fs FeatureSet) HasFeature(feature Feature) bool {
return feature.check(fs)
}
// WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is
// a minimal /proc/cpuinfo, it is missing some fields like "microcode" that are
// not always printed in Linux. Several fields are simply made up.
func (fs FeatureSet) WriteCPUInfoTo(cpu, numCPU uint, w io.Writer) {
// Avoid many redundant calls here, since this can occasionally appear
// in the hot path. Read all basic information up front, see above.
ax, _, _, _ := fs.query(featureInfo)
ef, em, _, f, m, _ := signatureSplit(ax)
vendor := fs.VendorID()
fmt.Fprintf(w, "processor\t: %d\n", cpu)
fmt.Fprintf(w, "vendor_id\t: %s\n", string(vendor[:]))
fmt.Fprintf(w, "cpu family\t: %d\n", ((ef<<4)&0xff)|f)
fmt.Fprintf(w, "model\t\t: %d\n", ((em<<4)&0xff)|m)
fmt.Fprintf(w, "model name\t: %s\n", "unknown") // Unknown for now.
fmt.Fprintf(w, "stepping\t: %s\n", "unknown") // Unknown for now.
fmt.Fprintf(w, "cpu MHz\t\t: %.3f\n", cpuFreqMHz)
// Pretend the CPU has 8192 KB of cache. Note that real /proc/cpuinfo exposes total L3 cache
// size on Intel and per-core L2 cache size on AMD (as of Linux 6.1.0), so the value of this
// field is not really important in practice. Any value that is chosen here will be wrong
// by an order of magnitude on a significant chunk of x86 machines.
// 8192 KB is selected because it is a reasonable size that will be effectively usable on
// lightly loaded machines - most machines have 1-4MB of L3 cache per core.
fmt.Fprintf(w, "cache size\t: 8192 KB\n")
fmt.Fprintf(w, "physical id\t: 0\n") // Pretend all CPUs are in the same socket.
fmt.Fprintf(w, "siblings\t: %d\n", numCPU)
fmt.Fprintf(w, "core id\t\t: %d\n", cpu)
fmt.Fprintf(w, "cpu cores\t: %d\n", numCPU) // Pretend each CPU is a distinct core (rather than a hyperthread).
fmt.Fprintf(w, "apicid\t\t: %d\n", cpu)
fmt.Fprintf(w, "initial apicid\t: %d\n", cpu)
fmt.Fprintf(w, "fpu\t\t: yes\n")
fmt.Fprintf(w, "fpu_exception\t: yes\n")
fmt.Fprintf(w, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID.
fmt.Fprintf(w, "wp\t\t: yes\n")
fmt.Fprintf(w, "flags\t\t: %s\n", fs.FlagString())
fmt.Fprintf(w, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
fmt.Fprintf(w, "clflush size\t: %d\n", fs.CacheLine())
fmt.Fprintf(w, "cache_alignment\t: %d\n", fs.CacheLine())
fmt.Fprintf(w, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48)
fmt.Fprintf(w, "power management:\n") // This is always here, but can be blank.
fmt.Fprintf(w, "\n") // The /proc/cpuinfo file ends with an extra newline.
}
var (
authenticAMD = [12]byte{'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D'}
genuineIntel = [12]byte{'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l'}
)
// AMD returns true if fs describes an AMD CPU.
//
//go:nosplit
func (fs FeatureSet) AMD() bool {
return fs.VendorID() == authenticAMD
}
// Intel returns true if fs describes an Intel CPU.
//
//go:nosplit
func (fs FeatureSet) Intel() bool {
return fs.VendorID() == genuineIntel
}
// Leaf 0 of xsaveinfo function returns the size for currently
// enabled xsave features in ebx, the maximum size if all valid
// features are saved with xsave in ecx, and valid XCR0 bits in
// edx:eax.
//
// If xSaveInfo isn't supported, cpuid will not fault but will
// return bogus values.
var (
xsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ebx
maxXsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ecx
amxTileCfgSize = native(In{Eax: uint32(xSaveInfo), Ecx: 17}).Eax
amxTileDataSize = native(In{Eax: uint32(xSaveInfo), Ecx: 18}).Eax
)
const (
// XCR0AMXMask are the bits that enable xsave to operate on AMX TILECFG
// and TILEDATA.
//
// Note: TILECFG and TILEDATA are always either both enabled or both
// disabled.
//
// See Intel® 64 and IA-32 Architectures Software Developers Manual Vol.1
// section 13.3 for details.
XCR0AMXMask = uint64((1 << 17) | (1 << 18))
)
// ExtendedStateSize returns the number of bytes needed to save the "extended
// state" for the enabled features and the boundary it must be aligned to.
// Extended state includes floating point registers, and other cpu state that's
// not associated with the normal task context.
//
// Note: the return value matches the size of signal FP state frames.
// Look at check_xstate_in_sigframe() in the kernel sources for more details.
//
//go:nosplit
func (fs FeatureSet) ExtendedStateSize() (size, align uint) {
if fs.UseXsave() {
return uint(xsaveSize), 64
}
// If we don't support xsave, we fall back to fxsave, which requires
// 512 bytes aligned to 16 bytes.
return 512, 16
}
// AMXExtendedStateSize returns the number of bytes within the "extended state"
// area that is used for AMX.
func (fs FeatureSet) AMXExtendedStateSize() uint {
if fs.UseXsave() {
xcr0 := xgetbv(0)
if (xcr0 & XCR0AMXMask) != 0 {
return uint(amxTileCfgSize + amxTileDataSize)
}
}
return 0
}
// ValidXCR0Mask returns the valid bits in control register XCR0.
//
// Always exclude AMX bits, because we do not support it.
// TODO(gvisor.dev/issues/9896): Implement AMX Support.
//
//go:nosplit
func (fs FeatureSet) ValidXCR0Mask() uint64 {
if !fs.HasFeature(X86FeatureXSAVE) {
return 0
}
ax, _, _, dx := fs.query(xSaveInfo)
return (uint64(dx)<<32 | uint64(ax)) &^ XCR0AMXMask
}
// UseXsave returns the choice of fp state saving instruction.
//
//go:nosplit
func (fs FeatureSet) UseXsave() bool {
return fs.HasFeature(X86FeatureXSAVE) && fs.HasFeature(X86FeatureOSXSAVE)
}
// UseXsaveopt returns true if 'fs' supports the "xsaveopt" instruction.
//
//go:nosplit
func (fs FeatureSet) UseXsaveopt() bool {
return fs.UseXsave() && fs.HasFeature(X86FeatureXSAVEOPT)
}
// UseXsavec returns true if 'fs' supports the "xsavec" instruction.
//
//go:nosplit
func (fs FeatureSet) UseXsavec() bool {
return fs.UseXsaveopt() && fs.HasFeature(X86FeatureXSAVEC)
}
// UseFSGSBASE returns true if 'fs' supports the (RD|WR)(FS|GS)BASE instructions.
func (fs FeatureSet) UseFSGSBASE() bool {
HWCAP2_FSGSBASE := uint64(1) << 1
return fs.HasFeature(X86FeatureFSGSBase) && ((fs.hwCap.hwCap2 & HWCAP2_FSGSBASE) != 0)
}
// archCheckHostCompatible checks for compatibility.
func (fs FeatureSet) archCheckHostCompatible(hfs FeatureSet) error {
// The size of a cache line must match, as it is critical to correctly
// utilizing CLFLUSH. Other cache properties are allowed to change, as
// they are not important to correctness.
fsCache := fs.CacheLine()
hostCache := hfs.CacheLine()
if fsCache != hostCache {
return &ErrIncompatible{
reason: fmt.Sprintf("CPU cache line size %d incompatible with host cache line size %d", fsCache, hostCache),
}
}
return nil
}
// AllowedHWCap1 returns the HWCAP1 bits that the guest is allowed to depend
// on.
func (fs FeatureSet) AllowedHWCap1() uint64 {
// HWCAPS are not supported on amd64.
return 0
}
// AllowedHWCap2 returns the HWCAP2 bits that the guest is allowed to depend
// on.
func (fs FeatureSet) AllowedHWCap2() uint64 {
// HWCAPS are not supported on amd64.
return 0
}