TCP retransmission fixes and fuzz failure fixes (#59)

* fix for #58

* add fixes for both retransmit and fuzz failure found

* fix infinite challenge ack due to RST+SYN+FIN corrupt packet in SYNRCV state

* fuzz: previous recovery ack path led to infinite transmit

* fuzz: calculate CRCs when fuzzing to reduce search space to non-CRC error cases

* add local fuzz corpus to tests

* add more cases to fuzz corpus
This commit is contained in:
Pat Whittingslow
2026-03-20 14:25:31 +01:00
committed by GitHub
parent 493446244f
commit faae7ab076
216 changed files with 1299 additions and 35 deletions
+13 -1
View File
@@ -127,7 +127,13 @@ func (ifrm Frame) SetCRC(cs uint16) {
binary.BigEndian.PutUint16(ifrm.buf[10:12], cs)
}
// CalculateHeaderCRC calculates the CRC for this IPv4 frame.
// CalculateHeaderCRC calculates the CRC for this IPv4 frame including CRC field.
// If the result of this function is 0 then the CRC is valid for the frame.
// To calculate the CRC for a frame set the CRC field to zero and then call this function:
//
// ifrm.SetCRC(0)
// crcValue := ifrm.CalculateHeaderCRC()
// ifrm.SetCRC(crcValue)
func (ifrm Frame) CalculateHeaderCRC() uint16 {
var crc lneto.CRC791
ifrm.CRCWriteHeader(&crc)
@@ -138,6 +144,12 @@ func (ifrm Frame) CRCWriteHeader(crc *lneto.CRC791) {
crc.WriteEven(ifrm.buf[:20])
}
// CRCWriteTCPPseudo is used to calculate the TCP checksum for IPv4 framed packets.
// To calculate the CRC of a TCP frame:
//
// ifrm.CRCWriteTCPPseudo(&crc)
// // tfrm.SetCRC(0) // Zero if calculating frame for sending out.
// crcValue := crc.PayloadSum16(ifrm.Payload()) // If validating a received frame crcValue should be zero here.
func (ifrm Frame) CRCWriteTCPPseudo(crc *lneto.CRC791) {
crc.WriteEven(ifrm.sourceAndDestinationAddr())
crc.AddUint16(ifrm.TotalLength() - uint16(ifrm.HeaderLength()))
+1
View File
@@ -386,6 +386,7 @@ func (conn *Conn) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
// RFC 6298 §5.1: check RTO before sending new data.
if conn.h.ShouldRetransmit() {
conn.h.triggerRetransmit()
conn.h.dupACKs = 0 // RTO is a new loss event; reset dup-ACK counter.
}
n, err = conn.h.Send(carrierData[offsetToFrame:])
if err != nil || n == 0 {
+24 -19
View File
@@ -438,7 +438,8 @@ func (tcb *ControlBlock) validateIncomingSegment(seg Segment) (err error) {
// 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
// LISTEN has no receive window context; RFC 9293 §3.10.7.1 step 1: "no checking in LISTEN state."
checkSEQ := (!flags.HasAny(FlagSYN) || !preestablished) && tcb._state != StateListen
established := tcb._state == StateEstablished
acksOld := hasAck && !tcb.snd.UNA.LessThan(seg.ACK)
acksUnsentData := hasAck && !seg.ACK.LessThanEq(tcb.snd.NXT)
@@ -493,24 +494,15 @@ func (tcb *ControlBlock) validateIncomingSegment(seg Segment) (err error) {
}
case established && acksUnsentData:
// After Retransmit() rewinds snd.NXT to snd.UNA, the remote may ACK
// data it received pre-rewind — a valid cumulative ACK that exceeds
// the rewound snd.NXT. Detect this case (NXT==UNA means rewind active)
// and accept the ACK if within the send window.
retransmitActive := tcb.snd.NXT == tcb.snd.UNA
if retransmitActive && seg.ACK.InWindow(tcb.snd.UNA, tcb.snd.WND) {
tcb.snd.NXT = seg.ACK
if isDebug {
tcb.debug("rcv:ACK-advance-nxt", slog.String("state", tcb._state.String()),
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)))
}
} else {
err = errDropSegment
tcb.pending[0] |= FlagACK // Send ACK for unsent data; |= preserves any pending FIN.
if isDebug {
tcb.debug("rcv:ACK-unsent", slog.String("state", tcb._state.String()),
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)))
}
// ACK for data we haven't sent. Drop and send challenge ACK.
// Note: after Retransmit() rewinds snd.NXT, a cumulative ACK may exceed
// the rewound NXT. That case is handled by Handler.RecoveryACK, not here —
// NXT==UNA is ambiguous (also true when no data is in flight).
err = errDropSegment
tcb.pending[0] |= FlagACK // Send ACK for unsent data; |= preserves any pending FIN.
if isDebug {
tcb.debug("rcv:ACK-unsent", slog.String("state", tcb._state.String()),
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)))
}
case preestablished && (acksOld || acksUnsentData):
@@ -581,6 +573,19 @@ func (tcb *ControlBlock) rstJump() Value {
// Implements RFC 9293 §3.10.8 (RETRANSMISSION TIMEOUT).
func (tcb *ControlBlock) Retransmit() { tcb.snd.NXT = tcb.snd.UNA }
// RecoveryACK accepts a cumulative ACK that covers data sent before a retransmit
// rewind. After Retransmit() rewinds snd.NXT, the remote may ACK data it received
// pre-rewind — a valid cumulative ACK that exceeds the rewound snd.NXT. This method
// advances snd.UNA, snd.NXT and updates the send window from the segment.
// The caller must verify that seg.ACK is within the pre-rewind NXT range.
func (tcb *ControlBlock) RecoveryACK(seg Segment) {
tcb.snd.UNA = seg.ACK
tcb.snd.NXT = seg.ACK
tcb.snd.WND = seg.WND
// Clear any pending ACK that validateIncomingSegment queued on rejection.
tcb.pending[0] &^= FlagACK
}
// Abort sets ControlBlock state to Closed and resets all sequence numbers and pending flag.
// No more data can be sent nor received after the connection is aborted until opened again.
// An abort call prepares the connection for opening an active connection via a
+6 -1
View File
@@ -97,7 +97,12 @@ func (tcb *ControlBlock) rcvFinWait1(seg Segment) (pending Flags, err error) {
default:
return 0, errFinwaitExpectedACK
}
pending = FlagACK
// Only queue ACK when there is data or FIN to acknowledge.
// Bare ACKs must not elicit an ACK response; doing so creates an
// infinite ACK ping-pong when the peer sends challenge ACKs.
if seg.DATALEN > 0 || hasFin {
pending = FlagACK
}
return pending, nil
}
+37 -10
View File
@@ -30,7 +30,10 @@ type Handler struct {
optcodec OptionCodec
closing bool
// dupACKs counts consecutive duplicate ACKs for fast retransmit (RFC 5681 §3.2).
dupACKs uint8
// nRetx counts consecutive retransmissions for exponential backoff (RFC 6298 §5.5).
nRetx uint8
// Retransmission timer state — all uint32 milliseconds, no time package needed.
// rto is the current retransmission timeout in ms; starts at 1000 per RFC 6298 §2.1.
rto uint32
@@ -38,10 +41,11 @@ type Handler struct {
now uint32
// lastACK is the last ACK value seen, for duplicate ACK detection (RFC 5681 §3.2).
lastACK Value
// dupACKs counts consecutive duplicate ACKs for fast retransmit (RFC 5681 §3.2).
dupACKs uint8
// nRetx counts consecutive retransmissions for exponential backoff (RFC 6298 §5.5).
nRetx uint8
// retransmitNXT is the pre-rewind value of snd.NXT, saved when fast retransmit
// fires. A cumulative ACK with seg.ACK <= retransmitNXT is valid even if it
// exceeds the rewound snd.NXT. Zero means not in recovery.
retransmitNXT Value
}
func (h *Handler) SetLoggers(handler, scb *slog.Logger) {
@@ -188,11 +192,30 @@ func (h *Handler) Recv(incomingPacket []byte) error {
prevUNA := h.scb.snd.UNA // Capture before Recv updates snd.UNA (RFC 6298 §5.3).
err = h.scb.Recv(segIncoming)
if err != nil {
if h.scb.State() == StateClosed {
// TODO(soypat): Should return EOF/ErrClosed?
err = net.ErrClosed //err // Connection closed by reset.
// Recovery path: after fast retransmit rewinds snd.NXT, a cumulative ACK
// for data sent pre-rewind exceeds the rewound NXT. The ControlBlock rejects
// it, but we know it's valid if ACK <= retransmitNXT (pre-rewind high water mark).
if h.retransmitNXT != 0 && segIncoming.Flags.HasAny(FlagACK) &&
h.scb.snd.NXT.LessThan(segIncoming.ACK) &&
segIncoming.ACK.LessThanEq(h.retransmitNXT) {
// TODO: This is a very hacky workaround. It'd be great
// to detect recover acks in Handler before calling ControlBlock.Recv
// and handle it cleanly instead of with an error.
h.scb.RecoveryACK(segIncoming)
h.bufTx.RecoveryACK(segIncoming.ACK)
h.retransmitNXT = 0
h.rto = rtoInitial
h.nRetx = 0
h.dupACKs = 0
h.lastACK = segIncoming.ACK
err = nil // Accept the segment.
} else {
if h.scb.State() == StateClosed {
// TODO(soypat): Should return EOF/ErrClosed?
err = net.ErrClosed //err // Connection closed by reset.
}
return err
}
return err
}
if h.scb.State() == StateClosed {
// TCB aborted, likely because it received an ACK in LastAck state.
@@ -486,6 +509,11 @@ func (h *Handler) ShouldRetransmit() bool {
// triggerRetransmit rewinds the transmit queue and control block so the next
// Send call retransmits from snd.UNA. Per RFC 9293 §3.10.8, RFC 6298 §5.45.5.
func (h *Handler) triggerRetransmit() {
// Save the high-water mark of NXT before rewinding so that cumulative ACKs
// for data sent pre-rewind can still be accepted (see Recv recovery path).
if h.retransmitNXT == 0 || h.retransmitNXT.LessThan(h.scb.snd.NXT) {
h.retransmitNXT = h.scb.snd.NXT
}
h.scb.Retransmit()
h.bufTx.RetransmitFromUNA()
// RFC 6298 §5.5: "The host MUST set RTO <- RTO * 2 ('back off the timer')."
@@ -494,7 +522,6 @@ func (h *Handler) triggerRetransmit() {
if h.rto > rtoMax {
h.rto = rtoMax
}
h.dupACKs = 0
h.debug("tcp.Handler:retransmit", slog.Uint64("port", uint64(h.localPort)),
slog.Uint64("rto", uint64(h.rto)), slog.Uint64("nRetx", uint64(h.nRetx)))
}
+527
View File
@@ -145,3 +145,530 @@ func TestPostRetransmitACKAccepted(t *testing.T) {
t.Fatalf("snd.UNA not advanced: got %d, want %d", client.scb.snd.UNA, preRewindNXT)
}
}
// TestRecoveryACKSkipsSpuriousRetransmit verifies that after fast retransmit
// rewinds snd.NXT and the client re-sends the lost segment, a cumulative ACK
// from the remote (acknowledging all data received before and after the hole)
// is accepted — even though it exceeds the rewound snd.NXT.
//
// Without this fix, lneto rejects the cumulative ACK as "acks unsent data"
// and then spuriously retransmits data that was already received by the remote.
//
// Timeline:
// 1. Client sends packets 0..N; packet 1 is lost (the "hole")
// 2. Server ACKs packet 0; sends 3 dup ACKs → fast retransmit fires
// 3. Client rewinds to snd.UNA, re-sends lost segment → snd.NXT advances by 1 MSS
// 4. Server (having received all other packets) sends cumulative ACK for ALL data
// 5. Client should accept this ACK (not reject it) and NOT send spurious retransmissions
func TestRecoveryACKSkipsSpuriousRetransmit(t *testing.T) {
const mtu = 60 // 20-byte header + 40-byte payload per packet.
const txBuf = 2048
const maxpackets = 10
rng := rand.New(rand.NewSource(59))
client := new(Handler)
server := new(Handler)
err := client.SetBuffers(make([]byte, txBuf), make([]byte, txBuf), maxpackets)
if err != nil {
t.Fatal(err)
}
client.rto = rtoInitial
err = server.SetBuffers(make([]byte, txBuf), make([]byte, txBuf), maxpackets)
if err != nil {
t.Fatal(err)
}
server.rto = rtoInitial
err = server.OpenListen(uint16(rng.Uint32()), 0)
if err != nil {
t.Fatal(err)
}
err = client.OpenActive(uint16(rng.Uint32()), server.LocalPort(), 0)
if err != nil {
t.Fatal(err)
}
var rawbuf [mtu]byte
establish(t, client, server, rawbuf[:])
// Send enough data to fill several packets (MSS=40).
data := make([]byte, 40*6) // 6 packets worth of data.
for i := range data {
data[i] = byte(i)
}
written := 0
var packets [][]byte
for written < len(data) {
n, werr := client.Write(data[written:])
if werr != nil {
t.Fatal("client write:", werr)
}
written += n
for {
clear(rawbuf[:])
ns, serr := client.Send(rawbuf[:])
if serr != nil {
t.Fatal("client send:", serr)
}
if ns == 0 {
break
}
packets = append(packets, append([]byte(nil), rawbuf[:ns]...))
}
}
if len(packets) < 4 {
t.Fatalf("need at least 4 data packets, got %d", len(packets))
}
t.Logf("sent %d data packets", len(packets))
// Record the sequence endpoint: this is the ACK value the server will
// send once it receives all data (including the "lost" packet).
preRewindNXT := client.scb.snd.NXT
t.Logf("pre-rewind snd.NXT=%d, snd.UNA=%d", preRewindNXT, client.scb.snd.UNA)
// Server receives only packet 0 → ACKs it. This establishes lastACK on client.
err = server.Recv(packets[0])
if err != nil {
t.Fatal("server recv pkt0:", err)
}
clear(rawbuf[:])
n, err := server.Send(rawbuf[:])
if err != nil {
t.Fatal("server send ACK:", err)
}
if n == 0 {
t.Fatal("expected server to send ACK")
}
err = client.Recv(rawbuf[:n])
if err != nil {
t.Fatal("client recv ACK:", err)
}
dupACKValue := client.lastACK
t.Logf("lastACK=%d after first ACK", dupACKValue)
// Craft 3 dup ACKs (packet 1 is "lost", server keeps acking dupACKValue).
for i := 0; i < 3; i++ {
var buf [mtu]byte
frm, ferr := NewFrame(buf[:])
if ferr != nil {
t.Fatal(ferr)
}
frm.SetSourcePort(server.LocalPort())
frm.SetDestinationPort(client.LocalPort())
frm.SetSegment(Segment{
SEQ: server.scb.snd.NXT,
ACK: dupACKValue,
Flags: FlagACK,
WND: 65535,
}, 5)
rerr := client.Recv(buf[:sizeHeaderTCP])
if rerr != nil {
t.Logf("dup ACK %d recv err (expected): %v", i+1, rerr)
}
}
if client.nRetx == 0 {
t.Fatal("fast retransmit did not fire after 3 dup ACKs")
}
t.Logf("fast retransmit fired: snd.NXT=%d, snd.UNA=%d", client.scb.snd.NXT, client.scb.snd.UNA)
// Client re-sends the lost segment. After this, snd.NXT > snd.UNA
// (advanced by one MSS), but still < preRewindNXT.
clear(rawbuf[:])
n, err = client.Send(rawbuf[:])
if err != nil {
t.Fatal("client retransmit send:", err)
}
if n == 0 {
t.Fatal("expected client to send retransmit packet")
}
if client.scb.snd.NXT == client.scb.snd.UNA {
t.Fatal("expected snd.NXT to advance past snd.UNA after re-send")
}
t.Logf("after retransmit send: snd.NXT=%d, snd.UNA=%d (preRewind=%d)",
client.scb.snd.NXT, client.scb.snd.UNA, preRewindNXT)
// Craft cumulative ACK from server for ALL data (as if server had received
// everything and the lost packet just arrived, filling the hole).
{
var buf [mtu]byte
frm, ferr := NewFrame(buf[:])
if ferr != nil {
t.Fatal(ferr)
}
frm.SetSourcePort(server.LocalPort())
frm.SetDestinationPort(client.LocalPort())
frm.SetSegment(Segment{
SEQ: server.scb.snd.NXT,
ACK: preRewindNXT, // ACKs all data sent before the rewind.
Flags: FlagACK,
WND: 65535,
}, 5)
err = client.Recv(buf[:sizeHeaderTCP])
if err != nil {
t.Fatalf("BUG: cumulative recovery ACK rejected: %v\n"+
"After fast retransmit rewound snd.NXT and client re-sent one packet,\n"+
"the remote's cumulative ACK (seg.ACK=%d) exceeds the current snd.NXT=%d\n"+
"and is incorrectly rejected as 'acks unsent data'.\n"+
"This causes spurious retransmissions of already-received data.",
err, preRewindNXT, client.scb.snd.NXT)
}
}
// snd.UNA should have advanced to cover all original data.
if client.scb.snd.UNA != preRewindNXT {
t.Fatalf("snd.UNA not advanced: got %d, want %d", client.scb.snd.UNA, preRewindNXT)
}
// snd.NXT should be at least preRewindNXT.
if client.scb.snd.NXT.LessThan(preRewindNXT) {
t.Fatalf("snd.NXT behind preRewindNXT: got %d, want >= %d", client.scb.snd.NXT, preRewindNXT)
}
// No more data should be sent — any Send() output here is a spurious retransmission.
clear(rawbuf[:])
n, err = client.Send(rawbuf[:])
if err != nil {
t.Fatal("client send after recovery:", err)
}
if n != 0 {
t.Fatalf("BUG: spurious retransmission after recovery ACK: sent %d bytes.\n"+
"All data was already acknowledged by the cumulative ACK, but the client\n"+
"still has 'unsent' data in the TX buffer that was actually received.", n)
}
}
// TestFastRetransmitOncePerLoss is a regression test for
// https://github.com/soypat/lneto/issues/58
// where fast retransmit was triggered multiple times for the same lost segment.
//
// When N packets are in flight and one is lost, up to N dup ACKs arrive.
// The bug: triggerRetransmit() reset dupACKs to 0, so every 3 dup ACKs
// triggered another fast retransmit of the same sequence. With 10 packets
// in flight, a single loss caused 3 retransmissions instead of 1.
//
// Per RFC 5681 §3.2, fast retransmit should fire once per loss event.
// Subsequent dup ACKs (beyond the 3rd) should NOT re-trigger it.
func TestFastRetransmitOncePerLoss(t *testing.T) {
const mtu = 60 // Small MTU: 20 byte header + 40 bytes payload per packet.
const txBuf = 2048
const maxpackets = 10
rng := rand.New(rand.NewSource(58))
client := new(Handler)
server := new(Handler)
err := client.SetBuffers(make([]byte, txBuf), make([]byte, txBuf), maxpackets)
if err != nil {
t.Fatal(err)
}
client.rto = rtoInitial
err = server.SetBuffers(make([]byte, txBuf), make([]byte, txBuf), maxpackets)
if err != nil {
t.Fatal(err)
}
server.rto = rtoInitial
err = server.OpenListen(uint16(rng.Uint32()), 0)
if err != nil {
t.Fatal(err)
}
err = client.OpenActive(uint16(rng.Uint32()), server.LocalPort(), 0)
if err != nil {
t.Fatal(err)
}
var rawbuf [mtu]byte
establish(t, client, server, rawbuf[:])
// With MSS=40 (mtu-20), write enough to fill several packets.
data := make([]byte, 40*maxpackets)
for i := range data {
data[i] = byte(i)
}
// Write in chunks since TX buffer may limit us.
written := 0
var packets [][]byte
for written < len(data) {
n, werr := client.Write(data[written:])
if werr != nil {
t.Fatal("client write:", werr)
}
written += n
// Send as many packets as possible.
for {
clear(rawbuf[:])
ns, serr := client.Send(rawbuf[:])
if serr != nil {
t.Fatal("client send:", serr)
}
if ns == 0 {
break
}
packets = append(packets, append([]byte(nil), rawbuf[:ns]...))
}
}
if len(packets) < 6 {
t.Fatal("need at least 6 data packets, got", len(packets))
}
t.Logf("sent %d data packets", len(packets))
// Server receives only the first packet so it ACKs it, advancing client's lastACK.
err = server.Recv(packets[0])
if err != nil {
t.Fatal("server recv first packet:", err)
}
clear(rawbuf[:])
n, err := server.Send(rawbuf[:])
if err != nil {
t.Fatal("server send ACK:", err)
}
if n == 0 {
t.Fatal("expected server to send ACK")
}
// Client receives the ACK for the first packet, establishing lastACK.
err = client.Recv(rawbuf[:n])
if err != nil {
t.Fatal("client recv ACK:", err)
}
dupACKValue := client.lastACK
t.Logf("lastACK established at %d, client.dupACKs=%d", dupACKValue, client.dupACKs)
// Craft 9 identical dup ACKs: same ACK value, no data, no SYN/FIN.
// These simulate what the server would send on receiving out-of-order packets.
const numDupAcks = 9
var dupAcks [numDupAcks][]byte
for i := range dupAcks {
var buf [mtu]byte
frm, ferr := NewFrame(buf[:])
if ferr != nil {
t.Fatal("new frame:", ferr)
}
frm.SetSourcePort(server.LocalPort())
frm.SetDestinationPort(client.LocalPort())
frm.SetSegment(Segment{
SEQ: server.scb.snd.NXT,
ACK: dupACKValue,
Flags: FlagACK,
WND: 65535,
}, 5)
dupAcks[i] = append([]byte(nil), buf[:sizeHeaderTCP]...)
}
t.Logf("crafted %d dup ACKs", numDupAcks)
// Feed all dup ACKs to client, counting how many times fast retransmit fires.
// Between dup ACKs, call Send() to transmit retransmitted packets (as a real
// stack would do). This makes BufferedSent > 0 again, which is required for
// the dup ACK condition to be met.
retransmitCount := 0
prevNRetx := client.nRetx
for i, ack := range dupAcks[:] {
rerr := client.Recv(ack)
if rerr != nil {
continue
}
if client.nRetx > prevNRetx {
retransmitCount++
t.Logf("fast retransmit #%d triggered at dup ACK %d (nRetx=%d)", retransmitCount, i+1, client.nRetx)
prevNRetx = client.nRetx
}
// Simulate real behavior: Send() is called between receives,
// which re-sends retransmitted data and makes BufferedSent > 0.
clear(rawbuf[:])
client.Send(rawbuf[:])
}
if retransmitCount == 0 {
t.Fatal("fast retransmit never triggered (expected exactly 1)")
}
if retransmitCount > 1 {
t.Fatalf("BUG (issue #58): fast retransmit triggered %d times for a single loss event, want 1.\n"+
"triggerRetransmit() resets dupACKs to 0, causing every 3rd dup ACK to\n"+
"re-trigger fast retransmit for the same lost sequence.", retransmitCount)
}
}
// TestFastRetransmitResetsOnNewACK verifies that after recovering from a loss
// event (new data ACKed), the dup-ACK counter resets so that a subsequent loss
// can trigger fast retransmit again.
//
// Without the dupACKs=0 reset on new ACK (handler.go line 219), the counter
// would stay above 3 after the first loss event and never reach ==3 again,
// disabling fast retransmit for all subsequent losses.
func TestFastRetransmitResetsOnNewACK(t *testing.T) {
const mtu = 60 // Small MTU: 20 byte header + 40 bytes payload per packet.
const txBuf = 2048
const maxpackets = 10
rng := rand.New(rand.NewSource(59))
client := new(Handler)
server := new(Handler)
err := client.SetBuffers(make([]byte, txBuf), make([]byte, txBuf), maxpackets)
if err != nil {
t.Fatal(err)
}
client.rto = rtoInitial
err = server.SetBuffers(make([]byte, txBuf), make([]byte, txBuf), maxpackets)
if err != nil {
t.Fatal(err)
}
server.rto = rtoInitial
err = server.OpenListen(uint16(rng.Uint32()), 0)
if err != nil {
t.Fatal(err)
}
err = client.OpenActive(uint16(rng.Uint32()), server.LocalPort(), 0)
if err != nil {
t.Fatal(err)
}
var rawbuf [mtu]byte
establish(t, client, server, rawbuf[:])
// Helper: craft a dup ACK packet from server to client for the given ACK value.
craftDupACK := func(ackVal Value) []byte {
var buf [mtu]byte
frm, ferr := NewFrame(buf[:])
if ferr != nil {
t.Fatal("new frame:", ferr)
}
frm.SetSourcePort(server.LocalPort())
frm.SetDestinationPort(client.LocalPort())
frm.SetSegment(Segment{
SEQ: server.scb.snd.NXT,
ACK: ackVal,
Flags: FlagACK,
WND: 65535,
}, 5)
return append([]byte(nil), buf[:sizeHeaderTCP]...)
}
// Helper: write data, send packets, return them.
sendPackets := func(payload []byte) [][]byte {
written := 0
var pkts [][]byte
for written < len(payload) {
n, werr := client.Write(payload[written:])
if werr != nil {
t.Fatal("client write:", werr)
}
written += n
for {
clear(rawbuf[:])
ns, serr := client.Send(rawbuf[:])
if serr != nil {
t.Fatal("client send:", serr)
}
if ns == 0 {
break
}
pkts = append(pkts, append([]byte(nil), rawbuf[:ns]...))
}
}
return pkts
}
// Helper: deliver packet to server, get ACK, deliver ACK to client.
deliverAndACK := func(pkt []byte) {
rerr := server.Recv(pkt)
if rerr != nil {
t.Fatal("server recv:", rerr)
}
clear(rawbuf[:])
n, serr := server.Send(rawbuf[:])
if serr != nil {
t.Fatal("server send:", serr)
}
if n == 0 {
t.Fatal("expected server to send ACK")
}
rerr = client.Recv(rawbuf[:n])
if rerr != nil {
t.Fatal("client recv ACK:", rerr)
}
}
// === Loss event #1 ===
packets1 := sendPackets(make([]byte, 40*4))
if len(packets1) < 4 {
t.Fatal("need at least 4 packets for loss event #1, got", len(packets1))
}
// Deliver the first packet to establish lastACK.
deliverAndACK(packets1[0])
ackVal1 := client.lastACK
// Send 3 dup ACKs to trigger fast retransmit.
for i := 0; i < 3; i++ {
err = client.Recv(craftDupACK(ackVal1))
if err != nil {
t.Fatalf("loss #1: dup ACK %d recv: %v", i+1, err)
}
clear(rawbuf[:])
client.Send(rawbuf[:]) // Keep BufferedSent > 0.
}
if client.nRetx != 1 {
t.Fatalf("loss #1: expected nRetx=1 after 3 dup ACKs, got %d", client.nRetx)
}
t.Logf("loss #1: fast retransmit triggered (nRetx=%d)", client.nRetx)
// === Recovery: retransmit the lost packet, server ACKs all data ===
// Re-send all the packets that server missed (client retransmits from UNA).
for {
clear(rawbuf[:])
n, serr := client.Send(rawbuf[:])
if serr != nil {
t.Fatal("client retransmit send:", serr)
}
if n == 0 {
break
}
_ = server.Recv(rawbuf[:n]) // Deliver retransmitted + new data.
}
// Deliver remaining original packets too.
for i := 1; i < len(packets1); i++ {
_ = server.Recv(packets1[i])
}
// Server sends cumulative ACK for all received data.
clear(rawbuf[:])
n, serr := server.Send(rawbuf[:])
if serr != nil {
t.Fatal("server send recovery ACK:", serr)
}
if n == 0 {
t.Fatal("expected server to send recovery ACK")
}
err = client.Recv(rawbuf[:n])
if err != nil {
t.Fatal("client recv recovery ACK:", err)
}
t.Logf("recovery: nRetx=%d, lastACK=%d", client.nRetx, client.lastACK)
// === Loss event #2 ===
packets2 := sendPackets(make([]byte, 40*4))
if len(packets2) < 4 {
t.Fatal("need at least 4 packets for loss event #2, got", len(packets2))
}
// Deliver the first packet to advance lastACK.
deliverAndACK(packets2[0])
ackVal2 := client.lastACK
if ackVal2 == ackVal1 {
t.Fatal("lastACK did not advance between loss events")
}
// Send 3 dup ACKs — fast retransmit should trigger again.
prevNRetx := client.nRetx
for i := 0; i < 3; i++ {
err = client.Recv(craftDupACK(ackVal2))
if err != nil {
t.Fatalf("loss #2: dup ACK %d recv: %v", i+1, err)
}
clear(rawbuf[:])
client.Send(rawbuf[:]) // Keep BufferedSent > 0.
}
if client.nRetx <= prevNRetx {
t.Fatalf("BUG: fast retransmit did NOT trigger for loss event #2.\n"+
"nRetx=%d (was %d).\n"+
"The dup-ACK counter was not reset after recovery from loss event #1,\n"+
"so it never reached the threshold again.", client.nRetx, prevNRetx)
}
t.Logf("loss #2: fast retransmit triggered (nRetx=%d)", client.nRetx)
}
+3 -3
View File
@@ -231,8 +231,8 @@ func TestExchange_rfc9293_figure12(t *testing.T) {
Incoming: &tcp.Segment{SEQ: issB, ACK: issA + 1, Flags: tcp.FlagACK, WND: windowB},
WantState: tcp.StateFinWait2,
WantPeerState: tcp.StateCloseWait,
// TODO(soypat): WantPending should be nil here? Perhaps fix test by modifying rcvFinWait1 pending result.
WantPending: &tcp.Segment{SEQ: issA + 1, ACK: issB, Flags: tcp.FlagACK, WND: windowA},
// RFC Figure 12: no response from A here — bare ACK must not elicit ACK.
WantPending: nil,
},
2: { // A receives FIN|ACK from B.
Incoming: &tcp.Segment{SEQ: issB, ACK: issA + 1, Flags: FINACK, WND: windowB},
@@ -496,7 +496,7 @@ func TestExchange_helloworld(t *testing.T) {
11: { // A receives B's ACK of FIN.
Incoming: &tcp.Segment{SEQ: issB + 1 + 2*datalen, ACK: issA + 2 + 2*datalen, Flags: tcp.FlagACK, WND: windowB},
WantState: tcp.StateFinWait2,
WantPending: &tcp.Segment{SEQ: issA + 2 + 2*datalen, ACK: issB + 1 + 2*datalen, Flags: tcp.FlagACK, WND: windowA},
WantPending: nil, // Bare ACK must not elicit ACK (same as Figure 12 fix).
WantPeerState: tcp.StateCloseWait,
},
}
+1 -1
View File
@@ -91,7 +91,7 @@ func TestExchangeTest_figure12(t *testing.T) {
Action: tcp.StepBSends,
AState: tcp.StateFinWait2,
BState: tcp.StateCloseWait,
APending: &tcp.Segment{SEQ: issA + 1, ACK: issB, Flags: tcp.FlagACK, WND: windowA}, // TODO: should be nil?
APending: nil, // RFC Figure 12 shows no response from A here — bare ACK must not elicit ACK.
},
2: { // B calls Close() (RFC 9293 Figure 12 step 4). B goes to LAST-ACK.
Action: tcp.StepBCloses,
+28
View File
@@ -233,6 +233,34 @@ func (rtx *ringTx) RetransmitFromUNA() {
rtx.slist.Reset(cap(rtx.slist.pkts), unaSeq)
}
// RecoveryACK processes a cumulative ACK that covers data sent before a
// retransmit rewind. After RetransmitFromUNA merged sent→unsent and cleared
// the sentlist, a recovery ACK may exceed what's currently in the sentlist.
// This method acks any sentlist entries, then skips unsent bytes that were
// implicitly acknowledged (they were received by the remote before the rewind).
func (rtx *ringTx) RecoveryACK(ack Value) {
size := rtx.Size()
// First, ack everything in the sentlist (if any packets were re-sent).
if newest := rtx.slist.Newest(); newest != nil {
rtx.slist.RecvAck(newest.endSeq(), size)
}
rtx.sentoff = 0
rtx.sentend = 0
// Skip unsent data that was implicitly acked. The sequence of the first
// unsent byte is slist.ssn (the end-seq of the last acked packet).
excess := int32(ack - rtx.slist.ssn)
if excess > 0 && rtx.unsentend != 0 {
rtx.unsentoff = addOff(rtx.unsentoff, int(excess), size)
if rtx.unsentoff == rtx.unsentend {
rtx.unsentoff = 0
rtx.unsentend = 0
}
}
rtx.slist.Reset(cap(rtx.slist.pkts), ack)
rtx.consolidateBufs()
}
func (rtx *ringTx) consolidateBufs() {
unsentEmpty := rtx.unsentend == 0
sentEmpty := rtx.sentend == 0
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00'\xff\xff@\x05@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff0000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?\x00\x04 #P\x10\xfaܵ\x02\x05\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000XB000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n000000000000000000000000000000000000000000000000000000000000: 00000000000000\n0000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000\n00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000:00000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x04 #P\x10\x05ܯ\x02\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00 000000000000\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00G0\x00 00000\x01000000\n\x00\x00\xff000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x06000000\n\x00\x00\xff\x00\x00\x00P0000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000 0 0\n0")
@@ -0,0 +1,3 @@
go test fuzz v1
int(6)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00\x00\x00\x10\xc0@\x00\x04 #P\x02\x05ܯ\a\x00P")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0@\x00\x04 #P\x02\x05ܯ\a\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB000000\x01\x01\x01\x010000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000\xf3B0000000000000000000000000000000000000000000000\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x06000000\n\x00\x00\xff000000000000X0000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000XB000000\r0000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(6)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0@\x00\x04 #P\x02\x05ܯ\a\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(6)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x04 #P\x02\x05ܯ\x10\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x05@>%\x81\n\x00\x00\x10\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB000000\x02\x04\x00\x020000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(7)
[]byte("0")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\xff\xff\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x04 #P\x10\x05ܯ\x02\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x06000000\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(6)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00$00000\x1100\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0@\x00\x04 #\xff\x02\x05\xdc0000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x06")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00\xff00000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00 00000\x01000000\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xff\xff\xff\xff\xff000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xff0000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000\xf3B000000\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000\xf3B000000\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000\n00000000000000000000000000000:0\n000000000000000000000000000000000000000000000000000000000000000000000:00000000000000000000000000000000000000000000000000000000000\n000000000000000000000000000000000000000:0\n000000000000000000000000000:0\n000000000000000000000000000000000000000000000000000000000000000000000:00000000000000000000000000000000000000000000000000000000000\n000000000000000000000000000000000000000:0\n000000000000000000000000000:0\n00000000000000000000000000000000000:0\n0000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x0000\x00 000000000000\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x06000000\n\x00\x00\xff000000000000X0000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(6)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?\x00\x04 #P\x10\xfaܵ\x02\x05\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000\n\r0")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB0000000\x03000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00 00000>000000\n\x00\x00\xff000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000XB000000\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000\n:\n")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?0000XA\x00\x000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000\xf3B0000000\n00000000\x03\x030\x03\x030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x06\x00\x01\b\x00\x06\x04\x00\x0200000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000XB0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n000000000000000000000000000000000000000000000000000000000000: 0000000000\n000000000000000000000000000000000000000000000000: 0000000000\n0000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(7)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x060000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000\xf3B0000000\n000000000\x0300\x0300\x0300\x0300\x0300\x030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0@\x00\x04 #\xaf\xdcP\a\x05\x02\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00H0\x00 00000\x06000000\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(6)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x02 #P\x00\x05ܯ\x10\x00\x04")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x02000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000\n000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:00000000000000000000000000000000000000000000000000000000000\n000000000000000000000000000000000000000000000000000000000000000000000000000000:0\n000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB000000\x03\x03000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x04 #P\x10\x05ܯ\x02\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P0000\x00\x04 \"X2000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00)00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?0000X 0000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x0000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?\xfa000X1000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000XB000000\r\r\r\r\r\r\r\r")
@@ -0,0 +1,3 @@
go test fuzz v1
int(1)
[]byte("00000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00*00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?0000XA\x00\x00000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00800000\x06000000\n\x00\x00\xff00\x00P00000000XB000000\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB0000000\x020\x020000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0\x02\x00\x04 #P@\x05ܯ\a\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00)00000\x06000000\n\x00\x00\xff000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x0600000N00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000XB00000000000\r\n0")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00 00000\x01000000\n\x00\x00\xff0000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?0000X\x01000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x04 #P\x10\x05ܯ\x02\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?\x00\xdc #P\x10\xfa\x04\xb5\x02\x05\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0@\x00\x04 #P\a\x05ܯ\x02\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x04\x0000000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P00000000XB000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000 0")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x06000000\n\x00\x00\xff000000000000a0000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB000000\x02\x04000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\xff\xff\x00\x10\xc0@\x00\x04 #P\x02\x05ܯ\a\x00P")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB000000\x02\x04\x00\x000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\x81\x000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00*00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?0000X 00000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00(00000\x0600\n\x00\x00\x00\n\x00\x00\xff\x059\x00P0000\x00\x04 \"Y\xfb000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00000000\x06000000\n\x00\x00\xff00\x00P00000000aB000000\x010000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00\xff00000\x06000000\n\x00\x00\xff0000000000000B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\x81\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x06\x00\x01\b\x00\x06\x04\x00\x0100000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\x10\xc0@\x00\x04 #P\x02\x05ܯ\a\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(7)
[]byte("\xbe\xef\x00\x00\x00\xff000000\x00\b0E\x00 00000p000000\n\x00\x00\xff00000000000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(2)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x11%\xc3\n\x00\x00\x00\n\x00\x00\xff0000\x00\x1100000000000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E0\x00 00000\x11000000\n\x00\x00\xff0000\x00\x00000000")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0@\x00\x02 #P\x04\x05ܯ\x10\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\a\xc0?\xfa\x04 #P\x00\x05ܵ\x10\x00\x02")
@@ -0,0 +1,3 @@
go test fuzz v1
int(0)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x00E\x00\x00(\x00\x04@\x00@\x06%\xce\n\x00\x00\x00\n\x00\x00\xff\x059\x00P\x00\b\xc0@\x00\x04 \x02P+\x05ܯ\a\x00\x00")
@@ -0,0 +1,3 @@
go test fuzz v1
int(3)
[]byte("\xbe\xef\x00\x00\x00\xff000000\b\x06\x00\x01\b\x00\x06\x040000000000000000000000")

Some files were not shown because too many files have changed in this diff Show More