Skip to content

Commit ec5acd4

Browse files
dcantahdmcgowan
authored andcommitted
CRI stream server: Fix goroutine leak in Exec
In the CRI streaming server, a goroutine (`handleResizeEvents`) is launched to handle terminal resize events if a TTY is asked for with an exec; this is the sender of terminal resize events. Another goroutine is launched shortly after successful process startup to actually do something with these events, however the issue arises if the exec process fails to start for any reason that would have `process.Start` return non-nil. The receiver goroutine never gets launched so the sender is stuck blocked on a channel send infinitely. This could be used in a malicious manner by repeatedly launching execs with a command that doesn't exist in the image, as a single goroutine will get leaked on every invocation which will slowly grow containerd's memory usage. Signed-off-by: Danny Canter <[email protected]> (cherry picked from commit f012617)
1 parent 52a4492 commit ec5acd4

File tree

1 file changed

+12
-3
lines changed

1 file changed

+12
-3
lines changed

pkg/cri/streaming/remotecommand/httpstream.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ limitations under the License.
3333
package remotecommand
3434

3535
import (
36+
gocontext "context"
3637
"encoding/json"
3738
"errors"
3839
"fmt"
@@ -132,7 +133,7 @@ func createStreams(req *http.Request, w http.ResponseWriter, opts *Options, supp
132133

133134
if ctx.resizeStream != nil {
134135
ctx.resizeChan = make(chan remotecommand.TerminalSize)
135-
go handleResizeEvents(ctx.resizeStream, ctx.resizeChan)
136+
go handleResizeEvents(req.Context(), ctx.resizeStream, ctx.resizeChan)
136137
}
137138

138139
return ctx, true
@@ -425,7 +426,7 @@ WaitForStreams:
425426
// supportsTerminalResizing returns false because v1ProtocolHandler doesn't support it.
426427
func (*v1ProtocolHandler) supportsTerminalResizing() bool { return false }
427428

428-
func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalSize) {
429+
func handleResizeEvents(ctx gocontext.Context, stream io.Reader, channel chan<- remotecommand.TerminalSize) {
429430
defer runtime.HandleCrash()
430431
defer close(channel)
431432

@@ -435,7 +436,15 @@ func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalS
435436
if err := decoder.Decode(&size); err != nil {
436437
break
437438
}
438-
channel <- size
439+
440+
select {
441+
case channel <- size:
442+
case <-ctx.Done():
443+
// To avoid leaking this routine, exit if the http request finishes. This path
444+
// would generally be hit if starting the process fails and nothing is started to
445+
// ingest these resize events.
446+
return
447+
}
439448
}
440449
}
441450

0 commit comments

Comments
 (0)