Skip to content

Commit ff15cd5

Browse files
Bryan C. Millsgopherbot
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

Lines changed: 16 additions & 2 deletions
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

Lines changed: 49 additions & 42 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy