From 0468dac252c44b500ef33a7b039ef31740d5d866 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 17 Sep 2025 22:50:18 +0200 Subject: [PATCH] client: Client.ContainerCreate: fix panic when passing a nil config The config is a required argument (to create a container, at least an image is needed), but the function was missing a check for this, which would result in a panic if the client was using API v1.44 or up due to the changes from ee9f0ed895a15131ee9e3e85670d25f8a5abe2e1 attempting to [reset the deprecated `MacAddress` field][1]. In practice, this would unlikely be hit, and we didn't hit this in unit-tests, due to a bug in `WithMockClient`, which initializes the client with an [empty API version][2], which is different from the actual client, which [initializes the client with the MaxAPIVersion][3] This patch updates the function to return an error if a nil config is passed. [1]: https://github.com/moby/moby/blob/5a582729d86c810c3c74035ca9830ea8478f8276/client/container_create.go#L72-L75 [2]: https://github.com/moby/moby/blob/5a582729d86c810c3c74035ca9830ea8478f8276/client/client_mock_test.go#L22-L36 [3]: https://github.com/moby/moby/blob/5a582729d86c810c3c74035ca9830ea8478f8276/client/client.go#L167-L190 Signed-off-by: Sebastiaan van Stijn --- client/container_create.go | 9 +++++++-- client/container_create_test.go | 14 +++++++++----- .../moby/moby/client/container_create.go | 9 +++++++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/client/container_create.go b/client/container_create.go index 61d76177cd..b15a5e6515 100644 --- a/client/container_create.go +++ b/client/container_create.go @@ -9,6 +9,7 @@ import ( "sort" "strings" + cerrdefs "github.com/containerd/errdefs" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/network" "github.com/moby/moby/api/types/versions" @@ -18,6 +19,10 @@ import ( // ContainerCreate creates a new container based on the given configuration. // It can be associated with a name, but it's not mandatory. func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) { + if config == nil { + return container.CreateResponse{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil") + } + var response container.CreateResponse // Make sure we negotiated (if the client is configured to do so), @@ -29,13 +34,13 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config return response, err } - if err := cli.NewVersionError(ctx, "1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { + if err := cli.NewVersionError(ctx, "1.25", "stop timeout"); config.StopTimeout != nil && err != nil { return response, err } if err := cli.NewVersionError(ctx, "1.41", "specify container image platform"); platform != nil && err != nil { return response, err } - if err := cli.NewVersionError(ctx, "1.44", "specify health-check start interval"); config != nil && config.Healthcheck != nil && config.Healthcheck.StartInterval != 0 && err != nil { + if err := cli.NewVersionError(ctx, "1.44", "specify health-check start interval"); config.Healthcheck != nil && config.Healthcheck.StartInterval != 0 && err != nil { return response, err } if err := cli.NewVersionError(ctx, "1.44", "specify mac-address per network"); hasEndpointSpecificMacAddress(networkingConfig) && err != nil { diff --git a/client/container_create_test.go b/client/container_create_test.go index 8cc8a3243a..2184dd28cd 100644 --- a/client/container_create_test.go +++ b/client/container_create_test.go @@ -23,6 +23,10 @@ func TestContainerCreateError(t *testing.T) { assert.NilError(t, err) _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") + assert.Error(t, err, "config is nil") + assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) + + _, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "nothing") assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) // 404 doesn't automatically means an unknown image @@ -31,7 +35,7 @@ func TestContainerCreateError(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") + _, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "nothing") assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) } @@ -70,7 +74,7 @@ func TestContainerCreateWithName(t *testing.T) { ) assert.NilError(t, err) - r, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "container_name") + r, err := client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "container_name") assert.NilError(t, err) assert.Check(t, is.Equal(r.ID, "container_id")) } @@ -115,7 +119,7 @@ func TestContainerCreateAutoRemove(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, "") + _, err = client.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{AutoRemove: true}, nil, nil, "") assert.NilError(t, err) }) } @@ -129,7 +133,7 @@ func TestContainerCreateConnectionError(t *testing.T) { client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid")) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "") + _, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "") assert.Check(t, is.ErrorType(err, IsErrConnectionFailed)) } @@ -179,6 +183,6 @@ func TestContainerCreateCapabilities(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), nil, &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}, nil, nil, "") + _, err = client.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}, nil, nil, "") assert.NilError(t, err) } diff --git a/vendor/github.com/moby/moby/client/container_create.go b/vendor/github.com/moby/moby/client/container_create.go index 61d76177cd..b15a5e6515 100644 --- a/vendor/github.com/moby/moby/client/container_create.go +++ b/vendor/github.com/moby/moby/client/container_create.go @@ -9,6 +9,7 @@ import ( "sort" "strings" + cerrdefs "github.com/containerd/errdefs" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/network" "github.com/moby/moby/api/types/versions" @@ -18,6 +19,10 @@ import ( // ContainerCreate creates a new container based on the given configuration. // It can be associated with a name, but it's not mandatory. func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) { + if config == nil { + return container.CreateResponse{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil") + } + var response container.CreateResponse // Make sure we negotiated (if the client is configured to do so), @@ -29,13 +34,13 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config return response, err } - if err := cli.NewVersionError(ctx, "1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { + if err := cli.NewVersionError(ctx, "1.25", "stop timeout"); config.StopTimeout != nil && err != nil { return response, err } if err := cli.NewVersionError(ctx, "1.41", "specify container image platform"); platform != nil && err != nil { return response, err } - if err := cli.NewVersionError(ctx, "1.44", "specify health-check start interval"); config != nil && config.Healthcheck != nil && config.Healthcheck.StartInterval != 0 && err != nil { + if err := cli.NewVersionError(ctx, "1.44", "specify health-check start interval"); config.Healthcheck != nil && config.Healthcheck.StartInterval != 0 && err != nil { return response, err } if err := cli.NewVersionError(ctx, "1.44", "specify mac-address per network"); hasEndpointSpecificMacAddress(networkingConfig) && err != nil {