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:
parent
ffebe42860
commit
117243aa02
293 changed files with 16413 additions and 2842 deletions
|
|
@ -341,6 +341,9 @@ func (sq *sndQueueInfo) CloneState(other *TCPSndBufState) {
|
|||
// TODO(b/339664055): Checklocks should be used more extensively here. Coverage
|
||||
// is currently sparse.
|
||||
//
|
||||
// +checklocksalias:snd.ep.mu=mu
|
||||
// +checklocksalias:rcv.ep.mu=mu
|
||||
// +checklocksalias:h.ep.mu=mu
|
||||
// +stateify savable
|
||||
type Endpoint struct {
|
||||
TCPEndpointStateInner
|
||||
|
|
@ -607,6 +610,16 @@ type Endpoint struct {
|
|||
//
|
||||
// +checklocks:mu
|
||||
alsoBindToV4 bool
|
||||
|
||||
// terminateAtRestore indicates whether the endpoint must be terminated
|
||||
// upon restore. This applies specifically when the snapshots are taken
|
||||
// with the "save-resume" flag, ensuring that if such a snapshot is
|
||||
// restored later, this endpoint is cleaned up. This flag is only set
|
||||
// to true in beforeSave for external endpoints which do not have
|
||||
// save-restore capability.
|
||||
//
|
||||
// +checklocks:mu
|
||||
terminateAtRestore bool
|
||||
}
|
||||
|
||||
// calculateAdvertisedMSS calculates the MSS to advertise.
|
||||
|
|
@ -641,7 +654,6 @@ func (e *Endpoint) isOwnedByUser() bool {
|
|||
// should not be holding the lock for long and spinning reduces latency as we
|
||||
// avoid an expensive sleep/wakeup of the syscall goroutine).
|
||||
// +checklocksacquire:e.mu
|
||||
// +checklocksacquire:e.snd.ep.mu
|
||||
func (e *Endpoint) LockUser() {
|
||||
const iterations = 5
|
||||
for i := 0; i < iterations; i++ {
|
||||
|
|
@ -654,14 +666,14 @@ func (e *Endpoint) LockUser() {
|
|||
if e.ownedByUser.Load() == 1 {
|
||||
e.mu.Lock()
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
// Spin but don't yield the processor since the lower half
|
||||
// should yield the lock soon.
|
||||
continue
|
||||
}
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
|
|
@ -674,7 +686,7 @@ func (e *Endpoint) LockUser() {
|
|||
if e.ownedByUser.Load() == 1 {
|
||||
e.mu.Lock()
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
// Spin but yield the processor since the lower half
|
||||
// should yield the lock soon.
|
||||
|
|
@ -682,7 +694,7 @@ func (e *Endpoint) LockUser() {
|
|||
continue
|
||||
}
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
|
||||
// Finally just give up and wait for the Lock.
|
||||
|
|
@ -725,7 +737,6 @@ func (e *Endpoint) UnlockUser() {
|
|||
// processor goroutine starts running before we release the lock here
|
||||
// then it will fail to process as TryLock() will fail.
|
||||
processor.queueEndpoint(e)
|
||||
return
|
||||
}
|
||||
|
||||
// StopWork halts packet processing. Only to be used in tests.
|
||||
|
|
@ -760,10 +771,7 @@ func (e *Endpoint) AssertLockHeld(locked *Endpoint) {
|
|||
// TODO(b/226403629): Remove this once checklocks understands TryLock.
|
||||
// +checklocksacquire:e.mu
|
||||
func (e *Endpoint) TryLock() bool {
|
||||
if e.mu.TryLock() {
|
||||
return true // +checklocksforce
|
||||
}
|
||||
return false // +checklocksignore
|
||||
return e.mu.TryLock() // +checklocksforce: TryLock.
|
||||
}
|
||||
|
||||
// setEndpointState updates the state of the endpoint to state atomically. This
|
||||
|
|
@ -1015,7 +1023,6 @@ func (e *Endpoint) purgeReadQueue() {
|
|||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) purgeWriteQueue() {
|
||||
if e.snd != nil {
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
|
|
@ -1358,7 +1365,7 @@ func (e *Endpoint) ModerateRecvBuf(copied int) {
|
|||
// Send the update after unlocking rcvQueueMu as sending a segment acquires
|
||||
// the lock to calculate the window to be sent.
|
||||
if e.EndpointState().connected() && sendNonZeroWindowUpdate {
|
||||
e.rcv.nonZeroWindow() // +checklocksforce:e.rcv.ep.mu
|
||||
e.rcv.nonZeroWindow()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1465,7 +1472,7 @@ func (e *Endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
|
|||
e.rcvQueueMu.Unlock()
|
||||
|
||||
if e.EndpointState().connected() && sendNonZeroWindowUpdate {
|
||||
e.rcv.nonZeroWindow() // +checklocksforce:e.rcv.ep.mu
|
||||
e.rcv.nonZeroWindow()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1601,7 +1608,6 @@ func (e *Endpoint) readFromPayloader(p tcpip.Payloader, opts tcpip.WriteOptions,
|
|||
|
||||
// queueSegment reads data from the payloader and returns a segment to be sent.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*segment, int, tcpip.Error) {
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
defer e.sndQueueInfo.sndQueueMu.Unlock()
|
||||
|
|
@ -1642,7 +1648,7 @@ func (e *Endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*se
|
|||
|
||||
// Add data to the send queue.
|
||||
size := int(buf.Size())
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buf)
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buf, e.ops.GetMark())
|
||||
e.sndQueueInfo.SndBufUsed += size
|
||||
e.snd.writeList.PushBack(s)
|
||||
|
||||
|
|
@ -1828,7 +1834,7 @@ func (e *Endpoint) OnSetReceiveBufferSize(rcvBufSz, oldSz int64) (newSz int64, p
|
|||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
if e.EndpointState().connected() && sendNonZeroWindowUpdate {
|
||||
e.rcv.nonZeroWindow() // +checklocksforce:e.rcv.ep.mu
|
||||
e.rcv.nonZeroWindow()
|
||||
}
|
||||
}
|
||||
e.UnlockUser()
|
||||
|
|
@ -1894,15 +1900,15 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
|||
e.UnlockUser()
|
||||
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// PROBE is accepted alongside DO/WANT/DONT. In Linux,
|
||||
// PROBE sets DF but ignores ICMP-based PMTU updates;
|
||||
// since gVisor lacks ICMP PMTU feedback, it behaves
|
||||
// identically to DO.
|
||||
switch v := tcpip.PMTUDStrategy(v); v {
|
||||
case tcpip.PMTUDiscoveryWant, tcpip.PMTUDiscoveryDont, tcpip.PMTUDiscoveryDo:
|
||||
case tcpip.PMTUDiscoveryWant, tcpip.PMTUDiscoveryDont, tcpip.PMTUDiscoveryDo, tcpip.PMTUDiscoveryProbe:
|
||||
e.LockUser()
|
||||
e.pmtud = v
|
||||
e.UnlockUser()
|
||||
case tcpip.PMTUDiscoveryProbe:
|
||||
// We don't support a way to ignore MTU updates; it's
|
||||
// either on or it's off.
|
||||
return &tcpip.ErrNotSupported{}
|
||||
default:
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
|
@ -2391,7 +2397,6 @@ func (e *Endpoint) registerEndpoint(addr tcpip.FullAddress, netProto tcpip.Netwo
|
|||
|
||||
// connect connects the endpoint to its peer.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) connect(addr tcpip.FullAddress, handshake bool) tcpip.Error {
|
||||
connectingAddr := addr.Addr
|
||||
|
||||
|
|
@ -2540,7 +2545,6 @@ func (e *Endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
|
|||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
e.shutdownFlags |= flags
|
||||
switch {
|
||||
|
|
@ -2581,9 +2585,18 @@ func (e *Endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Queue fin segment.
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buffer.Buffer{})
|
||||
// Queue FIN and transition to the closing state immediately,
|
||||
// matching Linux tcp_close_state(): the FIN may be queued but
|
||||
// not yet transmitted when the write queue is blocked.
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buffer.Buffer{}, e.ops.GetMark())
|
||||
e.snd.writeList.PushBack(s)
|
||||
e.updateConnDirectionState(connDirectionStateSndClosed)
|
||||
switch e.EndpointState() {
|
||||
case StateCloseWait:
|
||||
e.setEndpointState(StateLastAck)
|
||||
default:
|
||||
e.setEndpointState(StateFinWait1)
|
||||
}
|
||||
// Mark endpoint as closed.
|
||||
e.sndQueueInfo.SndClosed = true
|
||||
e.sndQueueInfo.sndQueueMu.Unlock()
|
||||
|
|
@ -2924,7 +2937,8 @@ func (e *Endpoint) onICMPError(err tcpip.Error, transErr stack.TransportError, p
|
|||
|
||||
if e.EndpointState().connecting() {
|
||||
e.mu.Lock()
|
||||
if lEP := e.h.listenEP; lEP != nil {
|
||||
if e.h != nil && e.h.listenEP != nil {
|
||||
lEP := e.h.listenEP
|
||||
// Remove from listening endpoints pending list.
|
||||
lEP.acceptMu.Lock()
|
||||
delete(lEP.acceptQueue.pendingEndpoints, e)
|
||||
|
|
@ -2956,7 +2970,7 @@ func (e *Endpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketB
|
|||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if e.snd != nil {
|
||||
e.snd.updateMaxPayloadSize(newMTU, 1 /* count */) // +checklocksforce:e.snd.ep.mu
|
||||
e.snd.updateMaxPayloadSize(newMTU, 1 /* count */)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2986,7 +3000,6 @@ func (e *Endpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketB
|
|||
// number of newly available bytes is v.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) updateSndBufferUsage(v int) {
|
||||
sendBufferSize := e.getSendBufferSize()
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
|
|
@ -3168,7 +3181,6 @@ func (e *Endpoint) maxOptionSize() (size int) {
|
|||
// used before invoking the probe.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) completeStateLocked(s *TCPEndpointState) {
|
||||
s.TCPEndpointStateInner = e.TCPEndpointStateInner
|
||||
s.ID = TCPEndpointID(e.TransportEndpointInfo.ID)
|
||||
|
|
@ -3277,11 +3289,7 @@ func GetTCPSendBufferLimits(sh tcpip.StackHandler) tcpip.SendBufferSizeOption {
|
|||
// This type assertion is safe because only the TCP stack calls this
|
||||
// function.
|
||||
ss := sh.(*stack.Stack).TCPSendBufferLimits()
|
||||
return tcpip.SendBufferSizeOption{
|
||||
Min: ss.Min,
|
||||
Default: ss.Default,
|
||||
Max: ss.Max,
|
||||
}
|
||||
return tcpip.SendBufferSizeOption(ss)
|
||||
}
|
||||
|
||||
// allowOutOfWindowAck returns true if an out-of-window ACK can be sent now.
|
||||
|
|
@ -3309,18 +3317,13 @@ func GetTCPReceiveBufferLimits(s tcpip.StackHandler) tcpip.ReceiveBufferSizeOpti
|
|||
panic(fmt.Sprintf("s.TransportProtocolOption(%d, %#v) = %s", header.TCPProtocolNumber, ss, err))
|
||||
}
|
||||
|
||||
return tcpip.ReceiveBufferSizeOption{
|
||||
Min: ss.Min,
|
||||
Default: ss.Default,
|
||||
Max: ss.Max,
|
||||
}
|
||||
return tcpip.ReceiveBufferSizeOption(ss)
|
||||
}
|
||||
|
||||
// computeTCPSendBufferSize implements auto tuning of send buffer size and
|
||||
// returns the new send buffer size.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) computeTCPSendBufferSize() int64 {
|
||||
curSndBufSz := int64(e.getSendBufferSize())
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue