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

@ -654,11 +654,29 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
case header.ICMPv6EchoRequest:
received.echoRequest.Increment()
if len(h) < header.ICMPv6EchoMinimumSize {
received.invalid.Increment()
replyPayload := pkt.Data().ToBuffer()
replyHeader := make([]byte, header.ICMPv6EchoMinimumSize)
copy(replyHeader, h[:header.ICMPv6EchoMinimumSize])
// It's possible that a raw socket or per-stack default handler expects
// to receive this packet.
defaultHandlerHandled := false
if dispatcher, ok := e.dispatcher.(stack.TransportDispatcherWithDefaultHandlerResult); ok {
_, defaultHandlerHandled = dispatcher.DeliverTransportPacketWithDefaultHandlerResult(header.ICMPv6ProtocolNumber, pkt)
} else {
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
}
pkt = nil
// Skip the built-in ICMP echo reply if the request was consumed by a
// per-stack default handler.
if defaultHandlerHandled {
replyPayload.Release()
return
}
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
e.sendICMPEchoReply(replyPayload, replyHeader, srcAddr, dstAddr, iph)
case header.ICMPv6EchoReply:
received.echoReply.Increment()
if len(h) < header.ICMPv6EchoMinimumSize {
@ -666,6 +684,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
return
}
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
case header.ICMPv6TimeExceeded:
received.timeExceeded.Increment()
@ -852,6 +871,62 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
}
}
func (e *endpoint) sendICMPEchoReply(replyPayload buffer.Buffer, replyHeader []byte, srcAddr, dstAddr tcpip.Address, ipHdr header.IPv6) {
sent := e.stats.icmp.packetsSent
// As per RFC 4291 section 2.7, multicast addresses must not be used as
// source addresses in IPv6 packets.
localAddr := dstAddr
if header.IsV6MulticastAddress(dstAddr) {
localAddr = tcpip.Address{}
}
r, err := e.protocol.stack.FindRoute(e.nic.ID(), localAddr, srcAddr, ProtocolNumber, false /* multicastLoop */)
if err != nil {
// If we cannot find a route to the destination, silently drop the packet.
replyPayload.Release()
return
}
defer r.Release()
if !e.protocol.allowICMPReply(header.ICMPv6EchoReply) {
sent.rateLimited.Increment()
replyPayload.Release()
return
}
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize,
Payload: replyPayload,
})
defer replyPkt.DecRef()
icmp := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
replyPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
copy(icmp, replyHeader)
icmp.SetType(header.ICMPv6EchoReply)
replyData := replyPkt.Data()
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
Header: icmp,
Src: r.LocalAddress(),
Dst: r.RemoteAddress(),
PayloadCsum: replyData.Checksum(),
PayloadLen: replyData.Size(),
}))
replyTClass, _ := ipHdr.TOS()
if err := r.WritePacket(stack.NetworkHeaderParams{
Protocol: header.ICMPv6ProtocolNumber,
TTL: r.DefaultTTL(),
// Even though RFC 4443 does not mention anything about it, Linux uses the
// TrafficClass of the received echo request when replying.
// https://github.com/torvalds/linux/blob/0280e3c58f9/net/ipv6/icmp.c#L797
TOS: replyTClass,
}, replyPkt); err != nil {
sent.dropped.Increment()
return
}
sent.echoReply.Increment()
}
// LinkAddressProtocol implements stack.LinkAddressResolver.
func (*endpoint) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
return header.IPv6ProtocolNumber