tcp: Reject SYN on synchronized connections; fix maxSend underflow (#44)

* tcp: add test replicating bugs and 32bit panic with GOARCH=386

* tcp: reject SYN on synchronized connections; fix maxSend underflow
This commit is contained in:
Pat Whittingslow
2026-02-27 16:38:46 +01:00
committed by GitHub
parent f9748da94f
commit eab43c4653
4 changed files with 242 additions and 6 deletions
+17 -4
View File
@@ -134,7 +134,12 @@ func (snd *sendSpace) inFlight() Size {
// maxSend returns maximum segment datalength receivable by remote peer.
func (snd *sendSpace) maxSend() Size {
return snd.WND - snd.inFlight()
if inf := snd.inFlight(); inf >= snd.WND {
// Guard uint32 underflow when window shrinks below inflight.
return 0
} else {
return snd.WND - inf
}
}
// recvSpace contains Receive Sequence Space data. Its sequence numbers correspond to remote data.
@@ -252,6 +257,13 @@ func (tcb *ControlBlock) Recv(seg Segment) (err error) {
return err
}
// RFC 9293 §3.10.7.4: SYN on synchronized connection → challenge ACK.
if seg.Flags.HasAny(FlagSYN) && !tcb._state.IsPreestablished() {
tcb.challengeAck = true
tcb.pending[0] |= FlagACK
return errDropSegment
}
prevNxt := tcb.snd.NXT
var pending Flags
switch tcb._state {
@@ -402,10 +414,11 @@ func (tcb *ControlBlock) validateOutgoingSegment(seg Segment) (err error) {
func (tcb *ControlBlock) validateIncomingSegment(seg Segment) (err error) {
flags := seg.Flags
hasAck := flags.HasAll(FlagACK)
// Short circuit SEQ checks if SYN present since the incoming segment initialize1s connection.
checkSEQ := !flags.HasAny(FlagSYN)
established := tcb._state == StateEstablished
// Short circuit SEQ checks if SYN present in pre-established states only.
// In synchronized states SYN must pass normal SEQ validation (RFC 9293 §3.10.7.4).
preestablished := tcb._state.IsPreestablished()
checkSEQ := !flags.HasAny(FlagSYN) || !preestablished
established := tcb._state == StateEstablished
acksOld := hasAck && !tcb.snd.UNA.LessThan(seg.ACK)
acksUnsentData := hasAck && !seg.ACK.LessThanEq(tcb.snd.NXT)
ctlOrDataSegment := established && (seg.DATALEN > 0 || flags.HasAny(FlagFIN|FlagRST))