Skip to content

Commit ff15cd5

Browse files
Bryan C. Millsgopherbot
Bryan C. Mills
authored andcommitted
ssh: eliminate some goroutine leaks in tests and examples
This should fix the "Log in goroutine" panic seen in https://build.golang.org/log/e42bf69fc002113dbccfe602a6c67fd52e8f31df, as well as a few other related leaks. It also helps to verify that none of the functions under test deadlock unexpectedly. See https://go.dev/wiki/CodeReviewComments#goroutine-lifetimes. Updates golang/go#58901. Change-Id: Ica943444db381ae1accb80b101ea646e28ebf4f9 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/541095 Auto-Submit: Bryan Mills <bcmills@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicola Murino <nicola.murino@gmail.com> Reviewed-by: Heschi Kreinick <heschi@google.com>
1 parent eb61739 commit ff15cd5

File tree

3 files changed

+124
-56
lines changed

3 files changed

+124
-56
lines changed

ssh/example_test.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"os"
1717
"path/filepath"
1818
"strings"
19+
"sync"
1920

2021
"golang.org/x/crypto/ssh"
2122
"golang.org/x/crypto/ssh/terminal"
@@ -98,8 +99,15 @@ func ExampleNewServerConn() {
9899
}
99100
log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
100101

102+
var wg sync.WaitGroup
103+
defer wg.Wait()
104+
101105
// The incoming Request channel must be serviced.
102-
go ssh.DiscardRequests(reqs)
106+
wg.Add(1)
107+
go func() {
108+
ssh.DiscardRequests(reqs)
109+
wg.Done()
110+
}()
103111

104112
// Service the incoming Channel channel.
105113
for newChannel := range chans {
@@ -119,16 +127,22 @@ func ExampleNewServerConn() {
119127
// Sessions have out-of-band requests such as "shell",
120128
// "pty-req" and "env". Here we handle only the
121129
// "shell" request.
130+
wg.Add(1)
122131
go func(in <-chan *ssh.Request) {
123132
for req := range in {
124133
req.Reply(req.Type == "shell", nil)
125134
}
135+
wg.Done()
126136
}(requests)
127137

128138
term := terminal.NewTerminal(channel, "> ")
129139

140+
wg.Add(1)
130141
go func() {
131-
defer channel.Close()
142+
defer func() {
143+
channel.Close()
144+
wg.Done()
145+
}()
132146
for {
133147
line, err := term.ReadLine()
134148
if err != nil {

ssh/mux_test.go

+49-42
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"io"
1111
"sync"
1212
"testing"
13-
"time"
1413
)
1514

1615
func muxPair() (*mux, *mux) {
@@ -112,7 +111,11 @@ func TestMuxReadWrite(t *testing.T) {
112111

113112
magic := "hello world"
114113
magicExt := "hello stderr"
114+
var wg sync.WaitGroup
115+
t.Cleanup(wg.Wait)
116+
wg.Add(1)
115117
go func() {
118+
defer wg.Done()
116119
_, err := s.Write([]byte(magic))
117120
if err != nil {
118121
t.Errorf("Write: %v", err)
@@ -152,13 +155,15 @@ func TestMuxChannelOverflow(t *testing.T) {
152155
defer writer.Close()
153156
defer mux.Close()
154157

155-
wDone := make(chan int, 1)
158+
var wg sync.WaitGroup
159+
t.Cleanup(wg.Wait)
160+
wg.Add(1)
156161
go func() {
162+
defer wg.Done()
157163
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
158164
t.Errorf("could not fill window: %v", err)
159165
}
160166
writer.Write(make([]byte, 1))
161-
wDone <- 1
162167
}()
163168
writer.remoteWin.waitWriterBlocked()
164169

@@ -175,7 +180,6 @@ func TestMuxChannelOverflow(t *testing.T) {
175180
if _, err := reader.SendRequest("hello", true, nil); err == nil {
176181
t.Errorf("SendRequest succeeded.")
177182
}
178-
<-wDone
179183
}
180184

181185
func TestMuxChannelCloseWriteUnblock(t *testing.T) {
@@ -184,20 +188,21 @@ func TestMuxChannelCloseWriteUnblock(t *testing.T) {
184188
defer writer.Close()
185189
defer mux.Close()
186190

187-
wDone := make(chan int, 1)
191+
var wg sync.WaitGroup
192+
t.Cleanup(wg.Wait)
193+
wg.Add(1)
188194
go func() {
195+
defer wg.Done()
189196
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
190197
t.Errorf("could not fill window: %v", err)
191198
}
192199
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
193200
t.Errorf("got %v, want EOF for unblock write", err)
194201
}
195-
wDone <- 1
196202
}()
197203

198204
writer.remoteWin.waitWriterBlocked()
199205
reader.Close()
200-
<-wDone
201206
}
202207

203208
func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
@@ -206,28 +211,34 @@ func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
206211
defer writer.Close()
207212
defer mux.Close()
208213

209-
wDone := make(chan int, 1)
214+
var wg sync.WaitGroup
215+
t.Cleanup(wg.Wait)
216+
wg.Add(1)
210217
go func() {
218+
defer wg.Done()
211219
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
212220
t.Errorf("could not fill window: %v", err)
213221
}
214222
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
215223
t.Errorf("got %v, want EOF for unblock write", err)
216224
}
217-
wDone <- 1
218225
}()
219226

220227
writer.remoteWin.waitWriterBlocked()
221228
mux.Close()
222-
<-wDone
223229
}
224230

225231
func TestMuxReject(t *testing.T) {
226232
client, server := muxPair()
227233
defer server.Close()
228234
defer client.Close()
229235

236+
var wg sync.WaitGroup
237+
t.Cleanup(wg.Wait)
238+
wg.Add(1)
230239
go func() {
240+
defer wg.Done()
241+
231242
ch, ok := <-server.incomingChannels
232243
if !ok {
233244
t.Error("cannot accept channel")
@@ -267,6 +278,7 @@ func TestMuxChannelRequest(t *testing.T) {
267278

268279
var received int
269280
var wg sync.WaitGroup
281+
t.Cleanup(wg.Wait)
270282
wg.Add(1)
271283
go func() {
272284
for r := range server.incomingRequests {
@@ -295,7 +307,6 @@ func TestMuxChannelRequest(t *testing.T) {
295307
}
296308
if ok {
297309
t.Errorf("SendRequest(no): %v", ok)
298-
299310
}
300311

301312
client.Close()
@@ -389,27 +400,18 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
389400

390401
// Wait for the server to send the keepalive message and receive back a
391402
// response.
392-
select {
393-
case err := <-kDone:
394-
if err != nil {
395-
t.Fatal(err)
396-
}
397-
case <-time.After(10 * time.Second):
398-
t.Fatalf("server never received ack")
403+
if err := <-kDone; err != nil {
404+
t.Fatal(err)
399405
}
400406

401407
// Confirm client hasn't closed.
402408
if _, _, err := client.SendRequest("keepalive@golang.org", true, nil); err != nil {
403409
t.Fatalf("failed to send keepalive: %v", err)
404410
}
405411

406-
select {
407-
case err := <-kDone:
408-
if err != nil {
409-
t.Fatal(err)
410-
}
411-
case <-time.After(10 * time.Second):
412-
t.Fatalf("server never shut down")
412+
// Wait for the server to shut down.
413+
if err := <-kDone; err != nil {
414+
t.Fatal(err)
413415
}
414416
}
415417

@@ -525,11 +527,7 @@ func TestMuxClosedChannel(t *testing.T) {
525527
defer ch.Close()
526528

527529
// Wait for the server to close the channel and send the keepalive.
528-
select {
529-
case <-kDone:
530-
case <-time.After(10 * time.Second):
531-
t.Fatalf("server never received ack")
532-
}
530+
<-kDone
533531

534532
// Make sure the channel closed.
535533
if _, ok := <-ch.incomingRequests; ok {
@@ -541,22 +539,29 @@ func TestMuxClosedChannel(t *testing.T) {
541539
t.Fatalf("failed to send keepalive: %v", err)
542540
}
543541

544-
select {
545-
case <-kDone:
546-
case <-time.After(10 * time.Second):
547-
t.Fatalf("server never shut down")
548-
}
542+
// Wait for the server to shut down.
543+
<-kDone
549544
}
550545

551546
func TestMuxGlobalRequest(t *testing.T) {
547+
var sawPeek bool
548+
var wg sync.WaitGroup
549+
defer func() {
550+
wg.Wait()
551+
if !sawPeek {
552+
t.Errorf("never saw 'peek' request")
553+
}
554+
}()
555+
552556
clientMux, serverMux := muxPair()
553557
defer serverMux.Close()
554558
defer clientMux.Close()
555559

556-
var seen bool
560+
wg.Add(1)
557561
go func() {
562+
defer wg.Done()
558563
for r := range serverMux.incomingRequests {
559-
seen = seen || r.Type == "peek"
564+
sawPeek = sawPeek || r.Type == "peek"
560565
if r.WantReply {
561566
err := r.Reply(r.Type == "yes",
562567
append([]byte(r.Type), r.Payload...))
@@ -586,10 +591,6 @@ func TestMuxGlobalRequest(t *testing.T) {
586591
t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v",
587592
ok, data, err)
588593
}
589-
590-
if !seen {
591-
t.Errorf("never saw 'peek' request")
592-
}
593594
}
594595

595596
func TestMuxGlobalRequestUnblock(t *testing.T) {
@@ -739,7 +740,13 @@ func TestMuxMaxPacketSize(t *testing.T) {
739740
t.Errorf("could not send packet")
740741
}
741742

742-
go a.SendRequest("hello", false, nil)
743+
var wg sync.WaitGroup
744+
t.Cleanup(wg.Wait)
745+
wg.Add(1)
746+
go func() {
747+
a.SendRequest("hello", false, nil)
748+
wg.Done()
749+
}()
743750

744751
_, ok := <-b.incomingRequests
745752
if ok {

0 commit comments

Comments
 (0)