From a0e1155b2843940fe98a8afd75ee33c3e9431ecf Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 10 Jul 2023 10:57:54 +0200 Subject: [PATCH] daemon/containerd: fix assignment to entry in nil map during commit A panic would happen when converting an config that had ports exposed, because the ExposedPorts map in the OCI-spec was not initialized. This could happen when committing a container, or when using the classic builder and the parent image had ports exposed, for example FROM busybox AS stage0 EXPOSE 80 FROM stage0 AS stage1 RUN echo hello Example of the panic: 2023/07/07 15:13:02 http: panic serving @: assignment to entry in nil map goroutine 1944 [running]: net/http.(*conn).serve.func1() /usr/local/go/src/net/http/server.go:1854 +0xbf panic({0x45f660, 0xb6a8d0}) /usr/local/go/src/runtime/panic.go:890 +0x263 github.com/docker/docker/daemon/containerd.containerConfigToOciImageConfig(...) /go/src/github.com/docker/docker/daemon/containerd/image_import.go:397 github.com/docker/docker/daemon/containerd.generateCommitImageConfig({0xc001470498, {0x0, 0x0}, {0xc000c437d8, 0x5}, {0x0, 0x0}, {0xc000c43b27, 0x5}, {0x0, ...}, ...}, ...) /go/src/github.com/docker/docker/daemon/containerd/image_commit.go:138 +0x40e github.com/docker/docker/daemon/containerd.(*ImageService).CommitImage(0xc0008853e0, {0xb8f660, 0xc000c4f7c0}, {{0x0, 0x0}, {0x0, 0x0}, 0xc00104b900, 0xc00104b180, {0xc0011a7640, ...}, ...}) /go/src/github.com/docker/docker/daemon/containerd/image_commit.go:82 +0x73b github.com/docker/docker/daemon/containerd.(*ImageService).CommitBuildStep(0xc0008853e0, {0xb8f660, 0xc000c4f7c0}, {{0x0, 0x0}, {0x0, 0x0}, 0xc00104b900, 0xc00104b180, {0xc0011a7640, ...}, ...}) /go/src/github.com/docker/docker/daemon/containerd/image_commit.go:308 +0x110 github.com/docker/docker/builder/dockerfile.(*Builder).commitContainer(0xc0012b8cc0, {0xb8f660, 0xc000c4f7c0}, 0xc0010b2b60, {0xc0011a7640, 0x40}, 0xc00104b180) /go/src/github.com/docker/docker/builder/dockerfile/internals.go:61 +0x168 github.com/docker/docker/builder/dockerfile.(*Builder).commit(0xc0012b8cc0, {0xb8f660, 0xc000c4f7c0}, 0xc0010b2b60, {0xc0012a7d80?, 0xc001340060?}) /go/src/github.com/docker/docker/builder/dockerfile/internals.go:45 +0x1aa github.com/docker/docker/builder/dockerfile.dispatchLabel({0xb8f660, 0xc000c4f7c0}, {0xc0010b2b60, 0xc000c6b628, 0xc0012b8cc0, {0xb80f60, 0xc0011a46c0}, 0xc000bc2560}, 0x1e24a85?) /go/src/github.com/docker/docker/builder/dockerfile/dispatchers.go:83 +0x258 github.com/docker/docker/builder/dockerfile.dispatch({0xb8f660, 0xc000c4f7c0}, {0xc0010b2b60, 0xc000c6b628, 0xc0012b8cc0, {0xb80f60, 0xc0011a46c0}, 0xc000bc2560}, {0xb7be40, 0xc00111cde0}) /go/src/github.com/docker/docker/builder/dockerfile/evaluator.go:74 +0x529 github.com/docker/docker/builder/dockerfile.(*Builder).dispatchDockerfileWithCancellation(0xc0012b8cc0, {0xb8f660, 0xc000c4f7c0}, {0xc000b1d380, 0x1, 0xc0011a4660?}, {0x0, 0x0, 0x0?}, 0x5c, ...) /go/src/github.com/docker/docker/builder/dockerfile/builder.go:296 +0x8f2 github.com/docker/docker/builder/dockerfile.(*Builder).build(0xc0012b8cc0, {0xb8f660, 0xc000c4f7c0}, {0xb80f60, 0xc0011a46c0}, 0xc0011a49f0) /go/src/github.com/docker/docker/builder/dockerfile/builder.go:211 +0x2e5 github.com/docker/docker/builder/dockerfile.(*BuildManager).Build(0xc0008868c0, {0xb8f708, 0xc0011a44b0}, {{0xb789c0, 0xc0011a4540}, {{0xb6b940, 0xc000c22a50}, {0xb6c5e0, 0xc000c22a68}, {0xb6c5e0, ...}, ...}, ...}) /go/src/github.com/docker/docker/builder/dockerfile/builder.go:98 +0x358 github.com/docker/docker/api/server/backend/build.(*Backend).Build(0xc0007d0870, {0xb8f708, 0xc0011a44b0}, {{0xb789c0, 0xc0011a4540}, {{0xb6b940, 0xc000c22a50}, {0xb6c5e0, 0xc000c22a68}, {0xb6c5e0, ...}, ...}, ...}) /go/src/github.com/docker/docker/api/server/backend/build/backend.go:69 +0x186 github.com/docker/docker/api/server/router/build.(*buildRouter).postBuild(0xc0008333c0, {0xb8f708, 0xc0011a44b0}, {0xb8e130, 0xc0000ed500}, 0xc0010d4800, 0xc0012df760?) /go/src/github.com/docker/docker/api/server/router/build/build_routes.go:280 +0x7a6 github.com/docker/docker/api/server/middleware.ExperimentalMiddleware.WrapHandler.func1({0xb8f708, 0xc0011a44b0}, {0xb8e130?, 0xc0000ed500?}, 0x36cf80?, 0xc0010ab550?) /go/src/github.com/docker/docker/api/server/middleware/experimental.go:26 +0x15b github.com/docker/docker/api/server/middleware.VersionMiddleware.WrapHandler.func1({0xb8f708, 0xc0011a4480}, {0xb8e130, 0xc0000ed500}, 0xc000d787e8?, 0xc000d787a0?) /go/src/github.com/docker/docker/api/server/middleware/version.go:62 +0x4d7 github.com/docker/docker/pkg/authorization.(*Middleware).WrapHandler.func1({0xb8f708, 0xc0011a4480}, {0xb8e130?, 0xc0000ed500?}, 0xc0010d4800, 0xc0010ab500?) /go/src/github.com/docker/docker/pkg/authorization/middleware.go:59 +0x649 github.com/docker/docker/api/server.(*Server).makeHTTPHandler.func1({0xb8e130, 0xc0000ed500}, 0xc0010d4700) /go/src/github.com/docker/docker/api/server/server.go:53 +0x1ce net/http.HandlerFunc.ServeHTTP(0xc0010d4600?, {0xb8e130?, 0xc0000ed500?}, 0xc000d789e8?) /usr/local/go/src/net/http/server.go:2122 +0x2f github.com/docker/docker/vendor/github.com/gorilla/mux.(*Router).ServeHTTP(0xc0001a7e00, {0xb8e130, 0xc0000ed500}, 0xc000d37600) /go/src/github.com/docker/docker/vendor/github.com/gorilla/mux/mux.go:210 +0x1cf net/http.serverHandler.ServeHTTP({0xb7ec58?}, {0xb8e130, 0xc0000ed500}, 0xc000d37600) /usr/local/go/src/net/http/server.go:2936 +0x316 net/http.(*conn).serve(0xc0012661b0, {0xb8f708, 0xc000fd0360}) /usr/local/go/src/net/http/server.go:1995 +0x612 created by net/http.(*Server).Serve /usr/local/go/src/net/http/server.go:3089 +0x5ed Signed-off-by: Sebastiaan van Stijn --- daemon/containerd/image_import.go | 7 +++++-- daemon/containerd/image_import_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 daemon/containerd/image_import_test.go diff --git a/daemon/containerd/image_import.go b/daemon/containerd/image_import.go index 7738b6e203..ff9242472d 100644 --- a/daemon/containerd/image_import.go +++ b/daemon/containerd/image_import.go @@ -393,8 +393,11 @@ func containerConfigToOciImageConfig(cfg *container.Config) ocispec.ImageConfig StopSignal: cfg.StopSignal, ArgsEscaped: cfg.ArgsEscaped, } - for k, v := range cfg.ExposedPorts { - ociCfg.ExposedPorts[string(k)] = v + if len(cfg.ExposedPorts) > 0 { + ociCfg.ExposedPorts = map[string]struct{}{} + for k, v := range cfg.ExposedPorts { + ociCfg.ExposedPorts[string(k)] = v + } } return ociCfg diff --git a/daemon/containerd/image_import_test.go b/daemon/containerd/image_import_test.go new file mode 100644 index 0000000000..1b2e20f371 --- /dev/null +++ b/daemon/containerd/image_import_test.go @@ -0,0 +1,22 @@ +package containerd + +import ( + "testing" + + "github.com/docker/docker/api/types/container" + "github.com/docker/go-connections/nat" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +// regression test for https://github.com/moby/moby/issues/45904 +func TestContainerConfigToOciImageConfig(t *testing.T) { + ociCFG := containerConfigToOciImageConfig(&container.Config{ + ExposedPorts: nat.PortSet{ + "80/tcp": struct{}{}, + }, + }) + + expected := map[string]struct{}{"80/tcp": {}} + assert.Check(t, is.DeepEqual(ociCFG.ExposedPorts, expected)) +}