Обновление снапшота с v0.0.0-20250811.0 на пин, которого требует sing-box после мержа 235 коммитов (upstream d620bbbf2 "Update gvisor to 20260727.0"). Прежний снапшот был взят 2026-08-04 ровно с той версии, на которой тогда стоял апстрим; разрыв возник 2026-08-05 вместе с его бампом. За год апстрим-gvisor изменил ~14 000 строк в 292 файлах. Значимое для нас — сетевой стек: tcp/connect.go (PMTU-discovery + исправление начального RTT/RTO: раньше задержка ACK внутри стека завышала стартовый таймаут на несколько RTT), tcp/snd.go, tcp/rcv.go, stack/conntrack.go, stack/packet_buffer.go. Всего 30 файлов в TCP и 37 в stack. Баг SPEC 048 апстрим НЕ исправил — проверено по коду новой версии: handleConnecting по-прежнему проверяет состояние endpoint'а, но не ep.h, а performHandshake так же зануляет h и отпускает мьютекс до Close(). Поэтому guard перенесён (12 строк) вместе со своим тестом (45 строк). Red/green проверен на новой базе: без guard'а тест падает с той же nil-паникой, что в полевом крашдампе; с ним зелёный.
283 lines
8.2 KiB
Go
283 lines
8.2 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 context defines an internal context type.
|
|
//
|
|
// The given Context conforms to the standard Go context, but mandates
|
|
// additional methods that are specific to the kernel internals. Note however,
|
|
// that the Context described by this package carries additional constraints
|
|
// regarding concurrent access and retaining beyond the scope of a call.
|
|
//
|
|
// See the Context type for complete details.
|
|
package context
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/sagernet/gvisor/pkg/log"
|
|
"github.com/sagernet/gvisor/pkg/waiter"
|
|
)
|
|
|
|
// Blocker represents an object with control flow hooks.
|
|
//
|
|
// These may be used to perform blocking operations, sleep or otherwise
|
|
// wait, since there may be asynchronous events that require processing.
|
|
type Blocker interface {
|
|
// Interrupt interrupts any Block operations.
|
|
Interrupt()
|
|
|
|
// Interrupted notes whether this context is Interrupted.
|
|
Interrupted() bool
|
|
|
|
// Killed returns true if this context is interrupted by a fatal signal.
|
|
Killed() bool
|
|
|
|
// BlockOn blocks until one of the previously registered events occurs,
|
|
// or some external interrupt (cancellation).
|
|
//
|
|
// The return value should indicate whether the wake-up occurred as a
|
|
// result of the requested event (versus an external interrupt).
|
|
BlockOn(waiter.Waitable, waiter.EventMask) bool
|
|
|
|
// Block blocks until an event is received from C, or some external
|
|
// interrupt. It returns nil if an event is received from C and an err if t
|
|
// is interrupted.
|
|
Block(C <-chan struct{}) error
|
|
|
|
// BlockWithTimeout blocks until an event is received from C, the timeout
|
|
// has elapsed (only if haveTimeout is true), or some external interrupt.
|
|
//
|
|
// It returns:
|
|
// - The remaining timeout, which is guaranteed to be 0 if the timeout
|
|
// expired, and is unspecified if haveTimeout is false.
|
|
// - An error which if the timeout expired or if interrupted.
|
|
BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.Duration) (time.Duration, error)
|
|
|
|
// BlockWithTimeoutOn blocks until either the conditions of Block are
|
|
// satisfied, or the timeout is hit. Note that deadlines are not supported
|
|
// since the notion of "with respect to what clock" is not resolved.
|
|
//
|
|
// It returns:
|
|
// - The remaining timeout, which is guaranteed to be 0 if the timeout
|
|
// expired.
|
|
// - Boolean as per BlockOn return value.
|
|
BlockWithTimeoutOn(waiter.Waitable, waiter.EventMask, time.Duration) (time.Duration, bool)
|
|
|
|
// UninterruptibleSleepStart indicates the beginning of an uninterruptible
|
|
// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE).
|
|
UninterruptibleSleepStart()
|
|
|
|
// UninterruptibleSleepFinish indicates the end of an uninterruptible sleep
|
|
// state that was begun by a previous call to UninterruptibleSleepStart.
|
|
UninterruptibleSleepFinish()
|
|
}
|
|
|
|
// NoTask is an implementation of Blocker that does not block.
|
|
type NoTask struct {
|
|
cancel chan struct{}
|
|
}
|
|
|
|
// Interrupt implements Blocker.Interrupt.
|
|
func (nt *NoTask) Interrupt() {
|
|
select {
|
|
case nt.cancel <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
|
|
// Interrupted implements Blocker.Interrupted.
|
|
func (nt *NoTask) Interrupted() bool {
|
|
return len(nt.cancel) > 0
|
|
}
|
|
|
|
// Killed implements Blocker.Killed.
|
|
func (nt *NoTask) Killed() bool {
|
|
return false
|
|
}
|
|
|
|
// Block implements Blocker.Block.
|
|
func (nt *NoTask) Block(C <-chan struct{}) error {
|
|
if nt.cancel == nil {
|
|
nt.cancel = make(chan struct{}, 1)
|
|
}
|
|
select {
|
|
case <-nt.cancel:
|
|
return errors.New("interrupted system call") // Interrupted.
|
|
case <-C:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// BlockOn implements Blocker.BlockOn.
|
|
func (nt *NoTask) BlockOn(w waiter.Waitable, mask waiter.EventMask) bool {
|
|
if nt.cancel == nil {
|
|
nt.cancel = make(chan struct{}, 1)
|
|
}
|
|
e, ch := waiter.NewChannelEntry(mask)
|
|
w.EventRegister(&e)
|
|
defer w.EventUnregister(&e)
|
|
select {
|
|
case <-nt.cancel:
|
|
return false // Interrupted.
|
|
case _, ok := <-ch:
|
|
return ok
|
|
}
|
|
}
|
|
|
|
// BlockWithTimeout implements Blocker.BlockWithTimeout.
|
|
func (nt *NoTask) BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.Duration) (time.Duration, error) {
|
|
if !haveTimeout {
|
|
return timeout, nt.Block(C)
|
|
}
|
|
|
|
if nt.cancel == nil {
|
|
nt.cancel = make(chan struct{}, 1)
|
|
}
|
|
start := time.Now() // In system time.
|
|
remainingTimeout := func() time.Duration {
|
|
rt := timeout - time.Since(start)
|
|
if rt < 0 {
|
|
rt = 0
|
|
}
|
|
return rt
|
|
}
|
|
select {
|
|
case <-nt.cancel:
|
|
return remainingTimeout(), errors.New("interrupted system call") // Interrupted.
|
|
case <-C:
|
|
return remainingTimeout(), nil
|
|
case <-time.After(timeout):
|
|
return 0, errors.New("timeout expired")
|
|
}
|
|
}
|
|
|
|
// BlockWithTimeoutOn implements Blocker.BlockWithTimeoutOn.
|
|
func (nt *NoTask) BlockWithTimeoutOn(w waiter.Waitable, mask waiter.EventMask, timeout time.Duration) (time.Duration, bool) {
|
|
e, ch := waiter.NewChannelEntry(mask)
|
|
w.EventRegister(&e)
|
|
defer w.EventUnregister(&e)
|
|
left, err := nt.BlockWithTimeout(ch, true, timeout)
|
|
return left, err == nil
|
|
}
|
|
|
|
// UninterruptibleSleepStart implmenents Blocker.UninterruptedSleepStart.
|
|
func (*NoTask) UninterruptibleSleepStart() {}
|
|
|
|
// UninterruptibleSleepFinish implmenents Blocker.UninterruptibleSleepFinish.
|
|
func (*NoTask) UninterruptibleSleepFinish() {}
|
|
|
|
// Context represents a thread of execution (hereafter "goroutine" to reflect
|
|
// Go idiosyncrasy). It carries state associated with the goroutine across API
|
|
// boundaries.
|
|
//
|
|
// While Context exists for essentially the same reasons as Go's standard
|
|
// context.Context, the standard type represents the state of an operation
|
|
// rather than that of a goroutine. This is a critical distinction:
|
|
//
|
|
// - Unlike context.Context, which "may be passed to functions running in
|
|
// different goroutines", it is *not safe* to use the same Context in multiple
|
|
// concurrent goroutines.
|
|
//
|
|
// - It is *not safe* to retain a Context passed to a function beyond the scope
|
|
// of that function call.
|
|
//
|
|
// In both cases, values extracted from the Context should be used instead.
|
|
type Context interface {
|
|
context.Context
|
|
log.Logger
|
|
Blocker
|
|
}
|
|
|
|
// logContext implements basic logging.
|
|
type logContext struct {
|
|
NoTask
|
|
log.Logger
|
|
context.Context
|
|
}
|
|
|
|
// bgContext is the context returned by context.Background.
|
|
var (
|
|
bgContext Context
|
|
bgOnce sync.Once
|
|
)
|
|
|
|
// Background returns an empty context using the default logger.
|
|
// Generally, one should use the Task as their context when available, or avoid
|
|
// having to use a context in places where a Task is unavailable.
|
|
//
|
|
// Using a Background context for tests is fine, as long as no values are
|
|
// needed from the context in the tested code paths.
|
|
//
|
|
// The global log.SetTarget() must be called before context.Background()
|
|
func Background() Context {
|
|
bgOnce.Do(func() {
|
|
bgContext = &logContext{
|
|
Context: context.Background(),
|
|
Logger: log.Log(),
|
|
}
|
|
})
|
|
return bgContext
|
|
}
|
|
|
|
// WithValue returns a copy of parent in which the value associated with key is
|
|
// val.
|
|
func WithValue(parent Context, key, val any) Context {
|
|
return &withValue{
|
|
Context: parent,
|
|
key: key,
|
|
val: val,
|
|
}
|
|
}
|
|
|
|
type withValue struct {
|
|
Context
|
|
key any
|
|
val any
|
|
}
|
|
|
|
// Value implements Context.Value.
|
|
func (ctx *withValue) Value(key any) any {
|
|
if key == ctx.key {
|
|
return ctx.val
|
|
}
|
|
return ctx.Context.Value(key)
|
|
}
|
|
|
|
// WithValues returns a copy of parent in which the values associated with keys
|
|
// are the corresponding values in the map.
|
|
func WithValues(parent Context, values map[any]any) Context {
|
|
if len(values) == 0 {
|
|
return parent
|
|
}
|
|
return &withValues{
|
|
Context: parent,
|
|
values: values,
|
|
}
|
|
}
|
|
|
|
type withValues struct {
|
|
Context
|
|
values map[any]any
|
|
}
|
|
|
|
// Value implements Context.Value.
|
|
func (ctx *withValues) Value(key any) any {
|
|
if val, ok := ctx.values[key]; ok {
|
|
return val
|
|
}
|
|
return ctx.Context.Value(key)
|
|
}
|