diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f983dacf..33aa62dbae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Upgrading from Docker 1.13.1 to 17.03.0 is expected to be simple and low-risk. ### Contrib * Update various `bash` and `zsh` completion scripts [#30823](https://github.com/docker/docker/pull/30823), [#30945](https://github.com/docker/docker/pull/30945) and more... +* Block obsolete socket families in default seccomp profile - mitigates unpatched kernels' CVE-2017-6074 [#29076](https://github.com/docker/docker/pull/29076) ### Networking @@ -36,6 +37,14 @@ Upgrading from Docker 1.13.1 to 17.03.0 is expected to be simple and low-risk. * Fix a deadlock in docker logs [#30223](https://github.com/docker/docker/pull/30223) * Fix cpu spin waiting for log write events [#31070](https://github.com/docker/docker/pull/31070) +* Fix a possible crash when using journald [#31231](https://github.com/docker/docker/pull/31231) [#31263](https://github.com/docker/docker/pull/31231) +* Fix a panic on close of nil channel [#31274](https://github.com/docker/docker/pull/31274) +* Fix duplicate mount point for `--volumes-from` in `docker run` [#29563](https://github.com/docker/docker/pull/29563) +* Fix `--cache-from` does not cache last step [#31189](https://github.com/docker/docker/pull/31189) + +### Swarm Mode + +* Shutdown leaks an error when the container was never started [#31279](https://github.com/docker/docker/pull/31279) ### Swarm Mode diff --git a/daemon/cache.go b/daemon/cache.go index a2c2c137f5..5ea13fd988 100644 --- a/daemon/cache.go +++ b/daemon/cache.go @@ -215,7 +215,7 @@ func isValidParent(img, parent *image.Image) bool { if len(parent.History) >= len(img.History) { return false } - if len(parent.RootFS.DiffIDs) >= len(img.RootFS.DiffIDs) { + if len(parent.RootFS.DiffIDs) > len(img.RootFS.DiffIDs) { return false } diff --git a/daemon/cluster/executor/container/controller.go b/daemon/cluster/executor/container/controller.go index 75f286a217..cbe6252872 100644 --- a/daemon/cluster/executor/container/controller.go +++ b/daemon/cluster/executor/container/controller.go @@ -323,8 +323,10 @@ func (r *controller) Shutdown(ctx context.Context) error { // remove container from service binding if err := r.adapter.deactivateServiceBinding(); err != nil { - log.G(ctx).WithError(err).Errorf("failed to deactivate service binding for container %s", r.adapter.container.name()) - return err + log.G(ctx).WithError(err).Warningf("failed to deactivate service binding for container %s", r.adapter.container.name()) + // Don't return an error here, because failure to deactivate + // the service binding is expected if the container was never + // started. } if err := r.adapter.shutdown(ctx); err != nil { diff --git a/daemon/logger/journald/read.go b/daemon/logger/journald/read.go index d91eb809bc..2fbbfe175d 100644 --- a/daemon/logger/journald/read.go +++ b/daemon/logger/journald/read.go @@ -237,7 +237,10 @@ drain: // free(NULL) is safe C.free(unsafe.Pointer(oldCursor)) - C.sd_journal_get_cursor(j, &cursor) + if C.sd_journal_get_cursor(j, &cursor) != 0 { + // ensure that we won't be freeing an address that's invalid + cursor = nil + } return cursor } @@ -245,6 +248,9 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re s.readers.mu.Lock() s.readers.readers[logWatcher] = logWatcher s.readers.mu.Unlock() + + newCursor := make(chan *C.char) + go func() { // Keep copying journal data out until we're notified to stop // or we hit an error. @@ -264,8 +270,8 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re s.readers.mu.Lock() delete(s.readers.readers, logWatcher) s.readers.mu.Unlock() - C.sd_journal_close(j) close(logWatcher.Msg) + newCursor <- cursor }() // Wait until we're told to stop. select { @@ -274,6 +280,8 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re C.close(pfd[1]) } + cursor = <-newCursor + return cursor } @@ -298,9 +306,9 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon following := false defer func(pfollowing *bool) { if !*pfollowing { - C.sd_journal_close(j) close(logWatcher.Msg) } + C.sd_journal_close(j) }(&following) // Remove limits on the size of data items that we'll retrieve. rc = C.sd_journal_set_data_threshold(j, C.size_t(0)) diff --git a/daemon/volumes.go b/daemon/volumes.go index 10cf787709..fa149b05c5 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -85,6 +85,15 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } }() + dereferenceIfExists := func(destination string) { + if v, ok := mountPoints[destination]; ok { + logrus.Debugf("Duplicate mount point '%s'", destination) + if v.Volume != nil { + daemon.volumes.Dereference(v.Volume, container.ID) + } + } + } + // 1. Read already configured mount points. for destination, point := range container.MountPoints { mountPoints[destination] = point @@ -121,7 +130,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } cp.Volume = v } - + dereferenceIfExists(cp.Destination) mountPoints[cp.Destination] = cp } } @@ -155,6 +164,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } binds[bind.Destination] = true + dereferenceIfExists(bind.Destination) mountPoints[bind.Destination] = bind } @@ -199,6 +209,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } binds[mp.Destination] = true + dereferenceIfExists(mp.Destination) mountPoints[mp.Destination] = mp } diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 41470f9777..49f287545c 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -7081,6 +7081,27 @@ func (s *DockerSuite) TestBuildWithFailure(c *check.C) { c.Assert(stdout, checker.Not(checker.Contains), "Step 2/2 : RUN nobody") } +func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) { + dockerfile := ` + FROM busybox + RUN echo "test" + ENTRYPOINT ["sh"]` + ctx, err := fakeContext(dockerfile, map[string]string{ + "Dockerfile": dockerfile, + }) + c.Assert(err, checker.IsNil) + defer ctx.Close() + + id1, err := buildImageFromContext("build1", ctx, true) + c.Assert(err, checker.IsNil) + + // rebuild with cache-from + id2, out, err := buildImageFromContextWithOut("build2", ctx, true, "--cache-from=build1") + c.Assert(err, checker.IsNil) + c.Assert(id1, checker.Equals, id2) + c.Assert(strings.Count(out, "Using cache"), checker.Equals, 2) +} + func (s *DockerSuite) TestBuildCacheFrom(c *check.C) { testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows dockerfile := ` diff --git a/integration-cli/docker_cli_volume_test.go b/integration-cli/docker_cli_volume_test.go index 61a9413758..cf0fb35288 100644 --- a/integration-cli/docker_cli_volume_test.go +++ b/integration-cli/docker_cli_volume_test.go @@ -3,6 +3,7 @@ package main import ( "fmt" "io/ioutil" + "net/http" "os" "os/exec" "path/filepath" @@ -425,3 +426,162 @@ func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *check.C) { c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k2, v2)) c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k3, v3)) } + +// Test case (1) for 21845: duplicate targets for --volumes-from +func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) { + testRequires(c, DaemonIsLinux) + + image := "vimage" + _, err := buildImage( + image, + ` + FROM busybox + VOLUME ["/tmp/data"] + `, + true) + c.Assert(err, check.IsNil) + + dockerCmd(c, "run", "--name=data1", image, "true") + dockerCmd(c, "run", "--name=data2", image, "true") + + out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1") + data1 := strings.TrimSpace(out) + c.Assert(data1, checker.Not(checker.Equals), "") + + out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2") + data2 := strings.TrimSpace(out) + c.Assert(data2, checker.Not(checker.Equals), "") + + // Both volume should exist + out, _ = dockerCmd(c, "volume", "ls", "-q") + c.Assert(strings.TrimSpace(out), checker.Contains, data1) + c.Assert(strings.TrimSpace(out), checker.Contains, data2) + + out, _, err = dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-d", "busybox", "top") + c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out)) + + // Only the second volume will be referenced, this is backward compatible + out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app") + c.Assert(strings.TrimSpace(out), checker.Equals, data2) + + dockerCmd(c, "rm", "-f", "-v", "app") + dockerCmd(c, "rm", "-f", "-v", "data1") + dockerCmd(c, "rm", "-f", "-v", "data2") + + // Both volume should not exist + out, _ = dockerCmd(c, "volume", "ls", "-q") + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) +} + +// Test case (2) for 21845: duplicate targets for --volumes-from and -v (bind) +func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *check.C) { + testRequires(c, DaemonIsLinux) + + image := "vimage" + _, err := buildImage(image, + ` + FROM busybox + VOLUME ["/tmp/data"] + `, + true) + c.Assert(err, check.IsNil) + + dockerCmd(c, "run", "--name=data1", image, "true") + dockerCmd(c, "run", "--name=data2", image, "true") + + out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1") + data1 := strings.TrimSpace(out) + c.Assert(data1, checker.Not(checker.Equals), "") + + out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2") + data2 := strings.TrimSpace(out) + c.Assert(data2, checker.Not(checker.Equals), "") + + // Both volume should exist + out, _ = dockerCmd(c, "volume", "ls", "-q") + c.Assert(strings.TrimSpace(out), checker.Contains, data1) + c.Assert(strings.TrimSpace(out), checker.Contains, data2) + + out, _, err = dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-v", "/tmp/data:/tmp/data", "-d", "busybox", "top") + c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out)) + + // No volume will be referenced (mount is /tmp/data), this is backward compatible + out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app") + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) + + dockerCmd(c, "rm", "-f", "-v", "app") + dockerCmd(c, "rm", "-f", "-v", "data1") + dockerCmd(c, "rm", "-f", "-v", "data2") + + // Both volume should not exist + out, _ = dockerCmd(c, "volume", "ls", "-q") + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) +} + +// Test case (3) for 21845: duplicate targets for --volumes-from and `Mounts` (API only) +func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C) { + testRequires(c, DaemonIsLinux) + + image := "vimage" + _, err := buildImage(image, + ` + FROM busybox + VOLUME ["/tmp/data"] + `, + true) + c.Assert(err, check.IsNil) + + dockerCmd(c, "run", "--name=data1", image, "true") + dockerCmd(c, "run", "--name=data2", image, "true") + + out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1") + data1 := strings.TrimSpace(out) + c.Assert(data1, checker.Not(checker.Equals), "") + + out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2") + data2 := strings.TrimSpace(out) + c.Assert(data2, checker.Not(checker.Equals), "") + + // Both volume should exist + out, _ = dockerCmd(c, "volume", "ls", "-q") + c.Assert(strings.TrimSpace(out), checker.Contains, data1) + c.Assert(strings.TrimSpace(out), checker.Contains, data2) + + // Mounts is available in API + status, body, err := sockRequest("POST", "/containers/create?name=app", map[string]interface{}{ + "Image": "busybox", + "Cmd": []string{"top"}, + "HostConfig": map[string]interface{}{ + "VolumesFrom": []string{ + "data1", + "data2", + }, + "Mounts": []map[string]interface{}{ + { + "Type": "bind", + "Source": "/tmp/data", + "Target": "/tmp/data", + }, + }}, + }) + + c.Assert(err, checker.IsNil, check.Commentf(string(body))) + c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body))) + + // No volume will be referenced (mount is /tmp/data), this is backward compatible + out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app") + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) + + dockerCmd(c, "rm", "-f", "-v", "app") + dockerCmd(c, "rm", "-f", "-v", "data1") + dockerCmd(c, "rm", "-f", "-v", "data2") + + // Both volume should not exist + out, _ = dockerCmd(c, "volume", "ls", "-q") + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2) +}