snapshot: sagernet/gvisor v0.0.0-20260727.0-sing-box-mod.1 + SPEC 048 guard

Обновление снапшота с 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-паникой, что в полевом крашдампе; с ним зелёный.
This commit is contained in:
Leadaxe 2026-08-05 14:53:31 +03:00
parent ffebe42860
commit 117243aa02
293 changed files with 16413 additions and 2842 deletions

View file

@ -384,7 +384,7 @@ func (s *addrSet) InsertWithoutMergingUnchecked(gap addrGapIterator, r addrRange
if splitMaxGap {
gap.node.updateMaxGapLeaf()
}
return addrIterator{gap.node, gap.index}
return addrIterator(gap)
}
// InsertRange inserts the given segment into the set. If the new segment can
@ -512,7 +512,7 @@ func (s *addrSet) Remove(seg addrIterator) addrGapIterator {
if addrtrackGaps != 0 {
seg.node.updateMaxGapLeaf()
}
return seg.node.rebalanceAfterRemove(addrGapIterator{seg.node, seg.index})
return seg.node.rebalanceAfterRemove(addrGapIterator(seg))
}
// RemoveAll removes all segments from the set. All existing iterators are
@ -597,6 +597,19 @@ func (s *addrSet) RemoveFullRangeWith(r addrRange, f func(seg addrIterator)) add
}
}
// MoveFrom moves all segments from s2 to s, replacing all existing segments in
// s and leaving s2 empty.
func (s *addrSet) MoveFrom(s2 *addrSet) {
*s = *s2
for _, child := range s.root.children {
if child == nil {
break
}
child.parent = &s.root
}
s2.RemoveAll()
}
// Merge attempts to merge two neighboring segments. If successful, Merge
// returns an iterator to the merged segment, and all existing iterators are
// invalidated. Otherwise, Merge returns a terminal iterator.
@ -1600,7 +1613,7 @@ func (seg addrIterator) PrevGap() addrGapIterator {
if seg.node.hasChildren {
return seg.node.children[seg.index].lastSegment().NextGap()
}
return addrGapIterator{seg.node, seg.index}
return addrGapIterator(seg)
}
// NextGap returns the gap immediately after the iterated segment.
@ -1889,26 +1902,26 @@ func (n *addrnode) String() string {
func (n *addrnode) writeDebugString(buf *bytes.Buffer, prefix string) {
if n.hasChildren != (n.nrSegments > 0 && n.children[0] != nil) {
buf.WriteString(prefix)
buf.WriteString(fmt.Sprintf("WARNING: inconsistent value of hasChildren: got %v, want %v\n", n.hasChildren, !n.hasChildren))
fmt.Fprintf(buf, "WARNING: inconsistent value of hasChildren: got %v, want %v\n", n.hasChildren, !n.hasChildren)
}
for i := 0; i < n.nrSegments; i++ {
if child := n.children[i]; child != nil {
cprefix := fmt.Sprintf("%s- % 3d ", prefix, i)
if child.parent != n || child.parentIndex != i {
buf.WriteString(cprefix)
buf.WriteString(fmt.Sprintf("WARNING: inconsistent linkage to parent: got (%p, %d), want (%p, %d)\n", child.parent, child.parentIndex, n, i))
fmt.Fprintf(buf, "WARNING: inconsistent linkage to parent: got (%p, %d), want (%p, %d)\n", child.parent, child.parentIndex, n, i)
}
child.writeDebugString(buf, fmt.Sprintf("%s- % 3d ", prefix, i))
}
buf.WriteString(prefix)
if n.hasChildren {
if addrtrackGaps != 0 {
buf.WriteString(fmt.Sprintf("- % 3d: %v => %v, maxGap: %d\n", i, n.keys[i], n.values[i], n.maxGap.Get()))
fmt.Fprintf(buf, "- % 3d: %v => %v, maxGap: %d\n", i, n.keys[i], n.values[i], n.maxGap.Get())
} else {
buf.WriteString(fmt.Sprintf("- % 3d: %v => %v\n", i, n.keys[i], n.values[i]))
fmt.Fprintf(buf, "- % 3d: %v => %v\n", i, n.keys[i], n.values[i])
}
} else {
buf.WriteString(fmt.Sprintf("- % 3d: %v => %v\n", i, n.keys[i], n.values[i]))
fmt.Fprintf(buf, "- % 3d: %v => %v\n", i, n.keys[i], n.values[i])
}
}
if child := n.children[n.nrSegments]; child != nil {

View file

@ -694,6 +694,7 @@ func (ds *decodeState) Load(obj reflect.Value) {
// iterations required to finish all objects.
if err := safely(func() {
for elem := ds.leaves.Front(); elem != nil; elem = elem.Next() {
ods = elem.ods
ds.checkComplete(elem.ods)
}
}); err != nil {
@ -714,7 +715,7 @@ func (ds *decodeState) Load(obj reflect.Value) {
fmt.Fprintf(&buf, "%q", cycleOS.obj.Type())
}
buf.WriteString("}")
Failf("incomplete graph: %s", string(buf.Bytes()))
Failf("incomplete graph: %s", buf.String())
}
}

View file

@ -770,8 +770,11 @@ func (es *encodeState) Save(obj reflect.Value) {
es.encodeObject(oes.obj, oes.how, &oes.encoded)
}
}); err != nil {
// Include the object in the error message.
Failf("encoding error: %w\nfor object %#v", err, oes.obj.Interface())
// Include the object in the error message, if available.
if oes != nil && oes.obj.IsValid() {
Failf("encoding error: %w\nfor object %#v", err, oes.obj.Interface())
}
Failf("encoding error: %w", err)
}
// Check that we have objects to serialize.
@ -802,14 +805,19 @@ func (es *encodeState) Save(obj reflect.Value) {
})
for _, id := range ids {
// Encode the id.
oes = nil
wire.Save(&es.w, wire.Uint(id))
// Marshal the object.
oes := es.pending[id]
oes = es.pending[id]
wire.Save(&es.w, oes.encoded)
}
}); err != nil {
// Include the object and the error.
Failf("error serializing object %#v: %w", oes.encoded, err)
if oes != nil {
// Include the object and the error.
Failf("error serializing object %#v: %w", oes.encoded, err)
} else {
Failf("error serializing type or ID: %w", err)
}
}
}

View file

@ -48,7 +48,7 @@ func (p *printer) formatRef(x *wire.Ref, graph uint64) string {
buf.WriteString(".")
buf.WriteString(string(*v))
case wire.Index:
buf.WriteString(fmt.Sprintf("[%d]", v))
fmt.Fprintf(&buf, "[%d]", v)
default:
panic(fmt.Sprintf("unreachable: switch should be exhaustive, unhandled case %v", reflect.TypeOf(component)))
}
@ -147,7 +147,7 @@ func (p *printer) format(graph uint64, depth int, encoded wire.Object) (string,
return strings.Join(items, tabs), len(zeros) < len(x.Contents)
case *wire.Struct:
tag := fmt.Sprintf("g%dt%d", graph, x.TypeID)
spec, _ := p.typeSpecs[tag]
spec := p.typeSpecs[tag]
typ, _ := p.formatType(x.TypeID, graph)
if x.Fields() == 0 {
return fmt.Sprintf("struct[%s]{}", typ), false

View file

@ -188,7 +188,7 @@ func (s Sink) Context() context.Context {
// Type is an interface that must be implemented by Struct objects. This allows
// these objects to be serialized while minimizing runtime reflection required.
//
// All these methods can be automatically generated by the go_statify tool.
// All these methods can be automatically generated by the go_stateify tool.
type Type interface {
// StateTypeName returns the type's name.
//

View file

@ -87,8 +87,8 @@ var ErrMetadataInvalid = fmt.Errorf("metadata invalid, can't start with _")
var ErrInvalidFlags = fmt.Errorf("flags set is invalid")
const (
// CompressionKey is the key for the compression level in the metadata.
CompressionKey = "compression"
// compressionKey is the key for the compression level in the metadata.
compressionKey = "compression"
)
// CompressionLevel is the image compression level.
@ -100,7 +100,7 @@ const (
// CompressionLevelNone represents the absence of any compression on an image.
CompressionLevelNone = CompressionLevel("none")
// CompressionLevelDefault represents the default compression level.
CompressionLevelDefault = CompressionLevelFlateBestSpeed
CompressionLevelDefault = CompressionLevelNone
)
func (c CompressionLevel) String() string {
@ -109,7 +109,7 @@ func (c CompressionLevel) String() string {
// ToMetadata returns the compression level as a metadata map.
func (c CompressionLevel) ToMetadata() map[string]string {
return map[string]string{CompressionKey: string(c)}
return map[string]string{compressionKey: string(c)}
}
// CompressionLevelFromString parses a string into the CompressionLevel.
@ -127,21 +127,14 @@ func CompressionLevelFromString(val string) (CompressionLevel, error) {
}
// CompressionLevelFromMetadata returns image compression type stored in the metadata.
// If the metadata doesn't contain compression information the default behavior
// is the "flate-best-speed" state because the default behavior used to be to always
// compress. If the parameter is missing it will be set to default.
// If the metadata doesn't contain compression information, the default behavior
// is "none" (no compression) and it is added to the metadata.
func CompressionLevelFromMetadata(metadata map[string]string) (CompressionLevel, error) {
compression := CompressionLevelDefault
if val, ok := metadata[CompressionKey]; ok {
var err error
if compression, err = CompressionLevelFromString(val); err != nil {
return CompressionLevelNone, err
}
} else {
metadata[CompressionKey] = string(compression)
if val, ok := metadata[compressionKey]; ok {
return CompressionLevelFromString(val)
}
compression := CompressionLevelDefault
metadata[compressionKey] = string(compression)
return compression, nil
}
@ -166,8 +159,12 @@ func NewWriter(w io.Writer, key []byte, metadata map[string]string) (io.WriteClo
}
// Create our HMAC function.
h := hmac.New(sha256.New, key)
mw := io.MultiWriter(w, h)
mw := w
var h hash.Hash
if len(key) > 0 {
h = hmac.New(sha256.New, key)
mw = io.MultiWriter(w, h)
}
// First, write the header.
if _, err := mw.Write(magicHeader); err != nil {
@ -204,12 +201,14 @@ func NewWriter(w io.Writer, key []byte, metadata map[string]string) (io.WriteClo
return nil, err
}
// Write the current hash.
cur := h.Sum(nil)
for done := 0; done < len(cur); {
n, err := mw.Write(cur[done:])
done += n
if err != nil {
return nil, err
if h != nil {
cur := h.Sum(nil)
for done := 0; done < len(cur); {
n, err := mw.Write(cur[done:])
done += n
if err != nil {
return nil, err
}
}
}
@ -240,8 +239,10 @@ func readMetadataLen(r io.Reader) (uint64, error) {
// metadata validates the magic header and reads out the metadata from a state
// data stream.
func metadata(r io.Reader, h hash.Hash) (map[string]string, error) {
if h != nil {
func metadata(r io.Reader, key []byte) (map[string]string, error) {
var h hash.Hash
if len(key) > 0 {
h = hmac.New(sha256.New, key)
r = io.TeeReader(r, h)
}
@ -285,6 +286,9 @@ func metadata(r io.Reader, h hash.Hash) (map[string]string, error) {
cur := h.Sum(nil)
buf := make([]byte, len(cur))
if _, err := io.ReadFull(r, buf); err != nil {
if err == io.EOF {
return nil, io.ErrUnexpectedEOF
}
return nil, err
}
if !hmac.Equal(cur, buf) {
@ -304,8 +308,7 @@ func metadata(r io.Reader, h hash.Hash) (map[string]string, error) {
// NewReader returns a reader for a statefile.
func NewReader(r io.ReadCloser, key []byte) (io.ReadCloser, map[string]string, error) {
// Read the metadata with the hash.
h := hmac.New(sha256.New, key)
metadata, err := metadata(r, h)
metadata, err := metadata(r, key)
if err != nil {
return nil, nil, err
}
@ -321,11 +324,12 @@ func NewReader(r io.ReadCloser, key []byte) (io.ReadCloser, map[string]string, e
// Pick correct reader
var cr io.ReadCloser
if compression == CompressionLevelFlateBestSpeed {
switch compression {
case CompressionLevelFlateBestSpeed:
cr, err = compressio.NewReader(r, key)
} else if compression == CompressionLevelNone {
case CompressionLevelNone:
cr = compressio.NewSimpleReader(r, key)
} else {
default:
// Should never occur, as it has the default path.
return nil, nil, fmt.Errorf("metadata contains invalid compression flag value: %v", compression)
}

View file

@ -124,7 +124,7 @@ func (s *Stats) String() string {
total time.Duration
)
buf.WriteString("\n")
buf.WriteString(fmt.Sprintf("% 16s | % 8s | % 16s | %s\n", "total", "count", "per", "type"))
fmt.Fprintf(&buf, "% 16s | % 8s | % 16s | %s\n", "total", "count", "per", "type")
buf.WriteString("-----------------+----------+------------------+----------------\n")
for _, se := range ss {
if se.entry.count == 0 {
@ -135,11 +135,11 @@ func (s *Stats) String() string {
count += se.entry.count
total += se.entry.total
per := se.entry.total / time.Duration(se.entry.count)
buf.WriteString(fmt.Sprintf("% 16s | %8d | % 16s | %s\n",
se.entry.total, se.entry.count, per, se.name))
fmt.Fprintf(&buf, "% 16s | %8d | % 16s | %s\n",
se.entry.total, se.entry.count, per, se.name)
}
buf.WriteString("-----------------+----------+------------------+----------------\n")
buf.WriteString(fmt.Sprintf("% 16s | % 8d | % 16s | [all]",
total, count, total/time.Duration(count)))
return string(buf.Bytes())
fmt.Fprintf(&buf, "% 16s | % 8d | % 16s | [all]",
total, count, total/time.Duration(count))
return buf.String()
}

View file

@ -295,23 +295,23 @@ const interfaceType = "interface"
var primitiveTypeDatabase = func() map[string]reflect.Type {
r := make(map[string]reflect.Type)
for _, t := range []reflect.Type{
reflect.TypeOf(false),
reflect.TypeOf(int(0)),
reflect.TypeOf(int8(0)),
reflect.TypeOf(int16(0)),
reflect.TypeOf(int32(0)),
reflect.TypeOf(int64(0)),
reflect.TypeOf(uint(0)),
reflect.TypeOf(uintptr(0)),
reflect.TypeOf(uint8(0)),
reflect.TypeOf(uint16(0)),
reflect.TypeOf(uint32(0)),
reflect.TypeOf(uint64(0)),
reflect.TypeOf(""),
reflect.TypeOf(float32(0.0)),
reflect.TypeOf(float64(0.0)),
reflect.TypeOf(complex64(0.0)),
reflect.TypeOf(complex128(0.0)),
reflect.TypeFor[bool](),
reflect.TypeFor[int](),
reflect.TypeFor[int8](),
reflect.TypeFor[int16](),
reflect.TypeFor[int32](),
reflect.TypeFor[int64](),
reflect.TypeFor[uint](),
reflect.TypeFor[uintptr](),
reflect.TypeFor[uint8](),
reflect.TypeFor[uint16](),
reflect.TypeFor[uint32](),
reflect.TypeFor[uint64](),
reflect.TypeFor[string](),
reflect.TypeFor[float32](),
reflect.TypeFor[float64](),
reflect.TypeFor[complex64](),
reflect.TypeFor[complex128](),
} {
r[t.Name()] = t
}

View file

@ -509,7 +509,7 @@ func loadMap(r *Reader) Map {
func (m *Map) save(w *Writer) {
l := Uint(len(m.Keys))
if int(l) != len(m.Values) {
panic(fmt.Sprintf("mismatched keys (%d) Aand values (%d)", len(m.Keys), len(m.Values)))
panic(fmt.Sprintf("mismatched keys (%d) and values (%d)", len(m.Keys), len(m.Values)))
}
l.save(w)
if l == 0 {