From cf3bde2b5a78d7ba8773eadcc3b28dfb0001aee0 Mon Sep 17 00:00:00 2001 From: zhongjiawei Date: Mon, 4 Jul 2022 14:34:23 +0800 Subject: [PATCH] containerd: Limit the response size of ExecSync fix CVE-2022-31030 upstream:https://github.com/containerd/containerd/commit/c1bcabb4541930f643aa36a2b38655e131346382 --- .../cri/pkg/server/container_execsync.go | 45 ++++++++++++++++- .../cri/pkg/server/container_execsync_test.go | 49 +++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 vendor/github.com/containerd/cri/pkg/server/container_execsync_test.go diff --git a/vendor/github.com/containerd/cri/pkg/server/container_execsync.go b/vendor/github.com/containerd/cri/pkg/server/container_execsync.go index fd54120..1ef93e5 100644 --- a/vendor/github.com/containerd/cri/pkg/server/container_execsync.go +++ b/vendor/github.com/containerd/cri/pkg/server/container_execsync.go @@ -37,14 +37,55 @@ import ( "github.com/containerd/cri/pkg/util" ) +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 { diff --git a/vendor/github.com/containerd/cri/pkg/server/container_execsync_test.go b/vendor/github.com/containerd/cri/pkg/server/container_execsync_test.go new file mode 100644 index 0000000..c8641d0 --- /dev/null +++ b/vendor/github.com/containerd/cri/pkg/server/container_execsync_test.go @@ -0,0 +1,49 @@ +/* + 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) +} -- 2.30.0