diff -Nru containerd-1.6.4/debian/changelog containerd-1.6.4/debian/changelog --- containerd-1.6.4/debian/changelog 2022-05-11 20:48:49.000000000 +0000 +++ containerd-1.6.4/debian/changelog 2022-12-12 10:43:31.000000000 +0000 @@ -1,3 +1,16 @@ +containerd (1.6.4-0ubuntu1.1) kinetic-security; urgency=medium + + * SECURITY UPDATE: Memory exhaustion through Exec + - debian/patches/CVE-2022-23471.patch: Prevent goroutine leak in Exec + in pkg/cri/streaming/remotecommand/httpstream.go. + - CVE-2022-23471 + * SECURITY UPDATE: Memory exhaustion through ExecSync. + - debian/patches/CVE-2022-31030.patch: limit the response size + of ExecSync in pkg/cri/server/container_execsync.go. + - CVE-2022-31030 + + -- David Fernandez Gonzalez Mon, 12 Dec 2022 11:43:31 +0100 + containerd (1.6.4-0ubuntu1) kinetic; urgency=medium * New upstream release. diff -Nru containerd-1.6.4/debian/patches/CVE-2022-23471.patch containerd-1.6.4/debian/patches/CVE-2022-23471.patch --- containerd-1.6.4/debian/patches/CVE-2022-23471.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.6.4/debian/patches/CVE-2022-23471.patch 2022-12-12 10:32:59.000000000 +0000 @@ -0,0 +1,71 @@ +From f012617edfd887a29345888d65640a7ccd7c72ce Mon Sep 17 00:00:00 2001 +From: Danny Canter +Date: Mon, 28 Nov 2022 14:45:34 -0800 +Subject: [PATCH] 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 +--- + pkg/cri/streaming/remotecommand/httpstream.go | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/pkg/cri/streaming/remotecommand/httpstream.go b/pkg/cri/streaming/remotecommand/httpstream.go +index 0417a1a9e66..9177fa794d2 100644 +--- a/pkg/cri/streaming/remotecommand/httpstream.go ++++ b/pkg/cri/streaming/remotecommand/httpstream.go +@@ -33,6 +33,7 @@ limitations under the License. + package remotecommand + + import ( ++ gocontext "context" + "encoding/json" + "errors" + "fmt" +@@ -132,7 +133,7 @@ func createStreams(req *http.Request, w http.ResponseWriter, opts *Options, supp + + if ctx.resizeStream != nil { + ctx.resizeChan = make(chan remotecommand.TerminalSize) +- go handleResizeEvents(ctx.resizeStream, ctx.resizeChan) ++ go handleResizeEvents(req.Context(), ctx.resizeStream, ctx.resizeChan) + } + + return ctx, true +@@ -425,7 +426,7 @@ WaitForStreams: + // supportsTerminalResizing returns false because v1ProtocolHandler doesn't support it. + func (*v1ProtocolHandler) supportsTerminalResizing() bool { return false } + +-func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalSize) { ++func handleResizeEvents(ctx gocontext.Context, stream io.Reader, channel chan<- remotecommand.TerminalSize) { + defer runtime.HandleCrash() + defer close(channel) + +@@ -435,7 +436,15 @@ func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalS + if err := decoder.Decode(&size); err != nil { + break + } +- channel <- size ++ ++ select { ++ case channel <- size: ++ case <-ctx.Done(): ++ // To avoid leaking this routine, exit if the http request finishes. This path ++ // would generally be hit if starting the process fails and nothing is started to ++ // ingest these resize events. ++ return ++ } + } + } + diff -Nru containerd-1.6.4/debian/patches/CVE-2022-31030.patch containerd-1.6.4/debian/patches/CVE-2022-31030.patch --- containerd-1.6.4/debian/patches/CVE-2022-31030.patch 1970-01-01 00:00:00.000000000 +0000 +++ containerd-1.6.4/debian/patches/CVE-2022-31030.patch 2022-12-12 10:39:52.000000000 +0000 @@ -0,0 +1,127 @@ +From 49ca87d7270091b8193301dc2f6759e9aa7c97b1 Mon Sep 17 00:00:00 2001 +From: Kazuyoshi Kato +Date: Mon, 16 May 2022 17:33:33 +0000 +Subject: [PATCH 1/2] Limit the response size of ExecSync + +Signed-off-by: Kazuyoshi Kato +--- + pkg/cri/server/container_execsync.go | 39 +++++++++++++++++- + pkg/cri/server/container_execsync_test.go | 49 +++++++++++++++++++++++ + 2 files changed, 86 insertions(+), 2 deletions(-) + create mode 100644 pkg/cri/server/container_execsync_test.go + +--- containerd-1.6.4.orig/pkg/cri/server/container_execsync.go ++++ containerd-1.6.4/pkg/cri/server/container_execsync.go +@@ -38,14 +38,55 @@ import ( + cioutil "github.com/containerd/containerd/pkg/ioutil" + ) + ++type cappedWriter struct { ++ w io.WriteCloser ++ remain int ++} ++ ++func (cw *cappedWriter) Write(p []byte) (int, error) { ++ if cw.remain <= 0 { ++ return len(p), nil ++ } ++ ++ end := cw.remain ++ if end > len(p) { ++ end = len(p) ++ } ++ written, err := cw.w.Write(p[0:end]) ++ cw.remain -= written ++ ++ if err != nil { ++ return written, err ++ } ++ return len(p), nil ++} ++ ++func (cw *cappedWriter) Close() error { ++ return cw.w.Close() ++} ++ ++func (cw *cappedWriter) isFull() bool { ++ return cw.remain <= 0 ++} ++ + // ExecSync executes a command in the container, and returns the stdout output. + // If command exits with a non-zero exit code, an error is returned. + func (c *criService) ExecSync(ctx context.Context, r *runtime.ExecSyncRequest) (*runtime.ExecSyncResponse, error) { ++ const maxStreamSize = 1024 * 1024 * 16 ++ + var stdout, stderr bytes.Buffer ++ ++ // cappedWriter truncates the output. In that case, the size of ++ // the ExecSyncResponse will hit the CRI plugin's gRPC response limit. ++ // Thus the callers outside of the containerd process (e.g. Kubelet) never see ++ // the truncated output. ++ cout := &cappedWriter{w: cioutil.NewNopWriteCloser(&stdout), remain: maxStreamSize} ++ cerr := &cappedWriter{w: cioutil.NewNopWriteCloser(&stderr), remain: maxStreamSize} ++ + exitCode, err := c.execInContainer(ctx, r.GetContainerId(), execOptions{ + cmd: r.GetCmd(), +- stdout: cioutil.NewNopWriteCloser(&stdout), +- stderr: cioutil.NewNopWriteCloser(&stderr), ++ stdout: cout, ++ stderr: cerr, + timeout: time.Duration(r.GetTimeout()) * time.Second, + }) + if err != nil { +--- /dev/null ++++ containerd-1.6.4/pkg/cri/server/container_execsync_test.go +@@ -0,0 +1,52 @@ ++/* ++ Copyright The containerd Authors. ++ ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. ++*/ ++ ++package server ++ ++import ( ++ "bytes" ++ "testing" ++ ++ cioutil "github.com/containerd/containerd/pkg/ioutil" ++ "github.com/stretchr/testify/assert" ++) ++ ++func TestCWWrite(t *testing.T) { ++ var buf bytes.Buffer ++ cw := &cappedWriter{w: cioutil.NewNopWriteCloser(&buf), remain: 10} ++ ++ n, err := cw.Write([]byte("hello")) ++ assert.NoError(t, err) ++ assert.Equal(t, 5, n) ++ ++ n, err = cw.Write([]byte("helloworld")) ++ assert.NoError(t, err, "no errors even it hits the cap") ++ assert.Equal(t, 10, n, "no indication of partial write") ++ assert.True(t, cw.isFull()) ++ assert.Equal(t, []byte("hellohello"), buf.Bytes(), "the underlying writer is capped") ++ ++ _, err = cw.Write([]byte("world")) ++ assert.NoError(t, err) ++ assert.True(t, cw.isFull()) ++ assert.Equal(t, []byte("hellohello"), buf.Bytes(), "the underlying writer is capped") ++} ++ ++func TestCWClose(t *testing.T) { ++ var buf bytes.Buffer ++ cw := &cappedWriter{w: cioutil.NewNopWriteCloser(&buf), remain: 5} ++ err := cw.Close() ++ assert.NoError(t, err) ++} diff -Nru containerd-1.6.4/debian/patches/series containerd-1.6.4/debian/patches/series --- containerd-1.6.4/debian/patches/series 2022-05-11 20:05:15.000000000 +0000 +++ containerd-1.6.4/debian/patches/series 2022-12-12 10:33:31.000000000 +0000 @@ -1,3 +1,5 @@ skip-tests-with-privilege.patch preserve-debug-info.patch build-gen-manpanges-instead-of-go-run.patch +CVE-2022-23471.patch +CVE-2022-31030.patch