Skip to content

Commit

Permalink
add test replicating panic encountered by @knieriem in #19 (#21)
Browse files Browse the repository at this point in the history
* add test replicating panic encountered by @knieriem

* apply @knieriem's fix
  • Loading branch information
soypat authored May 7, 2024
1 parent bde06f1 commit d1b5854
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 7 deletions.
14 changes: 7 additions & 7 deletions control_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,19 @@ func (tcb *ControlBlock) Recv(seg Segment) (err error) {
if seg.Flags.HasAny(FlagACK) {
tcb.close()
}
// case StateClosing:
// if seg.Flags.HasAny(FlagACK) {
// tcb.state = StateTimeWait
// pending = FlagACK
// }
case StateClosing:
// Thanks to @knieriem for finding and reporting this bug.
if seg.Flags.HasAny(FlagACK) {
tcb.state = StateTimeWait
}
default:
panic("unexpected state" + tcb.state.String())
panic("unexpected recv state:" + tcb.state.String())
}
if err != nil {
return err
}

tcb.pending[0] = pending
tcb.pending[0] |= pending
if prevNxt != 0 && tcb.snd.NXT != prevNxt && tcb.logenabled(slog.LevelDebug) {
tcb.debug("tcb:snd.nxt-change", slog.String("state", tcb.state.String()),
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)),
Expand Down
97 changes: 97 additions & 0 deletions seqs_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package seqs_test

import (
"log/slog"
"os"
"strconv"
"testing"

Expand Down Expand Up @@ -701,3 +703,98 @@ func TestUnexpectedStateClosing(t *testing.T) {
}
tcb.HelperExchange(t, ex[:])
}

// This corresponds to https://github.com/soypat/seqs/issues/19
// The bug consisted of a panic condition encountered when using wget client with a seqs based server.
// Thanks to @knieriem for finding this and the detailed report they submitted.
func TestIssue19(t *testing.T) {
var tcb seqs.ControlBlock
tcb.SetLogger(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug - 2,
})))
assertState := func(state seqs.State) {
t.Helper()
if tcb.State() != state {
t.Fatalf("want state %s; got %s", state.String(), tcb.State().String())
}
}
const httpLen = 1192
const issA, issB, windowA, windowB = 1, 0, 2000, 2000
tcb.HelperInitState(seqs.StateEstablished, issA, issA, windowA)
tcb.HelperInitRcv(issB, issB, windowB)

// Send out HTTP request and close connection.
err := tcb.Send(seqs.Segment{SEQ: issA, ACK: issB, Flags: PSHACK, WND: windowA, DATALEN: httpLen})
if err != nil {
t.Fatal(err)
}
err = tcb.Close()
if err != nil {
t.Fatal(err)
}
assertState(seqs.StateEstablished)

pending, ok := tcb.PendingSegment(0)
if !ok {
t.Fatal("expected pending segment")
} else if pending.Flags != FINACK {
t.Fatalf("expected FINACK; got %s", pending.Flags.String())
}

// Receive ACK of HTTP segment.
err = tcb.Recv(seqs.Segment{SEQ: issB, ACK: issA + httpLen, Flags: seqs.FlagACK, WND: windowB})
if err != nil {
t.Fatal(err)
}
assertState(seqs.StateEstablished)
err = tcb.Close()
if err != nil {
t.Fatal(err)
}
pending, ok = tcb.PendingSegment(0)
if !ok {
t.Fatal("expected pending segment")
} else if pending.Flags != FINACK {
t.Fatalf("expected FINACK; got %s", pending.Flags.String())
}

// Send out FINACK.
err = tcb.Send(pending)
if err != nil {
t.Fatal(err)
}
assertState(seqs.StateFinWait1)

// Receive FINACK response from client.
err = tcb.Recv(seqs.Segment{SEQ: issB, ACK: issA + httpLen, Flags: FINACK, WND: windowB})
if err != nil {
t.Fatal(err)
}
assertState(seqs.StateClosing)
pending, ok = tcb.PendingSegment(0)
if !ok {
t.Fatal("expected pending segment")
} else if pending.Flags != seqs.FlagACK {
t.Fatalf("expected ACK; got %s", pending.Flags.String())
}

// Before responding we receive an ACK from client. This is where panic is triggered.
err = tcb.Recv(seqs.Segment{SEQ: issB + 1, ACK: issA + httpLen + 1, Flags: seqs.FlagACK, WND: windowB})
if err != nil {
t.Fatal(err)
}
assertState(seqs.StateTimeWait)

// Check we still need to send an ACK.
pending, ok = tcb.PendingSegment(0)
if !ok {
t.Fatal("expected pending segment")
} else if pending.Flags != seqs.FlagACK {
t.Fatalf("expected ACK; got %s", pending.Flags.String())
}
// Prepare response to client.
err = tcb.Send(pending)
if err != nil {
t.Fatal(err)
}
}

0 comments on commit d1b5854

Please sign in to comment.