[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1000448: bullseye-pu: package containerd/1.4.12~ds1-1~deb11u1



Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: zhsj@debian.org

[ Reason ]

I'd like to update containerd in bullseye to latest upstream
patch version. Upstream does maintain a stable release branch
1.4.x with only backporting important bugfix.

Notably:
1.4.12~ds1-1~deb11u1 will have:

+ Workaround for "clone3" syscall. So users can run images like
  fedora:rawhide, ubuntu:impish, which has enabled clone3 syscall
  in glibc.
  See also https://bugs.launchpad.net/cloud-images/+bug/1943049
+ Mitigate CVE-2021-41190: Handle ambiguous OCI manifest parsing
+ Backport RPi1/RPi0 workaround #998909

[ Impact ]

Same as above three notable changes.

[ Tests ]

+ Extensive unit tests in upstream source
+ Manually install it on bullseye system. I can successfully run:
  ctr run --net-host --seccomp --rm registry.fedoraproject.org/fedora:rawhide test1 /usr/bin/curl www.debian.org

+ I don't have RPi1 or RPi0 to test, but the backported patch is fairly simple.

[ Risks ]

There are 7 patch releases from the version in bullseye.
So the change is not small.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]

I will copy filtered diff here, and attach full diff as attachment.

filter command:

cat containerd_1.4.12~ds1-1~deb11u1.patch|filterdiff -x '*/patches/*CVE-2021*' -x '*_windows.*' -x '*_test.*' -x '*.toml' -x '*.yml' -x '*.yaml' -x '*/vendor.conf' -x '*/Dockerfile.test'

diffstat:

 .mailmap                                                                    |    3 
 Vagrantfile                                                                 |    2 
 archive/tar_unix.go                                                         |    2 
 content/local/store.go                                                      |    2 
 contrib/seccomp/seccomp_default.go                                          |   11 
 debian/changelog                                                            |   31 ++
 debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch                       |    2 
 debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch |  114 ++++++----
 debian/patches/0008-Add-RPi1-RPi0-workaround.patch                          |   35 +++
 debian/patches/series                                                       |    5 
 images/image.go                                                             |   55 ++++
 metadata/containers.go                                                      |    2 
 metadata/content.go                                                         |    6 
 remotes/docker/authorizer.go                                                |    7 
 remotes/docker/fetcher.go                                                   |   12 -
 remotes/docker/pusher.go                                                    |    5 
 remotes/docker/resolver.go                                                  |   51 +++-
 remotes/docker/schema1/converter.go                                         |    9 
 runtime/v1/linux/bundle.go                                                  |   56 ++++
 runtime/v2/bundle.go                                                        |    5 
 runtime/v2/bundle_default.go                                                |   24 ++
 runtime/v2/bundle_linux.go                                                  |   74 ++++++
 script/setup/runc-version                                                   |    2 
 snapshots/btrfs/btrfs.go                                                    |    8 
 snapshots/devmapper/snapshotter.go                                          |    4 
 vendor/github.com/containerd/cri/pkg/os/os.go                               |   13 -
 vendor/github.com/containerd/cri/pkg/os/os_unix.go                          |   15 +
 vendor/github.com/containerd/cri/pkg/server/sandbox_run.go                  |    4 
 version/version.go                                                          |    2 
 29 files changed, 471 insertions(+), 90 deletions(-)

diff -Nru containerd-1.4.5~ds1/archive/tar_unix.go containerd-1.4.12~ds1/archive/tar_unix.go
--- containerd-1.4.5~ds1/archive/tar_unix.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/archive/tar_unix.go	2021-11-18 03:52:12.000000000 +0800
@@ -113,7 +113,7 @@
 
 func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
 	if hdr.Typeflag == tar.TypeLink {
-		if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
+		if fi, err := os.Lstat(path); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
 			if err := os.Chmod(path, hdrInfo.Mode()); err != nil && !os.IsNotExist(err) {
 				return err
 			}
diff -Nru containerd-1.4.5~ds1/content/local/store.go containerd-1.4.12~ds1/content/local/store.go
--- containerd-1.4.5~ds1/content/local/store.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/content/local/store.go	2021-11-18 03:52:12.000000000 +0800
@@ -502,7 +502,7 @@
 	if ref != status.Ref {
 		// NOTE(stevvooe): This is fairly catastrophic. Either we have some
 		// layout corruption or a hash collision for the ref key.
-		return status, errors.Wrapf(err, "ref key does not match: %v != %v", ref, status.Ref)
+		return status, errors.Errorf("ref key does not match: %v != %v", ref, status.Ref)
 	}
 
 	if total > 0 && status.Total > 0 && total != status.Total {
diff -Nru containerd-1.4.5~ds1/contrib/seccomp/seccomp_default.go containerd-1.4.12~ds1/contrib/seccomp/seccomp_default.go
--- containerd-1.4.5~ds1/contrib/seccomp/seccomp_default.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/contrib/seccomp/seccomp_default.go	2021-11-18 03:52:12.000000000 +0800
@@ -49,6 +49,7 @@
 
 // DefaultProfile defines the allowed syscalls for the default seccomp profile.
 func DefaultProfile(sp *specs.Spec) *specs.LinuxSeccomp {
+	nosys := uint(unix.ENOSYS)
 	syscalls := []specs.LinuxSyscall{
 		{
 			Names: []string{
@@ -524,6 +525,7 @@
 				Names: []string{
 					"bpf",
 					"clone",
+					"clone3",
 					"fanotify_init",
 					"lookup_dcookie",
 					"mount",
@@ -648,6 +650,15 @@
 				},
 			})
 		}
+		// clone3 is explicitly requested to give ENOSYS instead of the default EPERM, when CAP_SYS_ADMIN is unset
+		// https://github.com/moby/moby/pull/42681
+		s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{
+			Names: []string{
+				"clone3",
+			},
+			Action:   specs.ActErrno,
+			ErrnoRet: &nosys,
+		})
 	}
 
 	return s
diff -Nru containerd-1.4.5~ds1/debian/changelog containerd-1.4.12~ds1/debian/changelog
--- containerd-1.4.5~ds1/debian/changelog	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/changelog	2021-11-23 18:42:16.000000000 +0800
@@ -1,3 +1,34 @@
+containerd (1.4.12~ds1-1~deb11u1) bullseye; urgency=medium
+
+  * New upstream version 1.4.12~ds1
+    + 1.4.12
+      * Mitigate CVE-2021-41190: Handle ambiguous OCI manifest parsing
+      * Update pull to try next mirror for non-404 errors
+      * Update pull to handle of non-https urls in descriptors
+    + 1.4.11
+      * CVE-2021-41103: Fix insufficiently restricted permissions on container
+        root and plugin directories
+    + 1.4.10
+      * Support "clone3" in default seccomp profile
+      * Fix panic in metadata content writer on copy error
+    + 1.4.9
+      * Update pull authorization logic on redirect
+      * Fix user agent used for fetching registry authentication tokens
+    + 1.4.8
+      * CVE-2021-32760: Archive package allows chmod of file outside of unpack
+        target directory
+    + 1.4.7
+      * Fix invalid validation error checking
+      * Fix error on image pull resume
+  * Refresh patches
+    + Drop CVE-2021-32760 patch
+    + Drop CVE-2021-41103 patch
+    + Refresh 0005-backport-github.com-containerd-containerd-remotes.patch
+      with latest 1.5 release branch
+  * Backport RPi1/RPi0 workaround (Closes: #998909)
+
+ -- Shengjing Zhu <zhsj@debian.org>  Tue, 23 Nov 2021 18:42:16 +0800
+
 containerd (1.4.5~ds1-2+deb11u1) bullseye-security; urgency=high
 
   * CVE-2021-41103: Insufficiently restricted permissions on container
diff -Nru containerd-1.4.5~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch containerd-1.4.12~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch
--- containerd-1.4.5~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch	2021-11-23 18:42:16.000000000 +0800
@@ -20,7 +20,7 @@
  /*
     Copyright The containerd Authors.
 diff --git a/snapshots/btrfs/btrfs.go b/snapshots/btrfs/btrfs.go
-index ea90853..eef883b 100644
+index 78f825c..778b783 100644
 --- a/snapshots/btrfs/btrfs.go
 +++ b/snapshots/btrfs/btrfs.go
 @@ -1,4 +1,4 @@
diff -Nru containerd-1.4.5~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch containerd-1.4.12~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch
--- containerd-1.4.5~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch	2021-11-23 18:42:16.000000000 +0800
@@ -1,6 +1,7 @@
 From: Shengjing Zhu <zhsj@debian.org>
-Date: Fri, 5 Mar 2021 16:35:17 +0800
-Subject: backport_github=2Ecom/containerd/containerd/remotes
+Date: Tue, 23 Nov 2021 15:49:57 +0800
+Subject: =?utf-8?q?backport_github=2Ecom/containerd/containerd/remotes?=
+ =?utf-8?q?=C2=AC?=
 
 For building docker.io 20.10
 
@@ -10,19 +11,19 @@
 
 Forwarded: not-needed
 ---
- remotes/docker/auth.go           | 198 ---------------------------
- remotes/docker/auth/fetch.go     | 202 +++++++++++++++++++++++++++
- remotes/docker/auth/parse.go     | 203 ++++++++++++++++++++++++++++
- remotes/docker/authorizer.go     | 285 ++++++++++-----------------------------
+ remotes/docker/auth.go           | 198 --------------------------
+ remotes/docker/auth/fetch.go     | 209 ++++++++++++++++++++++++++++
+ remotes/docker/auth/parse.go     | 203 +++++++++++++++++++++++++++
+ remotes/docker/authorizer.go     | 292 +++++++++------------------------------
  remotes/docker/errcode.go        |   2 +-
  remotes/docker/fetcher.go        |   2 +-
  remotes/docker/httpreadseeker.go |   2 +-
- remotes/docker/pusher.go         |  36 +++--
- remotes/docker/resolver.go       |  47 +++----
+ remotes/docker/pusher.go         |  38 +++--
+ remotes/docker/resolver.go       |  49 +++----
  remotes/docker/scope.go          |  14 +-
  remotes/docker/scope_test.go     |   4 +-
- remotes/errors/errors.go         |  46 +++++++
- 12 files changed, 573 insertions(+), 468 deletions(-)
+ remotes/errors/errors.go         |  46 ++++++
+ 12 files changed, 581 insertions(+), 478 deletions(-)
  delete mode 100644 remotes/docker/auth.go
  create mode 100644 remotes/docker/auth/fetch.go
  create mode 100644 remotes/docker/auth/parse.go
@@ -234,10 +235,10 @@
 -}
 diff --git a/remotes/docker/auth/fetch.go b/remotes/docker/auth/fetch.go
 new file mode 100644
-index 0000000..0d01c81
+index 0000000..8b0a87e
 --- /dev/null
 +++ b/remotes/docker/auth/fetch.go
-@@ -0,0 +1,202 @@
+@@ -0,0 +1,209 @@
 +/*
 +   Copyright The containerd Authors.
 +
@@ -266,6 +267,7 @@
 +
 +	"github.com/containerd/containerd/log"
 +	remoteserrors "github.com/containerd/containerd/remotes/errors"
++	"github.com/containerd/containerd/version"
 +	"github.com/pkg/errors"
 +	"golang.org/x/net/context/ctxhttp"
 +)
@@ -349,6 +351,9 @@
 +	for k, v := range headers {
 +		req.Header[k] = append(req.Header[k], v...)
 +	}
++	if len(req.Header.Get("User-Agent")) == 0 {
++		req.Header.Set("User-Agent", "containerd/"+version.Version)
++	}
 +
 +	resp, err := ctxhttp.Do(ctx, client, req)
 +	if err != nil {
@@ -393,6 +398,9 @@
 +	for k, v := range headers {
 +		req.Header[k] = append(req.Header[k], v...)
 +	}
++	if len(req.Header.Get("User-Agent")) == 0 {
++		req.Header.Set("User-Agent", "containerd/"+version.Version)
++	}
 +
 +	reqParams := req.URL.Query()
 +
@@ -650,10 +658,10 @@
 +	return "", ""
 +}
 diff --git a/remotes/docker/authorizer.go b/remotes/docker/authorizer.go
-index 001423a..67e4aea 100644
+index 2f459b7..67e4aea 100644
 --- a/remotes/docker/authorizer.go
 +++ b/remotes/docker/authorizer.go
-@@ -19,21 +19,17 @@ package docker
+@@ -19,22 +19,17 @@ package docker
  import (
  	"context"
  	"encoding/base64"
@@ -669,6 +677,7 @@
  
  	"github.com/containerd/containerd/errdefs"
  	"github.com/containerd/containerd/log"
+-	"github.com/containerd/containerd/version"
 +	"github.com/containerd/containerd/remotes/docker/auth"
 +	remoteerrors "github.com/containerd/containerd/remotes/errors"
  	"github.com/pkg/errors"
@@ -677,7 +686,7 @@
  )
  
  type dockerAuthorizer struct {
-@@ -135,8 +131,8 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
+@@ -136,8 +131,8 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
  
  	a.mu.Lock()
  	defer a.mu.Unlock()
@@ -688,7 +697,7 @@
  			if err := invalidAuthorization(c, responses); err != nil {
  				delete(a.handlers, host)
  				return err
-@@ -152,26 +148,35 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
+@@ -153,26 +148,35 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
  				return nil
  			}
  
@@ -731,7 +740,7 @@
  				return nil
  			}
  		}
-@@ -179,38 +184,6 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
+@@ -180,38 +184,6 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
  	return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme")
  }
  
@@ -770,7 +779,7 @@
  // authResult is used to control limit rate.
  type authResult struct {
  	sync.WaitGroup
-@@ -227,17 +200,17 @@ type authHandler struct {
+@@ -228,17 +200,17 @@ type authHandler struct {
  	client *http.Client
  
  	// only support basic and bearer schemes
@@ -791,7 +800,7 @@
  	return &authHandler{
  		header:       hdr,
  		client:       client,
-@@ -249,17 +222,17 @@ func newAuthHandler(client *http.Client, hdr http.Header, scheme authenticationS
+@@ -250,17 +222,17 @@ func newAuthHandler(client *http.Client, hdr http.Header, scheme authenticationS
  
  func (ah *authHandler) authorize(ctx context.Context) (string, error) {
  	switch ah.scheme {
@@ -813,7 +822,7 @@
  
  	if username == "" || secret == "" {
  		return "", fmt.Errorf("failed to handle basic auth because missing username or secret")
-@@ -269,14 +242,14 @@ func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) {
+@@ -270,14 +242,14 @@ func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) {
  	return fmt.Sprintf("Basic %s", auth), nil
  }
  
@@ -831,7 +840,7 @@
  
  	ah.Lock()
  	if r, exist := ah.scopedTokens[scoped]; exist {
-@@ -291,174 +264,52 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (string, error) {
+@@ -292,180 +264,52 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (string, error) {
  	ah.scopedTokens[scoped] = r
  	ah.Unlock()
  
@@ -910,6 +919,9 @@
 -			req.Header[k] = append(req.Header[k], v...)
 -		}
 -	}
+-	if len(req.Header.Get("User-Agent")) == 0 {
+-		req.Header.Set("User-Agent", "containerd/"+version.Version)
+-	}
 -
 -	resp, err := ctxhttp.Do(ctx, ah.client, req)
 -	if err != nil {
@@ -984,6 +996,9 @@
  		}
 +		return resp.AccessToken, nil
  	}
+-	if len(req.Header.Get("User-Agent")) == 0 {
+-		req.Header.Set("User-Agent", "containerd/"+version.Version)
+-	}
 -
 -	reqParams := req.URL.Query()
 -
@@ -1058,7 +1073,7 @@
  	Message string
  
 diff --git a/remotes/docker/fetcher.go b/remotes/docker/fetcher.go
-index cd0168b..9d6708d 100644
+index 5aaaf9e..4b2c10e 100644
 --- a/remotes/docker/fetcher.go
 +++ b/remotes/docker/fetcher.go
 @@ -45,7 +45,7 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
@@ -1084,7 +1099,7 @@
  
  		if hrs.rc != nil {
 diff --git a/remotes/docker/pusher.go b/remotes/docker/pusher.go
-index d95e0c8..94a270c 100644
+index d46396b..c367b1e 100644
 --- a/remotes/docker/pusher.go
 +++ b/remotes/docker/pusher.go
 @@ -30,6 +30,7 @@ import (
@@ -1104,19 +1119,20 @@
  	if err != nil {
  		return nil, err
  	}
-@@ -112,8 +113,9 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -113,9 +114,10 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  				return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
  			}
  		} else if resp.StatusCode != http.StatusNotFound {
--			// TODO: log error
--			return nil, errors.Errorf("unexpected response: %s", resp.Status)
 +			err := remoteserrors.NewUnexpectedStatusErr(resp)
 +			log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response")
+ 			resp.Body.Close()
+-			// TODO: log error
+-			return nil, errors.Errorf("unexpected response: %s", resp.Status)
 +			return nil, err
  		}
+ 		resp.Body.Close()
  	}
- 
-@@ -128,7 +130,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -131,7 +133,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  		var resp *http.Response
  		if fromRepo := selectRepositoryMountCandidate(p.refspec, desc.Annotations); fromRepo != "" {
  			preq := requestWithMountFrom(req, desc.Digest.String(), fromRepo)
@@ -1125,7 +1141,7 @@
  
  			// NOTE: the fromRepo might be private repo and
  			// auth service still can grant token without error.
-@@ -166,8 +168,9 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -170,8 +172,9 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  			})
  			return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
  		default:
@@ -1137,7 +1153,7 @@
  		}
  
  		var (
-@@ -219,7 +222,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -223,7 +226,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  	// TODO: Support chunked upload
  
  	pr, pw := io.Pipe()
@@ -1146,7 +1162,7 @@
  	body := ioutil.NopCloser(pr)
  
  	req.body = func() (io.ReadCloser, error) {
-@@ -237,6 +240,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -241,6 +244,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  		defer close(respC)
  		resp, err := req.doWithRetries(ctx, nil)
  		if err != nil {
@@ -1154,7 +1170,7 @@
  			pr.CloseWithError(err)
  			return
  		}
-@@ -244,10 +248,11 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -248,10 +252,11 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  		switch resp.StatusCode {
  		case http.StatusOK, http.StatusCreated, http.StatusNoContent:
  		default:
@@ -1169,7 +1185,7 @@
  	}()
  
  	return &pushWriter{
-@@ -280,12 +285,17 @@ func getManifestPath(object string, dgst digest.Digest) []string {
+@@ -284,12 +289,17 @@ func getManifestPath(object string, dgst digest.Digest) []string {
  	return []string{"manifests", object}
  }
  
@@ -1188,7 +1204,7 @@
  	isManifest bool
  
  	expected digest.Digest
-@@ -335,8 +345,8 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di
+@@ -339,10 +349,10 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di
  
  	// TODO: timeout waiting for response
  	resp := <-pw.responseC
@@ -1197,10 +1213,13 @@
 +	if resp.err != nil {
 +		return resp.err
  	}
+-	defer resp.Body.Close()
++	defer resp.Response.Body.Close()
  
  	// 201 is specified return status, some registries return
+ 	// 200, 202 or 204.
 diff --git a/remotes/docker/resolver.go b/remotes/docker/resolver.go
-index aea092c..866379e 100644
+index 40831f1..d6ccd70 100644
 --- a/remotes/docker/resolver.go
 +++ b/remotes/docker/resolver.go
 @@ -41,10 +41,6 @@ import (
@@ -1235,8 +1254,8 @@
 -	}
 -
  	var (
- 		lastErr error
- 		paths   [][]string
+ 		firstErr error
+ 		paths    [][]string
 @@ -267,7 +258,7 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
  		return "", ocispec.Descriptor{}, errors.Wrap(errdefs.ErrNotFound, "no resolve hosts")
  	}
@@ -1246,7 +1265,22 @@
  	if err != nil {
  		return "", ocispec.Descriptor{}, err
  	}
-@@ -393,12 +384,7 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
+@@ -295,14 +286,12 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
+ 				if firstErr == nil {
+ 					firstErr = err
+ 				}
+-				log.G(ctx).WithError(err).Info("trying next host")
+ 				continue // try another host
+ 			}
+ 			resp.Body.Close() // don't care about body contents.
+ 
+ 			if resp.StatusCode > 299 {
+ 				if resp.StatusCode == http.StatusNotFound {
+-					log.G(ctx).Info("trying next host - response was http.StatusNotFound")
+ 					continue
+ 				}
+ 				if resp.StatusCode > 399 {
+@@ -404,12 +393,7 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
  }
  
  func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
@@ -1260,7 +1294,7 @@
  	if err != nil {
  		return nil, err
  	}
-@@ -409,23 +395,27 @@ func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetch
+@@ -420,23 +404,27 @@ func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetch
  }
  
  func (r *dockerResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
@@ -1295,7 +1329,7 @@
  type dockerBase struct {
  	refspec    reference.Spec
  	repository string
-@@ -457,10 +447,11 @@ func (r *dockerBase) filterHosts(caps HostCapabilities) (hosts []RegistryHost) {
+@@ -468,10 +456,11 @@ func (r *dockerBase) filterHosts(caps HostCapabilities) (hosts []RegistryHost) {
  }
  
  func (r *dockerBase) request(host RegistryHost, method string, ps ...string) *request {
diff -Nru containerd-1.4.5~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch containerd-1.4.12~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch
--- containerd-1.4.5~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch	2021-11-23 18:42:16.000000000 +0800
@@ -0,0 +1,35 @@
+From: Tianon Gravi <admwiggin@gmail.com>
+Date: Fri, 4 Sep 2020 14:12:04 -0700
+Subject: Add RPi1/RPi0 workaround
+
+On the very popular Raspberry Pi 1 and Zero devices, the CPU is actually ARMv6, but the chip happens to support the feature bit the kernel uses to differentiate v6/v7, so it gets reported as "CPU architecture: 7" and thus fails to run many of the images that get pulled.
+
+To account for this very popular edge case, this also checks "model name" which on these chips will begin with "ARMv6-compatible" -- we could also check uname, but getCPUInfo is already handy, low overhead, and mirrors the code before this.
+
+Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
+
+Origin: backport, https://github.com/containerd/containerd/commit/2055e12953bb538228d8d9fe627fa545d7cf82be
+---
+ platforms/cpuinfo.go | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/platforms/cpuinfo.go b/platforms/cpuinfo.go
+index db65a72..0512bc9 100644
+--- a/platforms/cpuinfo.go
++++ b/platforms/cpuinfo.go
+@@ -96,6 +96,15 @@ func getCPUVariant() string {
+ 		return ""
+ 	}
+ 
++	// handle edge case for Raspberry Pi ARMv6 devices (which due to a kernel quirk, report "CPU architecture: 7")
++	// https://www.raspberrypi.org/forums/viewtopic.php?t=12614
++	if runtime.GOARCH == "arm" && variant == "7" {
++		model, err := getCPUInfo("model name")
++		if err == nil && strings.HasPrefix(strings.ToLower(model), "armv6-compatible") {
++			variant = "6"
++		}
++	}
++
+ 	switch strings.ToLower(variant) {
+ 	case "8", "aarch64":
+ 		// special case: if running a 32-bit userspace on aarch64, the variant should be "v7"
diff -Nru containerd-1.4.5~ds1/debian/patches/series containerd-1.4.12~ds1/debian/patches/series
--- containerd-1.4.5~ds1/debian/patches/series	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/series	2021-11-23 18:42:16.000000000 +0800
@@ -5,7 +5,4 @@
 0005-backport-github.com-containerd-containerd-remotes.patch
 0006-backport-apparmor-handle-signal-mediation.patch
 0007-backport-runtime-ignore-file-already-closed-error.patch
-0008-CVE-2021-32760.patch
-0009-CVE-2021-41103/0009-v2-runtime-reduce-permissions-for-bundle-dir.patch
-0009-CVE-2021-41103/0010-v1-runtime-reduce-permissions-for-bundle-dir.patch
-0009-CVE-2021-41103/0011-btrfs-reduce-permissions-on-plugin-directories.patch
+0008-Add-RPi1-RPi0-workaround.patch
diff -Nru containerd-1.4.5~ds1/images/image.go containerd-1.4.12~ds1/images/image.go
--- containerd-1.4.5~ds1/images/image.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/images/image.go	2021-11-18 03:52:12.000000000 +0800
@@ -19,6 +19,7 @@
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"sort"
 	"time"
 
@@ -154,6 +155,10 @@
 				return nil, err
 			}
 
+			if err := validateMediaType(p, desc.MediaType); err != nil {
+				return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+			}
+
 			var manifest ocispec.Manifest
 			if err := json.Unmarshal(p, &manifest); err != nil {
 				return nil, err
@@ -194,6 +199,10 @@
 				return nil, err
 			}
 
+			if err := validateMediaType(p, desc.MediaType); err != nil {
+				return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+			}
+
 			var idx ocispec.Index
 			if err := json.Unmarshal(p, &idx); err != nil {
 				return nil, err
@@ -336,6 +345,10 @@
 			return nil, err
 		}
 
+		if err := validateMediaType(p, desc.MediaType); err != nil {
+			return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+		}
+
 		// TODO(stevvooe): We just assume oci manifest, for now. There may be
 		// subtle differences from the docker version.
 		var manifest ocispec.Manifest
@@ -351,6 +364,10 @@
 			return nil, err
 		}
 
+		if err := validateMediaType(p, desc.MediaType); err != nil {
+			return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+		}
+
 		var index ocispec.Index
 		if err := json.Unmarshal(p, &index); err != nil {
 			return nil, err
@@ -368,6 +385,44 @@
 	return descs, nil
 }
 
+// unknownDocument represents a manifest, manifest list, or index that has not
+// yet been validated.
+type unknownDocument struct {
+	MediaType string          `json:"mediaType,omitempty"`
+	Config    json.RawMessage `json:"config,omitempty"`
+	Layers    json.RawMessage `json:"layers,omitempty"`
+	Manifests json.RawMessage `json:"manifests,omitempty"`
+	FSLayers  json.RawMessage `json:"fsLayers,omitempty"` // schema 1
+}
+
+// validateMediaType returns an error if the byte slice is invalid JSON or if
+// the media type identifies the blob as one format but it contains elements of
+// another format.
+func validateMediaType(b []byte, mt string) error {
+	var doc unknownDocument
+	if err := json.Unmarshal(b, &doc); err != nil {
+		return err
+	}
+	if len(doc.FSLayers) != 0 {
+		return fmt.Errorf("media-type: schema 1 not supported")
+	}
+	switch mt {
+	case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
+		if len(doc.Manifests) != 0 ||
+			doc.MediaType == MediaTypeDockerSchema2ManifestList ||
+			doc.MediaType == ocispec.MediaTypeImageIndex {
+			return fmt.Errorf("media-type: expected manifest but found index (%s)", mt)
+		}
+	case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
+		if len(doc.Config) != 0 || len(doc.Layers) != 0 ||
+			doc.MediaType == MediaTypeDockerSchema2Manifest ||
+			doc.MediaType == ocispec.MediaTypeImageManifest {
+			return fmt.Errorf("media-type: expected index but found manifest (%s)", mt)
+		}
+	}
+	return nil
+}
+
 // RootFS returns the unpacked diffids that make up and images rootfs.
 //
 // These are used to verify that a set of layers unpacked to the expected
diff -Nru containerd-1.4.5~ds1/.mailmap containerd-1.4.12~ds1/.mailmap
--- containerd-1.4.5~ds1/.mailmap	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.mailmap	2021-11-18 03:52:12.000000000 +0800
@@ -17,6 +17,7 @@
 Cao Zhihao <caozhihao@163.com>
 Cao Zhihao <caozhihao@163.com> <caozhihao.xd@bytedance.com>
 Carlos Eduardo <me@carlosedp.com> <me@carlosedp.com>
+Cory Bennett <cbennett@netflix.com>
 Cristian Staretu <cristian.staretu@gmail.com>
 Cristian Staretu <cristian.staretu@gmail.com> <unclejack@users.noreply.github.com>
 Daniel Dao <dqminh89@gmail.com>
@@ -73,6 +74,7 @@
 Nishchay Kumar <mrawesomenix@gmail.com>
 Oliver Stenbom <oliver@stenbom.eu> <ostenbom@pivotal.io>
 Phil Estes <estesp@gmail.com> <estesp@linux.vnet.ibm.com>
+Phil Estes <estesp@gmail.com> <estesp@amazon.com>
 Reid Li <reid.li@utexas.edu>
 Ross Boucher <rboucher@gmail.com>
 Ruediger Maass <ruediger.maass@de.ibm.com>
@@ -90,6 +92,7 @@
 Su Fei  <fesu@ebay.com> <fesu@ebay.com>
 Ted Yu <yuzhihong@gmail.com>
 Tõnis Tiigi <tonistiigi@gmail.com>
+Wei Fu <fuweid89@gmail.com>
 Wei Fu <fuweid89@gmail.com> <fhfuwei@163.com>
 Xiaodong Zhang <a4012017@sina.com>
 Xuean Yan <yan.xuean@zte.com.cn>
diff -Nru containerd-1.4.5~ds1/metadata/containers.go containerd-1.4.12~ds1/metadata/containers.go
--- containerd-1.4.5~ds1/metadata/containers.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/metadata/containers.go	2021-11-18 03:52:12.000000000 +0800
@@ -290,7 +290,7 @@
 
 	// image has no validation
 	for k, v := range container.Labels {
-		if err := labels.Validate(k, v); err == nil {
+		if err := labels.Validate(k, v); err != nil {
 			return errors.Wrapf(err, "containers.Labels")
 		}
 	}
diff -Nru containerd-1.4.5~ds1/metadata/content.go containerd-1.4.12~ds1/metadata/content.go
--- containerd-1.4.5~ds1/metadata/content.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/metadata/content.go	2021-11-18 03:52:12.000000000 +0800
@@ -551,13 +551,13 @@
 	if desc.Size > 0 {
 		ra, err := nw.provider.ReaderAt(ctx, nw.desc)
 		if err != nil {
+			w.Close()
 			return err
 		}
 		defer ra.Close()
 
 		if err := content.CopyReaderAt(w, ra, desc.Size); err != nil {
-			nw.w.Close()
-			nw.w = nil
+			w.Close()
 			return err
 		}
 	}
@@ -708,7 +708,7 @@
 
 func validateInfo(info *content.Info) error {
 	for k, v := range info.Labels {
-		if err := labels.Validate(k, v); err == nil {
+		if err := labels.Validate(k, v); err != nil {
 			return errors.Wrapf(err, "info.Labels")
 		}
 	}
diff -Nru containerd-1.4.5~ds1/remotes/docker/authorizer.go containerd-1.4.12~ds1/remotes/docker/authorizer.go
--- containerd-1.4.5~ds1/remotes/docker/authorizer.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/authorizer.go	2021-11-18 03:52:12.000000000 +0800
@@ -31,6 +31,7 @@
 
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/log"
+	"github.com/containerd/containerd/version"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/net/context/ctxhttp"
@@ -356,6 +357,9 @@
 			req.Header[k] = append(req.Header[k], v...)
 		}
 	}
+	if len(req.Header.Get("User-Agent")) == 0 {
+		req.Header.Set("User-Agent", "containerd/"+version.Version)
+	}
 
 	resp, err := ctxhttp.Do(ctx, ah.client, req)
 	if err != nil {
@@ -408,6 +412,9 @@
 			req.Header[k] = append(req.Header[k], v...)
 		}
 	}
+	if len(req.Header.Get("User-Agent")) == 0 {
+		req.Header.Set("User-Agent", "containerd/"+version.Version)
+	}
 
 	reqParams := req.URL.Query()
 
diff -Nru containerd-1.4.5~ds1/remotes/docker/fetcher.go containerd-1.4.12~ds1/remotes/docker/fetcher.go
--- containerd-1.4.5~ds1/remotes/docker/fetcher.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/fetcher.go	2021-11-18 03:52:12.000000000 +0800
@@ -60,6 +60,10 @@
 				log.G(ctx).WithError(err).Debug("failed to parse")
 				continue
 			}
+			if u.Scheme != "http" && u.Scheme != "https" {
+				log.G(ctx).Debug("non-http(s) alternative url is unsupported")
+				continue
+			}
 			log.G(ctx).Debug("trying alternative url")
 
 			// Try this first, parse it
@@ -148,7 +152,7 @@
 	})
 }
 
-func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64) (io.ReadCloser, error) {
+func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64) (_ io.ReadCloser, retErr error) {
 	req.header.Set("Accept", strings.Join([]string{mediatype, `*/*`}, ", "))
 
 	if offset > 0 {
@@ -162,13 +166,17 @@
 	if err != nil {
 		return nil, err
 	}
+	defer func() {
+		if retErr != nil {
+			resp.Body.Close()
+		}
+	}()
 
 	if resp.StatusCode > 299 {
 		// TODO(stevvooe): When doing a offset specific request, we should
 		// really distinguish between a 206 and a 200. In the case of 200, we
 		// can discard the bytes, hiding the seek behavior from the
 		// implementation.
-		defer resp.Body.Close()
 
 		if resp.StatusCode == http.StatusNotFound {
 			return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", req.String())
diff -Nru containerd-1.4.5~ds1/remotes/docker/pusher.go containerd-1.4.12~ds1/remotes/docker/pusher.go
--- containerd-1.4.5~ds1/remotes/docker/pusher.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/pusher.go	2021-11-18 03:52:12.000000000 +0800
@@ -109,12 +109,15 @@
 						// TODO: Set updated time?
 					},
 				})
+				resp.Body.Close()
 				return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
 			}
 		} else if resp.StatusCode != http.StatusNotFound {
+			resp.Body.Close()
 			// TODO: log error
 			return nil, errors.Errorf("unexpected response: %s", resp.Status)
 		}
+		resp.Body.Close()
 	}
 
 	if isManifest {
@@ -155,6 +158,7 @@
 				return nil, err
 			}
 		}
+		defer resp.Body.Close()
 
 		switch resp.StatusCode {
 		case http.StatusOK, http.StatusAccepted, http.StatusNoContent:
@@ -338,6 +342,7 @@
 	if resp == nil {
 		return errors.New("no response")
 	}
+	defer resp.Body.Close()
 
 	// 201 is specified return status, some registries return
 	// 200, 202 or 204.
diff -Nru containerd-1.4.5~ds1/remotes/docker/resolver.go containerd-1.4.12~ds1/remotes/docker/resolver.go
--- containerd-1.4.5~ds1/remotes/docker/resolver.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/resolver.go	2021-11-18 03:52:12.000000000 +0800
@@ -238,10 +238,10 @@
 	}
 
 	var (
-		lastErr error
-		paths   [][]string
-		dgst    = refspec.Digest()
-		caps    = HostCapabilityPull
+		firstErr error
+		paths    [][]string
+		dgst     = refspec.Digest()
+		caps     = HostCapabilityPull
 	)
 
 	if dgst != "" {
@@ -292,8 +292,8 @@
 					err = errors.Wrapf(err, "pull access denied, repository does not exist or may require authorization")
 				}
 				// Store the error for referencing later
-				if lastErr == nil {
-					lastErr = err
+				if firstErr == nil {
+					firstErr = err
 				}
 				log.G(ctx).WithError(err).Info("trying next host")
 				continue // try another host
@@ -305,7 +305,14 @@
 					log.G(ctx).Info("trying next host - response was http.StatusNotFound")
 					continue
 				}
-				return "", ocispec.Descriptor{}, errors.Errorf("unexpected status code %v: %v", u, resp.Status)
+				if resp.StatusCode > 399 {
+					// Set firstErr when encountering the first non-404 status code.
+					if firstErr == nil {
+						firstErr = errors.Errorf("pulling from host %s failed with status code %v: %v", host.Host, u, resp.Status)
+					}
+					continue // try another host
+				}
+				return "", ocispec.Descriptor{}, errors.Errorf("pulling from host %s failed with unexpected status code %v: %v", host.Host, u, resp.Status)
 			}
 			size := resp.ContentLength
 			contentType := getManifestMediaType(resp)
@@ -368,8 +375,8 @@
 			}
 			// Prevent resolving to excessively large manifests
 			if size > MaxManifestSize {
-				if lastErr == nil {
-					lastErr = errors.Wrapf(errdefs.ErrNotFound, "rejecting %d byte manifest for %s", size, ref)
+				if firstErr == nil {
+					firstErr = errors.Wrapf(errdefs.ErrNotFound, "rejecting %d byte manifest for %s", size, ref)
 				}
 				continue
 			}
@@ -385,11 +392,15 @@
 		}
 	}
 
-	if lastErr == nil {
-		lastErr = errors.Wrap(errdefs.ErrNotFound, ref)
+	// If above loop terminates without return, then there was an error.
+	// "firstErr" contains the first non-404 error. That is, "firstErr == nil"
+	// means that either no registries were given or each registry returned 404.
+
+	if firstErr == nil {
+		firstErr = errors.Wrap(errdefs.ErrNotFound, ref)
 	}
 
-	return "", ocispec.Descriptor{}, lastErr
+	return "", ocispec.Descriptor{}, firstErr
 }
 
 func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
@@ -548,7 +559,21 @@
 	if err := r.authorize(ctx, req); err != nil {
 		return nil, errors.Wrap(err, "failed to authorize")
 	}
-	resp, err := ctxhttp.Do(ctx, r.host.Client, req)
+
+	var client = &http.Client{}
+	if r.host.Client != nil {
+		*client = *r.host.Client
+	}
+	if client.CheckRedirect == nil {
+		client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+			if len(via) >= 10 {
+				return errors.New("stopped after 10 redirects")
+			}
+			return errors.Wrap(r.authorize(ctx, req), "failed to authorize redirect")
+		}
+	}
+
+	resp, err := ctxhttp.Do(ctx, client, req)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to do request")
 	}
diff -Nru containerd-1.4.5~ds1/remotes/docker/schema1/converter.go containerd-1.4.12~ds1/remotes/docker/schema1/converter.go
--- containerd-1.4.5~ds1/remotes/docker/schema1/converter.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/schema1/converter.go	2021-11-18 03:52:12.000000000 +0800
@@ -256,6 +256,9 @@
 	if err := json.Unmarshal(b, &m); err != nil {
 		return err
 	}
+	if len(m.Manifests) != 0 || len(m.Layers) != 0 {
+		return errors.New("converter: expected schema1 document but found extra keys")
+	}
 	c.pulledManifest = &m
 
 	return nil
@@ -472,8 +475,10 @@
 }
 
 type manifest struct {
-	FSLayers []fsLayer `json:"fsLayers"`
-	History  []history `json:"history"`
+	FSLayers  []fsLayer       `json:"fsLayers"`
+	History   []history       `json:"history"`
+	Layers    json.RawMessage `json:"layers,omitempty"`    // OCI manifest
+	Manifests json.RawMessage `json:"manifests,omitempty"` // OCI index
 }
 
 type v1History struct {
diff -Nru containerd-1.4.5~ds1/runtime/v1/linux/bundle.go containerd-1.4.12~ds1/runtime/v1/linux/bundle.go
--- containerd-1.4.5~ds1/runtime/v1/linux/bundle.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v1/linux/bundle.go	2021-11-18 03:52:12.000000000 +0800
@@ -21,6 +21,7 @@
 import (
 	"context"
 	"crypto/sha256"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -30,6 +31,7 @@
 	"github.com/containerd/containerd/runtime/linux/runctypes"
 	"github.com/containerd/containerd/runtime/v1/shim"
 	"github.com/containerd/containerd/runtime/v1/shim/client"
+	"github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 )
 
@@ -48,7 +50,7 @@
 		return nil, err
 	}
 	path = filepath.Join(path, id)
-	if err := os.Mkdir(path, 0711); err != nil {
+	if err := os.Mkdir(path, 0700); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -56,6 +58,9 @@
 			os.RemoveAll(path)
 		}
 	}()
+	if err := prepareBundleDirectoryPermissions(path, spec); err != nil {
+		return nil, err
+	}
 	workDir = filepath.Join(workDir, id)
 	if err := os.MkdirAll(workDir, 0711); err != nil {
 		return nil, err
@@ -77,6 +82,55 @@
 	}, err
 }
 
+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
+// directory. When user namespaces are enabled, the permissions are modified
+// to allow the remapped root GID to access the bundle.
+func prepareBundleDirectoryPermissions(path string, spec []byte) error {
+	gid, err := remappedGID(spec)
+	if err != nil {
+		return err
+	}
+	if gid == 0 {
+		return nil
+	}
+	if err := os.Chown(path, -1, int(gid)); err != nil {
+		return err
+	}
+	return os.Chmod(path, 0710)
+}
+
+// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
+// unmarshal.
+type ociSpecUserNS struct {
+	Linux *linuxSpecUserNS
+}
+
+// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
+// unmarshal.
+type linuxSpecUserNS struct {
+	GIDMappings []specs.LinuxIDMapping
+}
+
+// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
+// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
+// remappedGID returns an error.
+func remappedGID(spec []byte) (uint32, error) {
+	var ociSpec ociSpecUserNS
+	err := json.Unmarshal(spec, &ociSpec)
+	if err != nil {
+		return 0, err
+	}
+	if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
+		return 0, nil
+	}
+	for _, mapping := range ociSpec.Linux.GIDMappings {
+		if mapping.ContainerID == 0 {
+			return mapping.HostID, nil
+		}
+	}
+	return 0, nil
+}
+
 type bundle struct {
 	id      string
 	path    string
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle_default.go containerd-1.4.12~ds1/runtime/v2/bundle_default.go
--- containerd-1.4.5~ds1/runtime/v2/bundle_default.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle_default.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,24 @@
+//go:build !linux
+// +build !linux
+
+/*
+   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 v2
+
+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
+// directory according to the needs of the current platform.
+func prepareBundleDirectoryPermissions(path string, spec []byte) error { return nil }
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle.go containerd-1.4.12~ds1/runtime/v2/bundle.go
--- containerd-1.4.5~ds1/runtime/v2/bundle.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle.go	2021-11-18 03:52:12.000000000 +0800
@@ -72,7 +72,10 @@
 	if err := os.MkdirAll(filepath.Dir(b.Path), 0711); err != nil {
 		return nil, err
 	}
-	if err := os.Mkdir(b.Path, 0711); err != nil {
+	if err := os.Mkdir(b.Path, 0700); err != nil {
+		return nil, err
+	}
+	if err := prepareBundleDirectoryPermissions(b.Path, spec); err != nil {
 		return nil, err
 	}
 	paths = append(paths, b.Path)
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle_linux.go containerd-1.4.12~ds1/runtime/v2/bundle_linux.go
--- containerd-1.4.5~ds1/runtime/v2/bundle_linux.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle_linux.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,74 @@
+/*
+   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 v2
+
+import (
+	"encoding/json"
+	"os"
+
+	"github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
+// directory according to the needs of the current platform.
+// On Linux when user namespaces are enabled, the permissions are modified to
+// allow the remapped root GID to access the bundle.
+func prepareBundleDirectoryPermissions(path string, spec []byte) error {
+	gid, err := remappedGID(spec)
+	if err != nil {
+		return err
+	}
+	if gid == 0 {
+		return nil
+	}
+	if err := os.Chown(path, -1, int(gid)); err != nil {
+		return err
+	}
+	return os.Chmod(path, 0710)
+}
+
+// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
+// unmarshal.
+type ociSpecUserNS struct {
+	Linux *linuxSpecUserNS
+}
+
+// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
+// unmarshal.
+type linuxSpecUserNS struct {
+	GIDMappings []specs.LinuxIDMapping
+}
+
+// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
+// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
+// remappedGID returns an error.
+func remappedGID(spec []byte) (uint32, error) {
+	var ociSpec ociSpecUserNS
+	err := json.Unmarshal(spec, &ociSpec)
+	if err != nil {
+		return 0, err
+	}
+	if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
+		return 0, nil
+	}
+	for _, mapping := range ociSpec.Linux.GIDMappings {
+		if mapping.ContainerID == 0 {
+			return mapping.HostID, nil
+		}
+	}
+	return 0, nil
+}
diff -Nru containerd-1.4.5~ds1/script/setup/runc-version containerd-1.4.12~ds1/script/setup/runc-version
--- containerd-1.4.5~ds1/script/setup/runc-version	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/script/setup/runc-version	2021-11-18 03:52:12.000000000 +0800
@@ -1 +1 @@
-v1.0.0-rc94
+v1.0.2
diff -Nru containerd-1.4.5~ds1/snapshots/btrfs/btrfs.go containerd-1.4.12~ds1/snapshots/btrfs/btrfs.go
--- containerd-1.4.5~ds1/snapshots/btrfs/btrfs.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/snapshots/btrfs/btrfs.go	2021-11-18 03:52:12.000000000 +0800
@@ -63,11 +63,15 @@
 // root needs to be a mount point of btrfs.
 func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
 	// If directory does not exist, create it
-	if _, err := os.Stat(root); err != nil {
+	if st, err := os.Stat(root); err != nil {
 		if !os.IsNotExist(err) {
 			return nil, err
 		}
-		if err := os.Mkdir(root, 0755); err != nil {
+		if err := os.Mkdir(root, 0700); err != nil {
+			return nil, err
+		}
+	} else if st.Mode()&os.ModePerm != 0700 {
+		if err := os.Chmod(root, 0700); err != nil {
 			return nil, err
 		}
 	}
diff -Nru containerd-1.4.5~ds1/snapshots/devmapper/snapshotter.go containerd-1.4.12~ds1/snapshots/devmapper/snapshotter.go
--- containerd-1.4.5~ds1/snapshots/devmapper/snapshotter.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/snapshots/devmapper/snapshotter.go	2021-11-18 03:52:12.000000000 +0800
@@ -232,7 +232,7 @@
 
 // View creates readonly thin device for the given snapshot key
 func (s *Snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
-	log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("prepare")
+	log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("view")
 
 	var (
 		mounts []mount.Mount
@@ -511,6 +511,8 @@
 }
 
 func (s *Snapshotter) Cleanup(ctx context.Context) error {
+	log.G(ctx).Debug("cleanup")
+
 	var removedDevices []*DeviceInfo
 
 	if !s.config.AsyncRemove {
diff -Nru containerd-1.4.5~ds1/Vagrantfile containerd-1.4.12~ds1/Vagrantfile
--- containerd-1.4.5~ds1/Vagrantfile	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/Vagrantfile	2021-11-18 03:52:12.000000000 +0800
@@ -77,7 +77,7 @@
   config.vm.provision "install-golang", type: "shell", run: "once" do |sh|
     sh.upload_path = "/tmp/vagrant-install-golang"
     sh.env = {
-        'GO_VERSION': ENV['GO_VERSION'] || "1.15.11",
+        'GO_VERSION': ENV['GO_VERSION'] || "1.16.10",
     }
     sh.inline = <<~SHELL
         #!/usr/bin/env bash
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os.go	2021-11-18 03:52:12.000000000 +0800
@@ -20,7 +20,6 @@
 	"io"
 	"io/ioutil"
 	"os"
-	"path/filepath"
 
 	"github.com/docker/docker/pkg/symlink"
 )
@@ -56,18 +55,6 @@
 	return os.Stat(name)
 }
 
-// ResolveSymbolicLink will follow any symbolic links
-func (RealOS) ResolveSymbolicLink(path string) (string, error) {
-	info, err := os.Lstat(path)
-	if err != nil {
-		return "", err
-	}
-	if info.Mode()&os.ModeSymlink != os.ModeSymlink {
-		return path, nil
-	}
-	return filepath.EvalSymlinks(path)
-}
-
 // FollowSymlinkInScope will call symlink.FollowSymlinkInScope.
 func (RealOS) FollowSymlinkInScope(path, scope string) (string, error) {
 	return symlink.FollowSymlinkInScope(path, scope)
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go	2021-11-18 03:52:12.000000000 +0800
@@ -19,6 +19,9 @@
 package os
 
 import (
+	"os"
+	"path/filepath"
+
 	"github.com/containerd/containerd/mount"
 	"golang.org/x/sys/unix"
 )
@@ -57,3 +60,15 @@
 
 	return err
 }
+
+// ResolveSymbolicLink will follow any symbolic links
+func (RealOS) ResolveSymbolicLink(path string) (string, error) {
+	info, err := os.Lstat(path)
+	if err != nil {
+		return "", err
+	}
+	if info.Mode()&os.ModeSymlink != os.ModeSymlink {
+		return path, nil
+	}
+	return filepath.EvalSymlinks(path)
+}
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go	2021-11-18 03:52:12.000000000 +0800
@@ -124,8 +124,10 @@
 		sandbox.NetNSPath = sandbox.NetNS.GetPath()
 		defer func() {
 			if retErr != nil {
+				deferCtx, deferCancel := ctrdutil.DeferContext()
+				defer deferCancel()
 				// Teardown network if an error is returned.
-				if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
+				if err := c.teardownPodNetwork(deferCtx, sandbox); err != nil {
 					log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
 				}
 
diff -Nru containerd-1.4.5~ds1/version/version.go containerd-1.4.12~ds1/version/version.go
--- containerd-1.4.5~ds1/version/version.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/version/version.go	2021-11-18 03:52:12.000000000 +0800
@@ -23,7 +23,7 @@
 	Package = "github.com/containerd/containerd"
 
 	// Version holds the complete version number. Filled in at linking time.
-	Version = "1.4.5+unknown"
+	Version = "1.4.12+unknown"
 
 	// Revision is filled with the VCS (e.g. git) revision being used to build
 	// the program at linking time.

[ Other info ]

Since it's a Go package, and containerd as library is static linked in other packages.
However I don't think we need to rebuild other packages. The most fixes are targeted
for containerd as a program.

The RPi0/RPi1 workaround may need to rebuild docker.io. But I think we can skip this
time. As I think docker.io needs its own update for bullseye as well, to fix things
like supporting "clone3" syscall.
diff -Nru containerd-1.4.5~ds1/archive/tar_test.go containerd-1.4.12~ds1/archive/tar_test.go
--- containerd-1.4.5~ds1/archive/tar_test.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/archive/tar_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -243,6 +243,11 @@
 		return nil
 	}
 	errFileDiff := errors.New("files differ")
+	td, err := ioutil.TempDir("", "test-breakouts-")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(td)
 
 	isSymlinkFile := func(f string) func(string) error {
 		return func(root string) error {
@@ -744,6 +749,36 @@
 			// resolution ends up just removing etc
 			validator: fileNotExists("etc/passwd"),
 		},
+		{
+
+			name: "HardlinkSymlinkChmod",
+			w: func() tartest.WriterToTar {
+				p := filepath.Join(td, "perm400")
+				if err := ioutil.WriteFile(p, []byte("..."), 0400); err != nil {
+					t.Fatal(err)
+				}
+				ep := filepath.Join(td, "also-exists-outside-root")
+				if err := ioutil.WriteFile(ep, []byte("..."), 0640); err != nil {
+					t.Fatal(err)
+				}
+
+				return tartest.TarAll(
+					tc.Symlink(p, ep),
+					tc.Link(ep, "sketchylink"),
+				)
+			}(),
+			validator: func(string) error {
+				p := filepath.Join(td, "perm400")
+				fi, err := os.Lstat(p)
+				if err != nil {
+					return err
+				}
+				if perm := fi.Mode() & os.ModePerm; perm != 0400 {
+					return errors.Errorf("%s perm changed from 0400 to %04o", p, perm)
+				}
+				return nil
+			},
+		},
 	}
 
 	for _, bo := range breakouts {
diff -Nru containerd-1.4.5~ds1/archive/tar_unix.go containerd-1.4.12~ds1/archive/tar_unix.go
--- containerd-1.4.5~ds1/archive/tar_unix.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/archive/tar_unix.go	2021-11-18 03:52:12.000000000 +0800
@@ -113,7 +113,7 @@
 
 func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
 	if hdr.Typeflag == tar.TypeLink {
-		if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
+		if fi, err := os.Lstat(path); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
 			if err := os.Chmod(path, hdrInfo.Mode()); err != nil && !os.IsNotExist(err) {
 				return err
 			}
diff -Nru containerd-1.4.5~ds1/client_test.go containerd-1.4.12~ds1/client_test.go
--- containerd-1.4.5~ds1/client_test.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/client_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -309,7 +309,7 @@
 	defer cancel()
 
 	cs := client.ContentStore()
-	img, err := client.Fetch(ctx, "docker.io/library/busybox:latest")
+	img, err := client.Fetch(ctx, "ghcr.io/containerd/busybox:latest")
 	if err != nil {
 		t.Fatal(err)
 	}
diff -Nru containerd-1.4.5~ds1/client_unix_test.go containerd-1.4.12~ds1/client_unix_test.go
--- containerd-1.4.5~ds1/client_unix_test.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/client_unix_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -40,17 +40,17 @@
 func init() {
 	switch runtime.GOARCH {
 	case "386":
-		testImage = "docker.io/i386/alpine:latest"
+		testImage = "ghcr.io/containerd/alpine:latest-i386"
 	case "arm":
-		testImage = "docker.io/arm32v6/alpine:latest"
+		testImage = "ghcr.io/containerd/alpine:latest-arm32v6"
 	case "arm64":
-		testImage = "docker.io/arm64v8/alpine:latest"
+		testImage = "ghcr.io/containerd/alpine:latest-arm64v8"
 	case "ppc64le":
-		testImage = "docker.io/ppc64le/alpine:latest"
+		testImage = "ghcr.io/containerd/alpine:latest-ppc64le"
 	case "s390x":
-		testImage = "docker.io/s390x/alpine:latest"
+		testImage = "ghcr.io/containerd/alpine:latest-s390x"
 	default:
-		testImage = "docker.io/library/alpine:latest"
+		testImage = "ghcr.io/containerd/alpine:latest"
 	}
 }
 
diff -Nru containerd-1.4.5~ds1/content/local/store.go containerd-1.4.12~ds1/content/local/store.go
--- containerd-1.4.5~ds1/content/local/store.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/content/local/store.go	2021-11-18 03:52:12.000000000 +0800
@@ -502,7 +502,7 @@
 	if ref != status.Ref {
 		// NOTE(stevvooe): This is fairly catastrophic. Either we have some
 		// layout corruption or a hash collision for the ref key.
-		return status, errors.Wrapf(err, "ref key does not match: %v != %v", ref, status.Ref)
+		return status, errors.Errorf("ref key does not match: %v != %v", ref, status.Ref)
 	}
 
 	if total > 0 && status.Total > 0 && total != status.Total {
diff -Nru containerd-1.4.5~ds1/contrib/Dockerfile.test containerd-1.4.12~ds1/contrib/Dockerfile.test
--- containerd-1.4.5~ds1/contrib/Dockerfile.test	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/contrib/Dockerfile.test	2021-11-18 03:52:12.000000000 +0800
@@ -10,7 +10,7 @@
 #
 # docker build -t containerd-test --build-arg RUNC_VERSION=v1.0.0-rc93 -f Dockerfile.test ../
 
-ARG GOLANG_VERSION=1.15.11
+ARG GOLANG_VERSION=1.16.10
 
 FROM golang:${GOLANG_VERSION} AS golang-base
 RUN mkdir -p /go/src/github.com/containerd/containerd
diff -Nru containerd-1.4.5~ds1/contrib/seccomp/seccomp_default.go containerd-1.4.12~ds1/contrib/seccomp/seccomp_default.go
--- containerd-1.4.5~ds1/contrib/seccomp/seccomp_default.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/contrib/seccomp/seccomp_default.go	2021-11-18 03:52:12.000000000 +0800
@@ -49,6 +49,7 @@
 
 // DefaultProfile defines the allowed syscalls for the default seccomp profile.
 func DefaultProfile(sp *specs.Spec) *specs.LinuxSeccomp {
+	nosys := uint(unix.ENOSYS)
 	syscalls := []specs.LinuxSyscall{
 		{
 			Names: []string{
@@ -524,6 +525,7 @@
 				Names: []string{
 					"bpf",
 					"clone",
+					"clone3",
 					"fanotify_init",
 					"lookup_dcookie",
 					"mount",
@@ -648,6 +650,15 @@
 				},
 			})
 		}
+		// clone3 is explicitly requested to give ENOSYS instead of the default EPERM, when CAP_SYS_ADMIN is unset
+		// https://github.com/moby/moby/pull/42681
+		s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{
+			Names: []string{
+				"clone3",
+			},
+			Action:   specs.ActErrno,
+			ErrnoRet: &nosys,
+		})
 	}
 
 	return s
diff -Nru containerd-1.4.5~ds1/debian/changelog containerd-1.4.12~ds1/debian/changelog
--- containerd-1.4.5~ds1/debian/changelog	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/changelog	2021-11-23 18:42:16.000000000 +0800
@@ -1,3 +1,34 @@
+containerd (1.4.12~ds1-1~deb11u1) bullseye; urgency=medium
+
+  * New upstream version 1.4.12~ds1
+    + 1.4.12
+      * Mitigate CVE-2021-41190: Handle ambiguous OCI manifest parsing
+      * Update pull to try next mirror for non-404 errors
+      * Update pull to handle of non-https urls in descriptors
+    + 1.4.11
+      * CVE-2021-41103: Fix insufficiently restricted permissions on container
+        root and plugin directories
+    + 1.4.10
+      * Support "clone3" in default seccomp profile
+      * Fix panic in metadata content writer on copy error
+    + 1.4.9
+      * Update pull authorization logic on redirect
+      * Fix user agent used for fetching registry authentication tokens
+    + 1.4.8
+      * CVE-2021-32760: Archive package allows chmod of file outside of unpack
+        target directory
+    + 1.4.7
+      * Fix invalid validation error checking
+      * Fix error on image pull resume
+  * Refresh patches
+    + Drop CVE-2021-32760 patch
+    + Drop CVE-2021-41103 patch
+    + Refresh 0005-backport-github.com-containerd-containerd-remotes.patch
+      with latest 1.5 release branch
+  * Backport RPi1/RPi0 workaround (Closes: #998909)
+
+ -- Shengjing Zhu <zhsj@debian.org>  Tue, 23 Nov 2021 18:42:16 +0800
+
 containerd (1.4.5~ds1-2+deb11u1) bullseye-security; urgency=high
 
   * CVE-2021-41103: Insufficiently restricted permissions on container
diff -Nru containerd-1.4.5~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch containerd-1.4.12~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch
--- containerd-1.4.5~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0004-Add-cgo-tag-to-btrfs-plugin.patch	2021-11-23 18:42:16.000000000 +0800
@@ -20,7 +20,7 @@
  /*
     Copyright The containerd Authors.
 diff --git a/snapshots/btrfs/btrfs.go b/snapshots/btrfs/btrfs.go
-index ea90853..eef883b 100644
+index 78f825c..778b783 100644
 --- a/snapshots/btrfs/btrfs.go
 +++ b/snapshots/btrfs/btrfs.go
 @@ -1,4 +1,4 @@
diff -Nru containerd-1.4.5~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch containerd-1.4.12~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch
--- containerd-1.4.5~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0005-backport-github.com-containerd-containerd-remotes.patch	2021-11-23 18:42:16.000000000 +0800
@@ -1,6 +1,7 @@
 From: Shengjing Zhu <zhsj@debian.org>
-Date: Fri, 5 Mar 2021 16:35:17 +0800
-Subject: backport_github=2Ecom/containerd/containerd/remotes
+Date: Tue, 23 Nov 2021 15:49:57 +0800
+Subject: =?utf-8?q?backport_github=2Ecom/containerd/containerd/remotes?=
+ =?utf-8?q?=C2=AC?=
 
 For building docker.io 20.10
 
@@ -10,19 +11,19 @@
 
 Forwarded: not-needed
 ---
- remotes/docker/auth.go           | 198 ---------------------------
- remotes/docker/auth/fetch.go     | 202 +++++++++++++++++++++++++++
- remotes/docker/auth/parse.go     | 203 ++++++++++++++++++++++++++++
- remotes/docker/authorizer.go     | 285 ++++++++++-----------------------------
+ remotes/docker/auth.go           | 198 --------------------------
+ remotes/docker/auth/fetch.go     | 209 ++++++++++++++++++++++++++++
+ remotes/docker/auth/parse.go     | 203 +++++++++++++++++++++++++++
+ remotes/docker/authorizer.go     | 292 +++++++++------------------------------
  remotes/docker/errcode.go        |   2 +-
  remotes/docker/fetcher.go        |   2 +-
  remotes/docker/httpreadseeker.go |   2 +-
- remotes/docker/pusher.go         |  36 +++--
- remotes/docker/resolver.go       |  47 +++----
+ remotes/docker/pusher.go         |  38 +++--
+ remotes/docker/resolver.go       |  49 +++----
  remotes/docker/scope.go          |  14 +-
  remotes/docker/scope_test.go     |   4 +-
- remotes/errors/errors.go         |  46 +++++++
- 12 files changed, 573 insertions(+), 468 deletions(-)
+ remotes/errors/errors.go         |  46 ++++++
+ 12 files changed, 581 insertions(+), 478 deletions(-)
  delete mode 100644 remotes/docker/auth.go
  create mode 100644 remotes/docker/auth/fetch.go
  create mode 100644 remotes/docker/auth/parse.go
@@ -234,10 +235,10 @@
 -}
 diff --git a/remotes/docker/auth/fetch.go b/remotes/docker/auth/fetch.go
 new file mode 100644
-index 0000000..0d01c81
+index 0000000..8b0a87e
 --- /dev/null
 +++ b/remotes/docker/auth/fetch.go
-@@ -0,0 +1,202 @@
+@@ -0,0 +1,209 @@
 +/*
 +   Copyright The containerd Authors.
 +
@@ -266,6 +267,7 @@
 +
 +	"github.com/containerd/containerd/log"
 +	remoteserrors "github.com/containerd/containerd/remotes/errors"
++	"github.com/containerd/containerd/version"
 +	"github.com/pkg/errors"
 +	"golang.org/x/net/context/ctxhttp"
 +)
@@ -349,6 +351,9 @@
 +	for k, v := range headers {
 +		req.Header[k] = append(req.Header[k], v...)
 +	}
++	if len(req.Header.Get("User-Agent")) == 0 {
++		req.Header.Set("User-Agent", "containerd/"+version.Version)
++	}
 +
 +	resp, err := ctxhttp.Do(ctx, client, req)
 +	if err != nil {
@@ -393,6 +398,9 @@
 +	for k, v := range headers {
 +		req.Header[k] = append(req.Header[k], v...)
 +	}
++	if len(req.Header.Get("User-Agent")) == 0 {
++		req.Header.Set("User-Agent", "containerd/"+version.Version)
++	}
 +
 +	reqParams := req.URL.Query()
 +
@@ -650,10 +658,10 @@
 +	return "", ""
 +}
 diff --git a/remotes/docker/authorizer.go b/remotes/docker/authorizer.go
-index 001423a..67e4aea 100644
+index 2f459b7..67e4aea 100644
 --- a/remotes/docker/authorizer.go
 +++ b/remotes/docker/authorizer.go
-@@ -19,21 +19,17 @@ package docker
+@@ -19,22 +19,17 @@ package docker
  import (
  	"context"
  	"encoding/base64"
@@ -669,6 +677,7 @@
  
  	"github.com/containerd/containerd/errdefs"
  	"github.com/containerd/containerd/log"
+-	"github.com/containerd/containerd/version"
 +	"github.com/containerd/containerd/remotes/docker/auth"
 +	remoteerrors "github.com/containerd/containerd/remotes/errors"
  	"github.com/pkg/errors"
@@ -677,7 +686,7 @@
  )
  
  type dockerAuthorizer struct {
-@@ -135,8 +131,8 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
+@@ -136,8 +131,8 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
  
  	a.mu.Lock()
  	defer a.mu.Unlock()
@@ -688,7 +697,7 @@
  			if err := invalidAuthorization(c, responses); err != nil {
  				delete(a.handlers, host)
  				return err
-@@ -152,26 +148,35 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
+@@ -153,26 +148,35 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
  				return nil
  			}
  
@@ -731,7 +740,7 @@
  				return nil
  			}
  		}
-@@ -179,38 +184,6 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
+@@ -180,38 +184,6 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
  	return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme")
  }
  
@@ -770,7 +779,7 @@
  // authResult is used to control limit rate.
  type authResult struct {
  	sync.WaitGroup
-@@ -227,17 +200,17 @@ type authHandler struct {
+@@ -228,17 +200,17 @@ type authHandler struct {
  	client *http.Client
  
  	// only support basic and bearer schemes
@@ -791,7 +800,7 @@
  	return &authHandler{
  		header:       hdr,
  		client:       client,
-@@ -249,17 +222,17 @@ func newAuthHandler(client *http.Client, hdr http.Header, scheme authenticationS
+@@ -250,17 +222,17 @@ func newAuthHandler(client *http.Client, hdr http.Header, scheme authenticationS
  
  func (ah *authHandler) authorize(ctx context.Context) (string, error) {
  	switch ah.scheme {
@@ -813,7 +822,7 @@
  
  	if username == "" || secret == "" {
  		return "", fmt.Errorf("failed to handle basic auth because missing username or secret")
-@@ -269,14 +242,14 @@ func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) {
+@@ -270,14 +242,14 @@ func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) {
  	return fmt.Sprintf("Basic %s", auth), nil
  }
  
@@ -831,7 +840,7 @@
  
  	ah.Lock()
  	if r, exist := ah.scopedTokens[scoped]; exist {
-@@ -291,174 +264,52 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (string, error) {
+@@ -292,180 +264,52 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (string, error) {
  	ah.scopedTokens[scoped] = r
  	ah.Unlock()
  
@@ -910,6 +919,9 @@
 -			req.Header[k] = append(req.Header[k], v...)
 -		}
 -	}
+-	if len(req.Header.Get("User-Agent")) == 0 {
+-		req.Header.Set("User-Agent", "containerd/"+version.Version)
+-	}
 -
 -	resp, err := ctxhttp.Do(ctx, ah.client, req)
 -	if err != nil {
@@ -984,6 +996,9 @@
  		}
 +		return resp.AccessToken, nil
  	}
+-	if len(req.Header.Get("User-Agent")) == 0 {
+-		req.Header.Set("User-Agent", "containerd/"+version.Version)
+-	}
 -
 -	reqParams := req.URL.Query()
 -
@@ -1058,7 +1073,7 @@
  	Message string
  
 diff --git a/remotes/docker/fetcher.go b/remotes/docker/fetcher.go
-index cd0168b..9d6708d 100644
+index 5aaaf9e..4b2c10e 100644
 --- a/remotes/docker/fetcher.go
 +++ b/remotes/docker/fetcher.go
 @@ -45,7 +45,7 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
@@ -1084,7 +1099,7 @@
  
  		if hrs.rc != nil {
 diff --git a/remotes/docker/pusher.go b/remotes/docker/pusher.go
-index d95e0c8..94a270c 100644
+index d46396b..c367b1e 100644
 --- a/remotes/docker/pusher.go
 +++ b/remotes/docker/pusher.go
 @@ -30,6 +30,7 @@ import (
@@ -1104,19 +1119,20 @@
  	if err != nil {
  		return nil, err
  	}
-@@ -112,8 +113,9 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -113,9 +114,10 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  				return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
  			}
  		} else if resp.StatusCode != http.StatusNotFound {
--			// TODO: log error
--			return nil, errors.Errorf("unexpected response: %s", resp.Status)
 +			err := remoteserrors.NewUnexpectedStatusErr(resp)
 +			log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response")
+ 			resp.Body.Close()
+-			// TODO: log error
+-			return nil, errors.Errorf("unexpected response: %s", resp.Status)
 +			return nil, err
  		}
+ 		resp.Body.Close()
  	}
- 
-@@ -128,7 +130,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -131,7 +133,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  		var resp *http.Response
  		if fromRepo := selectRepositoryMountCandidate(p.refspec, desc.Annotations); fromRepo != "" {
  			preq := requestWithMountFrom(req, desc.Digest.String(), fromRepo)
@@ -1125,7 +1141,7 @@
  
  			// NOTE: the fromRepo might be private repo and
  			// auth service still can grant token without error.
-@@ -166,8 +168,9 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -170,8 +172,9 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  			})
  			return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
  		default:
@@ -1137,7 +1153,7 @@
  		}
  
  		var (
-@@ -219,7 +222,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -223,7 +226,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  	// TODO: Support chunked upload
  
  	pr, pw := io.Pipe()
@@ -1146,7 +1162,7 @@
  	body := ioutil.NopCloser(pr)
  
  	req.body = func() (io.ReadCloser, error) {
-@@ -237,6 +240,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -241,6 +244,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  		defer close(respC)
  		resp, err := req.doWithRetries(ctx, nil)
  		if err != nil {
@@ -1154,7 +1170,7 @@
  			pr.CloseWithError(err)
  			return
  		}
-@@ -244,10 +248,11 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
+@@ -248,10 +252,11 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
  		switch resp.StatusCode {
  		case http.StatusOK, http.StatusCreated, http.StatusNoContent:
  		default:
@@ -1169,7 +1185,7 @@
  	}()
  
  	return &pushWriter{
-@@ -280,12 +285,17 @@ func getManifestPath(object string, dgst digest.Digest) []string {
+@@ -284,12 +289,17 @@ func getManifestPath(object string, dgst digest.Digest) []string {
  	return []string{"manifests", object}
  }
  
@@ -1188,7 +1204,7 @@
  	isManifest bool
  
  	expected digest.Digest
-@@ -335,8 +345,8 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di
+@@ -339,10 +349,10 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di
  
  	// TODO: timeout waiting for response
  	resp := <-pw.responseC
@@ -1197,10 +1213,13 @@
 +	if resp.err != nil {
 +		return resp.err
  	}
+-	defer resp.Body.Close()
++	defer resp.Response.Body.Close()
  
  	// 201 is specified return status, some registries return
+ 	// 200, 202 or 204.
 diff --git a/remotes/docker/resolver.go b/remotes/docker/resolver.go
-index aea092c..866379e 100644
+index 40831f1..d6ccd70 100644
 --- a/remotes/docker/resolver.go
 +++ b/remotes/docker/resolver.go
 @@ -41,10 +41,6 @@ import (
@@ -1235,8 +1254,8 @@
 -	}
 -
  	var (
- 		lastErr error
- 		paths   [][]string
+ 		firstErr error
+ 		paths    [][]string
 @@ -267,7 +258,7 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
  		return "", ocispec.Descriptor{}, errors.Wrap(errdefs.ErrNotFound, "no resolve hosts")
  	}
@@ -1246,7 +1265,22 @@
  	if err != nil {
  		return "", ocispec.Descriptor{}, err
  	}
-@@ -393,12 +384,7 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
+@@ -295,14 +286,12 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
+ 				if firstErr == nil {
+ 					firstErr = err
+ 				}
+-				log.G(ctx).WithError(err).Info("trying next host")
+ 				continue // try another host
+ 			}
+ 			resp.Body.Close() // don't care about body contents.
+ 
+ 			if resp.StatusCode > 299 {
+ 				if resp.StatusCode == http.StatusNotFound {
+-					log.G(ctx).Info("trying next host - response was http.StatusNotFound")
+ 					continue
+ 				}
+ 				if resp.StatusCode > 399 {
+@@ -404,12 +393,7 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
  }
  
  func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
@@ -1260,7 +1294,7 @@
  	if err != nil {
  		return nil, err
  	}
-@@ -409,23 +395,27 @@ func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetch
+@@ -420,23 +404,27 @@ func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetch
  }
  
  func (r *dockerResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
@@ -1295,7 +1329,7 @@
  type dockerBase struct {
  	refspec    reference.Spec
  	repository string
-@@ -457,10 +447,11 @@ func (r *dockerBase) filterHosts(caps HostCapabilities) (hosts []RegistryHost) {
+@@ -468,10 +456,11 @@ func (r *dockerBase) filterHosts(caps HostCapabilities) (hosts []RegistryHost) {
  }
  
  func (r *dockerBase) request(host RegistryHost, method string, ps ...string) *request {
diff -Nru containerd-1.4.5~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch containerd-1.4.12~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch
--- containerd-1.4.5~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0008-Add-RPi1-RPi0-workaround.patch	2021-11-23 18:42:16.000000000 +0800
@@ -0,0 +1,35 @@
+From: Tianon Gravi <admwiggin@gmail.com>
+Date: Fri, 4 Sep 2020 14:12:04 -0700
+Subject: Add RPi1/RPi0 workaround
+
+On the very popular Raspberry Pi 1 and Zero devices, the CPU is actually ARMv6, but the chip happens to support the feature bit the kernel uses to differentiate v6/v7, so it gets reported as "CPU architecture: 7" and thus fails to run many of the images that get pulled.
+
+To account for this very popular edge case, this also checks "model name" which on these chips will begin with "ARMv6-compatible" -- we could also check uname, but getCPUInfo is already handy, low overhead, and mirrors the code before this.
+
+Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
+
+Origin: backport, https://github.com/containerd/containerd/commit/2055e12953bb538228d8d9fe627fa545d7cf82be
+---
+ platforms/cpuinfo.go | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/platforms/cpuinfo.go b/platforms/cpuinfo.go
+index db65a72..0512bc9 100644
+--- a/platforms/cpuinfo.go
++++ b/platforms/cpuinfo.go
+@@ -96,6 +96,15 @@ func getCPUVariant() string {
+ 		return ""
+ 	}
+ 
++	// handle edge case for Raspberry Pi ARMv6 devices (which due to a kernel quirk, report "CPU architecture: 7")
++	// https://www.raspberrypi.org/forums/viewtopic.php?t=12614
++	if runtime.GOARCH == "arm" && variant == "7" {
++		model, err := getCPUInfo("model name")
++		if err == nil && strings.HasPrefix(strings.ToLower(model), "armv6-compatible") {
++			variant = "6"
++		}
++	}
++
+ 	switch strings.ToLower(variant) {
+ 	case "8", "aarch64":
+ 		// special case: if running a 32-bit userspace on aarch64, the variant should be "v7"
diff -Nru containerd-1.4.5~ds1/debian/patches/0008-CVE-2021-32760.patch containerd-1.4.12~ds1/debian/patches/0008-CVE-2021-32760.patch
--- containerd-1.4.5~ds1/debian/patches/0008-CVE-2021-32760.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0008-CVE-2021-32760.patch	1970-01-01 08:00:00.000000000 +0800
@@ -1,91 +0,0 @@
-From 03aa748c11663e87a72fab92b7ab7c88c28bf13e Mon Sep 17 00:00:00 2001
-From: Derek McGowan <derek@mcg.dev>
-Date: Tue, 6 Jul 2021 12:37:54 -0700
-Subject: [PATCH 1/2] Use chmod path for checking symlink
-
-Signed-off-by: Derek McGowan <derek@mcg.dev>
-(cherry picked from commit 27597ccfd30d8aa06b448062896bccfb33ad8f22)
-Signed-off-by: Derek McGowan <derek@mcg.dev>
----
- archive/tar_unix.go | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/archive/tar_unix.go b/archive/tar_unix.go
-index 6e89d2fdbc9..c22e79bf2be 100644
---- a/archive/tar_unix.go
-+++ b/archive/tar_unix.go
-@@ -113,7 +113,7 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
- 
- func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
- 	if hdr.Typeflag == tar.TypeLink {
--		if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
-+		if fi, err := os.Lstat(path); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
- 			if err := os.Chmod(path, hdrInfo.Mode()); err != nil && !os.IsNotExist(err) {
- 				return err
- 			}
-
-From 664f93ead6c613a9f0e9932dfa75c602dbe35f41 Mon Sep 17 00:00:00 2001
-From: Derek McGowan <derek@mcg.dev>
-Date: Tue, 6 Jul 2021 16:23:03 -0700
-Subject: [PATCH 2/2] Add test for archive breakout test for lchmod
-
-Signed-off-by: Derek McGowan <derek@mcg.dev>
-(cherry picked from commit ad81d76219a75559cb9d74a214efe0d779d7cbef)
-Signed-off-by: Derek McGowan <derek@mcg.dev>
----
- archive/tar_test.go | 35 +++++++++++++++++++++++++++++++++++
- 1 file changed, 35 insertions(+)
-
-diff --git a/archive/tar_test.go b/archive/tar_test.go
-index 568f5a95f1c..8ffd3f221b8 100644
---- a/archive/tar_test.go
-+++ b/archive/tar_test.go
-@@ -243,6 +243,11 @@ func TestBreakouts(t *testing.T) {
- 		return nil
- 	}
- 	errFileDiff := errors.New("files differ")
-+	td, err := ioutil.TempDir("", "test-breakouts-")
-+	if err != nil {
-+		t.Fatal(err)
-+	}
-+	defer os.RemoveAll(td)
- 
- 	isSymlinkFile := func(f string) func(string) error {
- 		return func(root string) error {
-@@ -744,6 +749,36 @@ func TestBreakouts(t *testing.T) {
- 			// resolution ends up just removing etc
- 			validator: fileNotExists("etc/passwd"),
- 		},
-+		{
-+
-+			name: "HardlinkSymlinkChmod",
-+			w: func() tartest.WriterToTar {
-+				p := filepath.Join(td, "perm400")
-+				if err := ioutil.WriteFile(p, []byte("..."), 0400); err != nil {
-+					t.Fatal(err)
-+				}
-+				ep := filepath.Join(td, "also-exists-outside-root")
-+				if err := ioutil.WriteFile(ep, []byte("..."), 0640); err != nil {
-+					t.Fatal(err)
-+				}
-+
-+				return tartest.TarAll(
-+					tc.Symlink(p, ep),
-+					tc.Link(ep, "sketchylink"),
-+				)
-+			}(),
-+			validator: func(string) error {
-+				p := filepath.Join(td, "perm400")
-+				fi, err := os.Lstat(p)
-+				if err != nil {
-+					return err
-+				}
-+				if perm := fi.Mode() & os.ModePerm; perm != 0400 {
-+					return errors.Errorf("%s perm changed from 0400 to %04o", p, perm)
-+				}
-+				return nil
-+			},
-+		},
- 	}
- 
- 	for _, bo := range breakouts {
diff -Nru containerd-1.4.5~ds1/debian/patches/0009-CVE-2021-41103/0009-v2-runtime-reduce-permissions-for-bundle-dir.patch containerd-1.4.12~ds1/debian/patches/0009-CVE-2021-41103/0009-v2-runtime-reduce-permissions-for-bundle-dir.patch
--- containerd-1.4.5~ds1/debian/patches/0009-CVE-2021-41103/0009-v2-runtime-reduce-permissions-for-bundle-dir.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0009-CVE-2021-41103/0009-v2-runtime-reduce-permissions-for-bundle-dir.patch	1970-01-01 08:00:00.000000000 +0800
@@ -1,349 +0,0 @@
-From: Samuel Karp <skarp@amazon.com>
-Date: Mon, 20 Sep 2021 16:20:26 -0700
-Subject: v2 runtime: reduce permissions for bundle dir
-
-Bundle directory permissions should be 0700 by default.  On Linux with
-user namespaces enabled, the remapped root also needs access to the
-bundle directory.  In this case, the bundle directory is modified to
-0710 and group ownership is changed to the remapped root group.
-
-Signed-off-by: Samuel Karp <skarp@amazon.com>
----
- runtime/v2/bundle.go            |   5 +-
- runtime/v2/bundle_default.go    |  24 ++++++
- runtime/v2/bundle_linux.go      |  74 ++++++++++++++++++
- runtime/v2/bundle_linux_test.go | 166 ++++++++++++++++++++++++++++++++++++++++
- runtime/v2/bundle_test.go       |  23 ++++++
- 5 files changed, 291 insertions(+), 1 deletion(-)
- create mode 100644 runtime/v2/bundle_default.go
- create mode 100644 runtime/v2/bundle_linux.go
- create mode 100644 runtime/v2/bundle_linux_test.go
- create mode 100644 runtime/v2/bundle_test.go
-
-diff --git a/runtime/v2/bundle.go b/runtime/v2/bundle.go
-index 1a58e62..954163b 100644
---- a/runtime/v2/bundle.go
-+++ b/runtime/v2/bundle.go
-@@ -72,7 +72,10 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun
- 	if err := os.MkdirAll(filepath.Dir(b.Path), 0711); err != nil {
- 		return nil, err
- 	}
--	if err := os.Mkdir(b.Path, 0711); err != nil {
-+	if err := os.Mkdir(b.Path, 0700); err != nil {
-+		return nil, err
-+	}
-+	if err := prepareBundleDirectoryPermissions(b.Path, spec); err != nil {
- 		return nil, err
- 	}
- 	paths = append(paths, b.Path)
-diff --git a/runtime/v2/bundle_default.go b/runtime/v2/bundle_default.go
-new file mode 100644
-index 0000000..2be40c8
---- /dev/null
-+++ b/runtime/v2/bundle_default.go
-@@ -0,0 +1,24 @@
-+//go:build !linux
-+// +build !linux
-+
-+/*
-+   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 v2
-+
-+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
-+// directory according to the needs of the current platform.
-+func prepareBundleDirectoryPermissions(path string, spec []byte) error { return nil }
-diff --git a/runtime/v2/bundle_linux.go b/runtime/v2/bundle_linux.go
-new file mode 100644
-index 0000000..5f1915d
---- /dev/null
-+++ b/runtime/v2/bundle_linux.go
-@@ -0,0 +1,74 @@
-+/*
-+   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 v2
-+
-+import (
-+	"encoding/json"
-+	"os"
-+
-+	"github.com/opencontainers/runtime-spec/specs-go"
-+)
-+
-+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
-+// directory according to the needs of the current platform.
-+// On Linux when user namespaces are enabled, the permissions are modified to
-+// allow the remapped root GID to access the bundle.
-+func prepareBundleDirectoryPermissions(path string, spec []byte) error {
-+	gid, err := remappedGID(spec)
-+	if err != nil {
-+		return err
-+	}
-+	if gid == 0 {
-+		return nil
-+	}
-+	if err := os.Chown(path, -1, int(gid)); err != nil {
-+		return err
-+	}
-+	return os.Chmod(path, 0710)
-+}
-+
-+// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
-+// unmarshal.
-+type ociSpecUserNS struct {
-+	Linux *linuxSpecUserNS
-+}
-+
-+// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
-+// unmarshal.
-+type linuxSpecUserNS struct {
-+	GIDMappings []specs.LinuxIDMapping
-+}
-+
-+// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
-+// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
-+// remappedGID returns an error.
-+func remappedGID(spec []byte) (uint32, error) {
-+	var ociSpec ociSpecUserNS
-+	err := json.Unmarshal(spec, &ociSpec)
-+	if err != nil {
-+		return 0, err
-+	}
-+	if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
-+		return 0, nil
-+	}
-+	for _, mapping := range ociSpec.Linux.GIDMappings {
-+		if mapping.ContainerID == 0 {
-+			return mapping.HostID, nil
-+		}
-+	}
-+	return 0, nil
-+}
-diff --git a/runtime/v2/bundle_linux_test.go b/runtime/v2/bundle_linux_test.go
-new file mode 100644
-index 0000000..617b105
---- /dev/null
-+++ b/runtime/v2/bundle_linux_test.go
-@@ -0,0 +1,166 @@
-+/*
-+   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 v2
-+
-+import (
-+	"context"
-+	"encoding/json"
-+	"fmt"
-+	"io/ioutil"
-+	"os"
-+	"path/filepath"
-+	"strconv"
-+	"syscall"
-+	"testing"
-+
-+	"github.com/containerd/containerd/namespaces"
-+	"github.com/containerd/containerd/oci"
-+	"github.com/containerd/containerd/pkg/testutil"
-+	"github.com/opencontainers/runtime-spec/specs-go"
-+)
-+
-+func TestNewBundle(t *testing.T) {
-+	testutil.RequiresRoot(t)
-+	tests := []struct {
-+		userns bool
-+	}{{
-+		userns: false,
-+	}, {
-+		userns: true,
-+	}}
-+	const usernsGID = 4200
-+
-+	for i, tc := range tests {
-+		t.Run(strconv.Itoa(i), func(t *testing.T) {
-+			dir, err := ioutil.TempDir("", "test-new-bundle")
-+			if err != nil {
-+				t.Fatal("failed to create test directory", err)
-+			}
-+			defer os.RemoveAll(dir)
-+			work := filepath.Join(dir, "work")
-+			state := filepath.Join(dir, "state")
-+			id := fmt.Sprintf("new-bundle-%d", i)
-+			spec := oci.Spec{}
-+			if tc.userns {
-+				spec.Linux = &specs.Linux{
-+					GIDMappings: []specs.LinuxIDMapping{{ContainerID: 0, HostID: usernsGID}},
-+				}
-+			}
-+			specBytes, err := json.Marshal(&spec)
-+			if err != nil {
-+				t.Fatal("failed to marshal spec", err)
-+			}
-+
-+			ctx := namespaces.WithNamespace(context.TODO(), namespaces.Default)
-+			b, err := NewBundle(ctx, work, state, id, specBytes)
-+			if err != nil {
-+				t.Fatal("NewBundle should succeed", err)
-+			}
-+			if b == nil {
-+				t.Fatal("bundle should not be nil")
-+			}
-+
-+			fi, err := os.Stat(b.Path)
-+			if err != nil {
-+				t.Error("should be able to stat bundle path", err)
-+			}
-+			if tc.userns {
-+				if fi.Mode() != os.ModeDir|0710 {
-+					t.Error("bundle path should be a directory with perm 0710")
-+				}
-+			} else {
-+				if fi.Mode() != os.ModeDir|0700 {
-+					t.Error("bundle path should be a directory with perm 0700")
-+				}
-+			}
-+			stat, ok := fi.Sys().(*syscall.Stat_t)
-+			if !ok {
-+				t.Fatal("should assert to *syscall.Stat_t")
-+			}
-+			expectedGID := uint32(0)
-+			if tc.userns {
-+				expectedGID = usernsGID
-+			}
-+			if expectedGID != stat.Gid {
-+				t.Error("gid should match", expectedGID, stat.Gid)
-+			}
-+		})
-+	}
-+}
-+
-+func TestRemappedGID(t *testing.T) {
-+	tests := []struct {
-+		spec oci.Spec
-+		gid  uint32
-+	}{{
-+		// empty spec
-+		spec: oci.Spec{},
-+		gid:  0,
-+	}, {
-+		// empty Linux section
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{},
-+		},
-+		gid: 0,
-+	}, {
-+		// empty ID mappings
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{
-+				GIDMappings: make([]specs.LinuxIDMapping, 0),
-+			},
-+		},
-+		gid: 0,
-+	}, {
-+		// valid ID mapping
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{
-+				GIDMappings: []specs.LinuxIDMapping{{
-+					ContainerID: 0,
-+					HostID:      1000,
-+				}},
-+			},
-+		},
-+		gid: 1000,
-+	}, {
-+		// missing ID mapping
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{
-+				GIDMappings: []specs.LinuxIDMapping{{
-+					ContainerID: 100,
-+					HostID:      1000,
-+				}},
-+			},
-+		},
-+		gid: 0,
-+	}}
-+
-+	for i, tc := range tests {
-+		t.Run(strconv.Itoa(i), func(t *testing.T) {
-+			s, err := json.Marshal(tc.spec)
-+			if err != nil {
-+				t.Fatal("failed to marshal spec", err)
-+			}
-+			gid, err := remappedGID(s)
-+			if err != nil {
-+				t.Error("should unmarshal successfully", err)
-+			}
-+			if tc.gid != gid {
-+				t.Error("expected GID to match", tc.gid, gid)
-+			}
-+		})
-+	}
-+}
-diff --git a/runtime/v2/bundle_test.go b/runtime/v2/bundle_test.go
-new file mode 100644
-index 0000000..54e5f24
---- /dev/null
-+++ b/runtime/v2/bundle_test.go
-@@ -0,0 +1,23 @@
-+/*
-+   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 v2
-+
-+import (
-+	// When testutil is imported for one platform (bundle_linux_test.go) it
-+	// should be imported for all platforms.
-+	_ "github.com/containerd/containerd/pkg/testutil"
-+)
diff -Nru containerd-1.4.5~ds1/debian/patches/0009-CVE-2021-41103/0010-v1-runtime-reduce-permissions-for-bundle-dir.patch containerd-1.4.12~ds1/debian/patches/0009-CVE-2021-41103/0010-v1-runtime-reduce-permissions-for-bundle-dir.patch
--- containerd-1.4.5~ds1/debian/patches/0009-CVE-2021-41103/0010-v1-runtime-reduce-permissions-for-bundle-dir.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0009-CVE-2021-41103/0010-v1-runtime-reduce-permissions-for-bundle-dir.patch	1970-01-01 08:00:00.000000000 +0800
@@ -1,285 +0,0 @@
-From: Samuel Karp <skarp@amazon.com>
-Date: Tue, 21 Sep 2021 13:46:40 -0700
-Subject: v1 runtime: reduce permissions for bundle dir
-
-Bundle directory permissions should be 0700 by default.  On Linux with
-user namespaces enabled, the remapped root also needs access to the
-bundle directory.  In this case, the bundle directory is modified to
-0710 and group ownership is changed to the remapped root group.
-
-Port of the same change for the v2 runtime
-
-Signed-off-by: Samuel Karp <skarp@amazon.com>
----
- runtime/v1/linux/bundle.go      |  56 +++++++++++++-
- runtime/v1/linux/bundle_test.go | 166 ++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 221 insertions(+), 1 deletion(-)
- create mode 100644 runtime/v1/linux/bundle_test.go
-
-diff --git a/runtime/v1/linux/bundle.go b/runtime/v1/linux/bundle.go
-index 9d0a6c4..48d81e8 100644
---- a/runtime/v1/linux/bundle.go
-+++ b/runtime/v1/linux/bundle.go
-@@ -21,6 +21,7 @@ package linux
- import (
- 	"context"
- 	"crypto/sha256"
-+	"encoding/json"
- 	"fmt"
- 	"io/ioutil"
- 	"os"
-@@ -30,6 +31,7 @@ import (
- 	"github.com/containerd/containerd/runtime/linux/runctypes"
- 	"github.com/containerd/containerd/runtime/v1/shim"
- 	"github.com/containerd/containerd/runtime/v1/shim/client"
-+	"github.com/opencontainers/runtime-spec/specs-go"
- 	"github.com/pkg/errors"
- )
- 
-@@ -48,7 +50,7 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
- 		return nil, err
- 	}
- 	path = filepath.Join(path, id)
--	if err := os.Mkdir(path, 0711); err != nil {
-+	if err := os.Mkdir(path, 0700); err != nil {
- 		return nil, err
- 	}
- 	defer func() {
-@@ -56,6 +58,9 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
- 			os.RemoveAll(path)
- 		}
- 	}()
-+	if err := prepareBundleDirectoryPermissions(path, spec); err != nil {
-+		return nil, err
-+	}
- 	workDir = filepath.Join(workDir, id)
- 	if err := os.MkdirAll(workDir, 0711); err != nil {
- 		return nil, err
-@@ -77,6 +82,55 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
- 	}, err
- }
- 
-+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
-+// directory. When user namespaces are enabled, the permissions are modified
-+// to allow the remapped root GID to access the bundle.
-+func prepareBundleDirectoryPermissions(path string, spec []byte) error {
-+	gid, err := remappedGID(spec)
-+	if err != nil {
-+		return err
-+	}
-+	if gid == 0 {
-+		return nil
-+	}
-+	if err := os.Chown(path, -1, int(gid)); err != nil {
-+		return err
-+	}
-+	return os.Chmod(path, 0710)
-+}
-+
-+// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
-+// unmarshal.
-+type ociSpecUserNS struct {
-+	Linux *linuxSpecUserNS
-+}
-+
-+// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
-+// unmarshal.
-+type linuxSpecUserNS struct {
-+	GIDMappings []specs.LinuxIDMapping
-+}
-+
-+// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
-+// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
-+// remappedGID returns an error.
-+func remappedGID(spec []byte) (uint32, error) {
-+	var ociSpec ociSpecUserNS
-+	err := json.Unmarshal(spec, &ociSpec)
-+	if err != nil {
-+		return 0, err
-+	}
-+	if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
-+		return 0, nil
-+	}
-+	for _, mapping := range ociSpec.Linux.GIDMappings {
-+		if mapping.ContainerID == 0 {
-+			return mapping.HostID, nil
-+		}
-+	}
-+	return 0, nil
-+}
-+
- type bundle struct {
- 	id      string
- 	path    string
-diff --git a/runtime/v1/linux/bundle_test.go b/runtime/v1/linux/bundle_test.go
-new file mode 100644
-index 0000000..adf39b4
---- /dev/null
-+++ b/runtime/v1/linux/bundle_test.go
-@@ -0,0 +1,166 @@
-+//go:build linux
-+// +build linux
-+
-+/*
-+   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 linux
-+
-+import (
-+	"encoding/json"
-+	"fmt"
-+	"io/ioutil"
-+	"os"
-+	"path/filepath"
-+	"strconv"
-+	"syscall"
-+	"testing"
-+
-+	"github.com/containerd/containerd/oci"
-+	"github.com/containerd/continuity/testutil"
-+	"github.com/opencontainers/runtime-spec/specs-go"
-+)
-+
-+func TestNewBundle(t *testing.T) {
-+	testutil.RequiresRoot(t)
-+	tests := []struct {
-+		userns bool
-+	}{{
-+		userns: false,
-+	}, {
-+		userns: true,
-+	}}
-+	const usernsGID = 4200
-+
-+	for i, tc := range tests {
-+		t.Run(strconv.Itoa(i), func(t *testing.T) {
-+			dir, err := ioutil.TempDir("", "test-new-bundle")
-+			if err != nil {
-+				t.Fatal("failed to create test directory", err)
-+			}
-+			defer os.RemoveAll(dir)
-+			work := filepath.Join(dir, "work")
-+			state := filepath.Join(dir, "state")
-+			id := fmt.Sprintf("new-bundle-%d", i)
-+			spec := oci.Spec{}
-+			if tc.userns {
-+				spec.Linux = &specs.Linux{
-+					GIDMappings: []specs.LinuxIDMapping{{ContainerID: 0, HostID: usernsGID}},
-+				}
-+			}
-+			specBytes, err := json.Marshal(&spec)
-+			if err != nil {
-+				t.Fatal("failed to marshal spec", err)
-+			}
-+
-+			b, err := newBundle(id, work, state, specBytes)
-+			if err != nil {
-+				t.Fatal("newBundle should succeed", err)
-+			}
-+			if b == nil {
-+				t.Fatal("bundle should not be nil")
-+			}
-+
-+			fi, err := os.Stat(b.path)
-+			if err != nil {
-+				t.Error("should be able to stat bundle path", err)
-+			}
-+			if tc.userns {
-+				if fi.Mode() != os.ModeDir|0710 {
-+					t.Error("bundle path should be a directory with perm 0710")
-+				}
-+			} else {
-+				if fi.Mode() != os.ModeDir|0700 {
-+					t.Error("bundle path should be a directory with perm 0700")
-+				}
-+			}
-+			stat, ok := fi.Sys().(*syscall.Stat_t)
-+			if !ok {
-+				t.Fatal("should assert to *syscall.Stat_t")
-+			}
-+			expectedGID := uint32(0)
-+			if tc.userns {
-+				expectedGID = usernsGID
-+			}
-+			if stat.Gid != expectedGID {
-+				t.Error("gid should match", expectedGID, stat.Gid)
-+			}
-+		})
-+	}
-+}
-+
-+func TestRemappedGID(t *testing.T) {
-+	tests := []struct {
-+		spec oci.Spec
-+		gid  uint32
-+	}{{
-+		// empty spec
-+		spec: oci.Spec{},
-+		gid:  0,
-+	}, {
-+		// empty Linux section
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{},
-+		},
-+		gid: 0,
-+	}, {
-+		// empty ID mappings
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{
-+				GIDMappings: make([]specs.LinuxIDMapping, 0),
-+			},
-+		},
-+		gid: 0,
-+	}, {
-+		// valid ID mapping
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{
-+				GIDMappings: []specs.LinuxIDMapping{{
-+					ContainerID: 0,
-+					HostID:      1000,
-+				}},
-+			},
-+		},
-+		gid: 1000,
-+	}, {
-+		// missing ID mapping
-+		spec: oci.Spec{
-+			Linux: &specs.Linux{
-+				GIDMappings: []specs.LinuxIDMapping{{
-+					ContainerID: 100,
-+					HostID:      1000,
-+				}},
-+			},
-+		},
-+		gid: 0,
-+	}}
-+
-+	for i, tc := range tests {
-+		t.Run(strconv.Itoa(i), func(t *testing.T) {
-+			s, err := json.Marshal(tc.spec)
-+			if err != nil {
-+				t.Fatal("failed to marshal spec", err)
-+			}
-+			gid, err := remappedGID(s)
-+			if err != nil {
-+				t.Error("should unmarshal successfully", err)
-+			}
-+			if gid != tc.gid {
-+				t.Error("expected GID to match", tc.gid, gid)
-+			}
-+		})
-+	}
-+}
diff -Nru containerd-1.4.5~ds1/debian/patches/0009-CVE-2021-41103/0011-btrfs-reduce-permissions-on-plugin-directories.patch containerd-1.4.12~ds1/debian/patches/0009-CVE-2021-41103/0011-btrfs-reduce-permissions-on-plugin-directories.patch
--- containerd-1.4.5~ds1/debian/patches/0009-CVE-2021-41103/0011-btrfs-reduce-permissions-on-plugin-directories.patch	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/0009-CVE-2021-41103/0011-btrfs-reduce-permissions-on-plugin-directories.patch	1970-01-01 08:00:00.000000000 +0800
@@ -1,36 +0,0 @@
-From: Derek McGowan <derek@mcg.dev>
-Date: Wed, 15 Sep 2021 17:57:13 -0700
-Subject: btrfs: reduce permissions on plugin directories
-
-Disallow traversal into directories that may contain
-unpacked or mounted image filesystems.
-
-Signed-off-by: Derek McGowan <derek@mcg.dev>
-Signed-off-by: Samuel Karp <skarp@amazon.com>
-(cherry picked from commit 7c621e1fcc08bcf5a1a48b837342cc22eada1685)
----
- snapshots/btrfs/btrfs.go | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/snapshots/btrfs/btrfs.go b/snapshots/btrfs/btrfs.go
-index eef883b..778b783 100644
---- a/snapshots/btrfs/btrfs.go
-+++ b/snapshots/btrfs/btrfs.go
-@@ -63,11 +63,15 @@ type snapshotter struct {
- // root needs to be a mount point of btrfs.
- func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
- 	// If directory does not exist, create it
--	if _, err := os.Stat(root); err != nil {
-+	if st, err := os.Stat(root); err != nil {
- 		if !os.IsNotExist(err) {
- 			return nil, err
- 		}
--		if err := os.Mkdir(root, 0755); err != nil {
-+		if err := os.Mkdir(root, 0700); err != nil {
-+			return nil, err
-+		}
-+	} else if st.Mode()&os.ModePerm != 0700 {
-+		if err := os.Chmod(root, 0700); err != nil {
- 			return nil, err
- 		}
- 	}
diff -Nru containerd-1.4.5~ds1/debian/patches/series containerd-1.4.12~ds1/debian/patches/series
--- containerd-1.4.5~ds1/debian/patches/series	2021-10-05 18:45:47.000000000 +0800
+++ containerd-1.4.12~ds1/debian/patches/series	2021-11-23 18:42:16.000000000 +0800
@@ -5,7 +5,4 @@
 0005-backport-github.com-containerd-containerd-remotes.patch
 0006-backport-apparmor-handle-signal-mediation.patch
 0007-backport-runtime-ignore-file-already-closed-error.patch
-0008-CVE-2021-32760.patch
-0009-CVE-2021-41103/0009-v2-runtime-reduce-permissions-for-bundle-dir.patch
-0009-CVE-2021-41103/0010-v1-runtime-reduce-permissions-for-bundle-dir.patch
-0009-CVE-2021-41103/0011-btrfs-reduce-permissions-on-plugin-directories.patch
+0008-Add-RPi1-RPi0-workaround.patch
diff -Nru containerd-1.4.5~ds1/.github/workflows/ci.yml containerd-1.4.12~ds1/.github/workflows/ci.yml
--- containerd-1.4.5~ds1/.github/workflows/ci.yml	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.github/workflows/ci.yml	2021-11-18 03:52:12.000000000 +0800
@@ -26,7 +26,7 @@
       - name: Install Go
         uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -47,6 +47,8 @@
         working-directory: src/github.com/containerd/containerd
 
       - name: Make check
+        env:
+          GO111MODULE: off
         shell: bash
         run: make check
         working-directory: src/github.com/containerd/containerd
@@ -80,7 +82,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -126,7 +128,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -143,6 +145,8 @@
         run: GO111MODULE=on go get github.com/cpuguy83/go-md2man/v2@v2.0.0
 
       - name: Make
+        env:
+          GO111MODULE: off
         run: make man
         working-directory: src/github.com/containerd/containerd
 
@@ -162,7 +166,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -176,6 +180,8 @@
           path: src/github.com/containerd/containerd
 
       - name: Make
+        env:
+          GO111MODULE: off
         run: |
           make build
           make binaries
@@ -193,7 +199,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -217,6 +223,8 @@
           cd src/github.com/containerd/containerd
           script/setup/install-dev-tools
       - name: Binaries
+        env:
+          GO111MODULE: off
         shell: bash
         run: |
           set -o xtrace
@@ -234,11 +242,14 @@
         shell: bash
         env:
           CGO_ENABLED: 1
+          GO111MODULE: off
         run: |
           cd src/github.com/containerd/containerd
           mingw32-make.exe test root-test
 
       - name: Integration 1
+        env:
+          GO111MODULE: off
         shell: bash
         run: |
           cd src/github.com/containerd/containerd
@@ -246,6 +257,8 @@
           mingw32-make.exe integration
       # Run the integration suite a second time. See discussion in github.com/containerd/containerd/pull/175
       - name: Integration 2
+        env:
+          GO111MODULE: off
         shell: bash
         run: |
           cd src/github.com/containerd/containerd
@@ -272,7 +285,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -315,6 +328,7 @@
       - name: Install containerd
         env:
           CGO_ENABLED: 1
+          GO111MODULE: off
         run: |
           make binaries
           sudo -E PATH=$PATH make install
@@ -324,6 +338,7 @@
         env:
           GOPROXY: direct
           SKIPTESTS: github.com/containerd/containerd/snapshots/devmapper
+          GO111MODULE: off
         run: |
           make test
           sudo -E PATH=$PATH make root-test
@@ -334,6 +349,7 @@
           GOPROXY: direct
           TEST_RUNTIME: ${{ matrix.runtime }}
           RUNC_FLAVOR: ${{ matrix.runc }}
+          GO111MODULE: off
         run: |
           sudo -E PATH=$PATH make integration EXTRA_TESTFLAGS=-no-criu TESTFLAGS_RACE=-race
         working-directory: src/github.com/containerd/containerd
@@ -344,6 +360,7 @@
           GOPROXY: direct
           TEST_RUNTIME: ${{ matrix.runtime }}
           RUNC_FLAVOR: ${{ matrix.runc }}
+          GO111MODULE: off
         run: |
           sudo -E PATH=$PATH TESTFLAGS_PARALLEL=1 make integration EXTRA_TESTFLAGS=-no-criu
         working-directory: src/github.com/containerd/containerd
diff -Nru containerd-1.4.5~ds1/.github/workflows/nightly.yml containerd-1.4.12~ds1/.github/workflows/nightly.yml
--- containerd-1.4.5~ds1/.github/workflows/nightly.yml	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.github/workflows/nightly.yml	2021-11-18 03:52:12.000000000 +0800
@@ -14,7 +14,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Checkout
         uses: actions/checkout@v1
@@ -63,6 +63,7 @@
           GOPATH: ${{ runner.workspace }}
           GOOS: linux
           GOARCH: amd64
+          GO111MODULE: off
         run: |
           make binaries
           mv bin bin_amd64
@@ -74,6 +75,7 @@
           GOARCH: arm64
           CC: aarch64-linux-gnu-gcc
           CGO_ENABLED: 1
+          GO111MODULE: off
         run: |
           make binaries
           mv bin bin_arm64
@@ -85,6 +87,7 @@
           GOARCH: s390x
           CGO_ENABLED: 1
           CC: s390x-linux-gnu-gcc
+          GO111MODULE: off
         run: |
           make binaries
           mv bin bin_s390x
@@ -96,6 +99,7 @@
           GOARCH: ppc64le
           CGO_ENABLED: 1
           CC: powerpc64le-linux-gnu-gcc
+          GO111MODULE: off
         run: |
           make binaries
           mv bin bin_ppc64le
@@ -134,7 +138,7 @@
     steps:
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Checkout
         uses: actions/checkout@v1
@@ -155,6 +159,7 @@
           GOPATH: ${{ runner.workspace }}
           GOOS: windows
           GOARCH: amd64
+          GO111MODULE: off
         run: |
           make binaries
 
diff -Nru containerd-1.4.5~ds1/.github/workflows/release.yml containerd-1.4.12~ds1/.github/workflows/release.yml
--- containerd-1.4.5~ds1/.github/workflows/release.yml	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.github/workflows/release.yml	2021-11-18 03:52:12.000000000 +0800
@@ -62,7 +62,7 @@
       - name: Install Go
         uses: actions/setup-go@v2
         with:
-          go-version: '1.15.11'
+          go-version: '1.16.10'
 
       - name: Set env
         shell: bash
@@ -103,6 +103,8 @@
           path: src/github.com/Microsoft/hcsshim
 
       - name: Make
+        env:
+          GO111MODULE: off
         shell: bash
         run: |
           make build
diff -Nru containerd-1.4.5~ds1/images/image.go containerd-1.4.12~ds1/images/image.go
--- containerd-1.4.5~ds1/images/image.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/images/image.go	2021-11-18 03:52:12.000000000 +0800
@@ -19,6 +19,7 @@
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"sort"
 	"time"
 
@@ -154,6 +155,10 @@
 				return nil, err
 			}
 
+			if err := validateMediaType(p, desc.MediaType); err != nil {
+				return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+			}
+
 			var manifest ocispec.Manifest
 			if err := json.Unmarshal(p, &manifest); err != nil {
 				return nil, err
@@ -194,6 +199,10 @@
 				return nil, err
 			}
 
+			if err := validateMediaType(p, desc.MediaType); err != nil {
+				return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+			}
+
 			var idx ocispec.Index
 			if err := json.Unmarshal(p, &idx); err != nil {
 				return nil, err
@@ -336,6 +345,10 @@
 			return nil, err
 		}
 
+		if err := validateMediaType(p, desc.MediaType); err != nil {
+			return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+		}
+
 		// TODO(stevvooe): We just assume oci manifest, for now. There may be
 		// subtle differences from the docker version.
 		var manifest ocispec.Manifest
@@ -351,6 +364,10 @@
 			return nil, err
 		}
 
+		if err := validateMediaType(p, desc.MediaType); err != nil {
+			return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+		}
+
 		var index ocispec.Index
 		if err := json.Unmarshal(p, &index); err != nil {
 			return nil, err
@@ -368,6 +385,44 @@
 	return descs, nil
 }
 
+// unknownDocument represents a manifest, manifest list, or index that has not
+// yet been validated.
+type unknownDocument struct {
+	MediaType string          `json:"mediaType,omitempty"`
+	Config    json.RawMessage `json:"config,omitempty"`
+	Layers    json.RawMessage `json:"layers,omitempty"`
+	Manifests json.RawMessage `json:"manifests,omitempty"`
+	FSLayers  json.RawMessage `json:"fsLayers,omitempty"` // schema 1
+}
+
+// validateMediaType returns an error if the byte slice is invalid JSON or if
+// the media type identifies the blob as one format but it contains elements of
+// another format.
+func validateMediaType(b []byte, mt string) error {
+	var doc unknownDocument
+	if err := json.Unmarshal(b, &doc); err != nil {
+		return err
+	}
+	if len(doc.FSLayers) != 0 {
+		return fmt.Errorf("media-type: schema 1 not supported")
+	}
+	switch mt {
+	case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
+		if len(doc.Manifests) != 0 ||
+			doc.MediaType == MediaTypeDockerSchema2ManifestList ||
+			doc.MediaType == ocispec.MediaTypeImageIndex {
+			return fmt.Errorf("media-type: expected manifest but found index (%s)", mt)
+		}
+	case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
+		if len(doc.Config) != 0 || len(doc.Layers) != 0 ||
+			doc.MediaType == MediaTypeDockerSchema2Manifest ||
+			doc.MediaType == ocispec.MediaTypeImageManifest {
+			return fmt.Errorf("media-type: expected index but found manifest (%s)", mt)
+		}
+	}
+	return nil
+}
+
 // RootFS returns the unpacked diffids that make up and images rootfs.
 //
 // These are used to verify that a set of layers unpacked to the expected
diff -Nru containerd-1.4.5~ds1/images/image_test.go containerd-1.4.12~ds1/images/image_test.go
--- containerd-1.4.5~ds1/images/image_test.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/images/image_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,150 @@
+/*
+   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 images
+
+import (
+	"encoding/json"
+	"testing"
+
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+func TestValidateMediaType(t *testing.T) {
+	docTests := []struct {
+		mt    string
+		index bool
+	}{
+		{MediaTypeDockerSchema2Manifest, false},
+		{ocispec.MediaTypeImageManifest, false},
+		{MediaTypeDockerSchema2ManifestList, true},
+		{ocispec.MediaTypeImageIndex, true},
+	}
+	for _, tc := range docTests {
+		t.Run("manifest-"+tc.mt, func(t *testing.T) {
+			manifest := ocispec.Manifest{
+				Config: ocispec.Descriptor{Size: 1},
+				Layers: []ocispec.Descriptor{{Size: 2}},
+			}
+			b, err := json.Marshal(manifest)
+			if err != nil {
+				t.Fatal("failed to marshal manifest", err)
+			}
+
+			err = validateMediaType(b, tc.mt)
+			if tc.index {
+				if err == nil {
+					t.Error("manifest should not be a valid index")
+				}
+			} else {
+				if err != nil {
+					t.Error("manifest should be valid")
+				}
+			}
+		})
+		t.Run("index-"+tc.mt, func(t *testing.T) {
+			index := ocispec.Index{
+				Manifests: []ocispec.Descriptor{{Size: 1}},
+			}
+			b, err := json.Marshal(index)
+			if err != nil {
+				t.Fatal("failed to marshal index", err)
+			}
+
+			err = validateMediaType(b, tc.mt)
+			if tc.index {
+				if err != nil {
+					t.Error("index should be valid")
+				}
+			} else {
+				if err == nil {
+					t.Error("index should not be a valid manifest")
+				}
+			}
+		})
+	}
+
+	mtTests := []struct {
+		mt      string
+		valid   []string
+		invalid []string
+	}{{
+		MediaTypeDockerSchema2Manifest,
+		[]string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+		[]string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+	}, {
+		ocispec.MediaTypeImageManifest,
+		[]string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+		[]string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+	}, {
+		MediaTypeDockerSchema2ManifestList,
+		[]string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+		[]string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+	}, {
+		ocispec.MediaTypeImageIndex,
+		[]string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+		[]string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+	}}
+	for _, tc := range mtTests {
+		for _, v := range tc.valid {
+			t.Run("valid-"+tc.mt+"-"+v, func(t *testing.T) {
+				doc := struct {
+					MediaType string `json:"mediaType"`
+				}{MediaType: v}
+				b, err := json.Marshal(doc)
+				if err != nil {
+					t.Fatal("failed to marshal document", err)
+				}
+
+				err = validateMediaType(b, tc.mt)
+				if err != nil {
+					t.Error("document should be valid", err)
+				}
+			})
+		}
+		for _, iv := range tc.invalid {
+			t.Run("invalid-"+tc.mt+"-"+iv, func(t *testing.T) {
+				doc := struct {
+					MediaType string `json:"mediaType"`
+				}{MediaType: iv}
+				b, err := json.Marshal(doc)
+				if err != nil {
+					t.Fatal("failed to marshal document", err)
+				}
+
+				err = validateMediaType(b, tc.mt)
+				if err == nil {
+					t.Error("document should not be valid")
+				}
+			})
+		}
+	}
+	t.Run("schema1", func(t *testing.T) {
+		doc := struct {
+			FSLayers []string `json:"fsLayers"`
+		}{FSLayers: []string{"1"}}
+		b, err := json.Marshal(doc)
+		if err != nil {
+			t.Fatal("failed to marshal document", err)
+		}
+
+		err = validateMediaType(b, "")
+		if err == nil {
+			t.Error("document should not be valid")
+		}
+
+	})
+}
diff -Nru containerd-1.4.5~ds1/image_test.go containerd-1.4.12~ds1/image_test.go
--- containerd-1.4.5~ds1/image_test.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/image_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -34,7 +34,7 @@
 		t.Skip()
 	}
 
-	const imageName = "docker.io/library/busybox:latest"
+	const imageName = "ghcr.io/containerd/busybox:latest"
 	ctx, cancel := testContext(t)
 	defer cancel()
 
@@ -140,7 +140,7 @@
 		t.Skip()
 	}
 
-	imageName := "docker.io/library/busybox:latest"
+	imageName := "ghcr.io/containerd/busybox:latest"
 	ctx, cancel := testContext(t)
 	defer cancel()
 
diff -Nru containerd-1.4.5~ds1/lease_test.go containerd-1.4.12~ds1/lease_test.go
--- containerd-1.4.5~ds1/lease_test.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/lease_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -54,7 +54,7 @@
 	defer ls.Delete(ctx, l, leases.SynchronousDelete)
 
 	// step 1: download image
-	imageName := "docker.io/library/busybox:1.25"
+	imageName := "ghcr.io/containerd/busybox:1.28"
 
 	image, err := client.Pull(ctx, imageName, WithPullUnpack, WithPullSnapshotter("native"))
 	if err != nil {
diff -Nru containerd-1.4.5~ds1/.mailmap containerd-1.4.12~ds1/.mailmap
--- containerd-1.4.5~ds1/.mailmap	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.mailmap	2021-11-18 03:52:12.000000000 +0800
@@ -17,6 +17,7 @@
 Cao Zhihao <caozhihao@163.com>
 Cao Zhihao <caozhihao@163.com> <caozhihao.xd@bytedance.com>
 Carlos Eduardo <me@carlosedp.com> <me@carlosedp.com>
+Cory Bennett <cbennett@netflix.com>
 Cristian Staretu <cristian.staretu@gmail.com>
 Cristian Staretu <cristian.staretu@gmail.com> <unclejack@users.noreply.github.com>
 Daniel Dao <dqminh89@gmail.com>
@@ -73,6 +74,7 @@
 Nishchay Kumar <mrawesomenix@gmail.com>
 Oliver Stenbom <oliver@stenbom.eu> <ostenbom@pivotal.io>
 Phil Estes <estesp@gmail.com> <estesp@linux.vnet.ibm.com>
+Phil Estes <estesp@gmail.com> <estesp@amazon.com>
 Reid Li <reid.li@utexas.edu>
 Ross Boucher <rboucher@gmail.com>
 Ruediger Maass <ruediger.maass@de.ibm.com>
@@ -90,6 +92,7 @@
 Su Fei  <fesu@ebay.com> <fesu@ebay.com>
 Ted Yu <yuzhihong@gmail.com>
 Tõnis Tiigi <tonistiigi@gmail.com>
+Wei Fu <fuweid89@gmail.com>
 Wei Fu <fuweid89@gmail.com> <fhfuwei@163.com>
 Xiaodong Zhang <a4012017@sina.com>
 Xuean Yan <yan.xuean@zte.com.cn>
diff -Nru containerd-1.4.5~ds1/metadata/containers.go containerd-1.4.12~ds1/metadata/containers.go
--- containerd-1.4.5~ds1/metadata/containers.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/metadata/containers.go	2021-11-18 03:52:12.000000000 +0800
@@ -290,7 +290,7 @@
 
 	// image has no validation
 	for k, v := range container.Labels {
-		if err := labels.Validate(k, v); err == nil {
+		if err := labels.Validate(k, v); err != nil {
 			return errors.Wrapf(err, "containers.Labels")
 		}
 	}
diff -Nru containerd-1.4.5~ds1/metadata/content.go containerd-1.4.12~ds1/metadata/content.go
--- containerd-1.4.5~ds1/metadata/content.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/metadata/content.go	2021-11-18 03:52:12.000000000 +0800
@@ -551,13 +551,13 @@
 	if desc.Size > 0 {
 		ra, err := nw.provider.ReaderAt(ctx, nw.desc)
 		if err != nil {
+			w.Close()
 			return err
 		}
 		defer ra.Close()
 
 		if err := content.CopyReaderAt(w, ra, desc.Size); err != nil {
-			nw.w.Close()
-			nw.w = nil
+			w.Close()
 			return err
 		}
 	}
@@ -708,7 +708,7 @@
 
 func validateInfo(info *content.Info) error {
 	for k, v := range info.Labels {
-		if err := labels.Validate(k, v); err == nil {
+		if err := labels.Validate(k, v); err != nil {
 			return errors.Wrapf(err, "info.Labels")
 		}
 	}
diff -Nru containerd-1.4.5~ds1/releases/v1.4.10.toml containerd-1.4.12~ds1/releases/v1.4.10.toml
--- containerd-1.4.5~ds1/releases/v1.4.10.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.10.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,24 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.9"
+
+pre_release = false
+
+preface = """\
+The tenth patch release for containerd 1.4 contains minor fixes and updates
+including an updated runc and hcsshim.
+
+### Notable Updates
+
+* **Update runc to v1.0.2** [#5899](https://github.com/containerd/containerd/pull/5899)
+* **Update hcsshim to v0.8.21** [#5957](https://github.com/containerd/containerd/pull/5957)
+* **Support "clone3" in default seccomp profile** [#5982](https://github.com/containerd/containerd/pull/5982)
+* **Fix panic in metadata content writer on copy error** [#6043](https://github.com/containerd/containerd/pull/6043)
+
+See the changelog for complete list of changes"""
diff -Nru containerd-1.4.5~ds1/releases/v1.4.11.toml containerd-1.4.12~ds1/releases/v1.4.11.toml
--- containerd-1.4.5~ds1/releases/v1.4.11.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.11.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,20 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.10"
+
+pre_release = false
+
+preface = """\
+The eleventh patch release for containerd 1.4 is a security release to fix CVE-2021-41103.
+
+### Notable Updates
+
+* **Fix insufficiently restricted permissions on container root and plugin directories** [GHSA-c2h3-6mxw-7mvq](https://github.com/containerd/containerd/security/advisories/GHSA-c2h3-6mxw-7mvq)
+
+See the changelog for complete list of changes"""
diff -Nru containerd-1.4.5~ds1/releases/v1.4.12.toml containerd-1.4.12~ds1/releases/v1.4.12.toml
--- containerd-1.4.5~ds1/releases/v1.4.12.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.12.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,23 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.11"
+
+pre_release = false
+
+preface = """\
+The twelfth patch release for containerd 1.4 contains a few minor bug fixes
+and an update to mitigate [CVE-2021-41190](https://github.com/opencontainers/distribution-spec/security/advisories/GHSA-mc8v-mgrf-8f4m).
+
+### Notable Updates
+
+* **Handle ambiguous OCI manifest parsing** ([GHSA-5j5w-g665-5m35](https://github.com/containerd/containerd/security/advisories/GHSA-5j5w-g665-5m35))
+* **Update pull to try next mirror for non-404 errors** ([#5275](https://github.com/containerd/containerd/pull/5275))
+* **Update pull to handle of non-https urls in descriptors** ([#6221](https://github.com/containerd/containerd/pull/6221))
+
+See the changelog for complete list of changes"""
diff -Nru containerd-1.4.5~ds1/releases/v1.4.6.toml containerd-1.4.12~ds1/releases/v1.4.6.toml
--- containerd-1.4.5~ds1/releases/v1.4.6.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.6.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,15 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.5"
+
+pre_release = false
+
+preface = """\
+The sixth patch release for containerd 1.4 is a security release to update
+runc for [CVE-2021-30465](https://github.com/opencontainers/runc/security/advisories/GHSA-c3xm-pvg7-gh7r)"""
diff -Nru containerd-1.4.5~ds1/releases/v1.4.7.toml containerd-1.4.12~ds1/releases/v1.4.7.toml
--- containerd-1.4.5~ds1/releases/v1.4.7.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.7.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,23 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.6"
+
+pre_release = false
+
+preface = """\
+The seventh patch release for containerd 1.4 updates runc to 1.0.0 and contains
+various other fixes.
+
+### Notable Updates
+* **Update runc binary to 1.0.0** [5552](https://github.com/containerd/containerd/pull/5552)
+* **Fix invalid validation error checking** [#5565](https://github.com/containerd/containerd/pull/5565)
+* **Fix error on image pull resume** [#5560](https://github.com/containerd/containerd/pull/5560)
+* **Fix symlink resolution for disk mounts on Windows** [#5411](https://github.com/containerd/containerd/pull/5411)
+
+See the changelog for complete list of changes"""
diff -Nru containerd-1.4.5~ds1/releases/v1.4.8.toml containerd-1.4.12~ds1/releases/v1.4.8.toml
--- containerd-1.4.5~ds1/releases/v1.4.8.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.8.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,14 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.7"
+
+pre_release = false
+
+preface = """\
+The eighth patch release for containerd 1.4 is a security release to address [CVE-2021-32760](https://github.com/containerd/containerd/security/advisories/GHSA-c72p-9xmj-rx3w)."""
diff -Nru containerd-1.4.5~ds1/releases/v1.4.9.toml containerd-1.4.12~ds1/releases/v1.4.9.toml
--- containerd-1.4.5~ds1/releases/v1.4.9.toml	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/releases/v1.4.9.toml	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,23 @@
+# commit to be tagged for new release
+commit = "HEAD"
+
+project_name = "containerd"
+github_repo = "containerd/containerd"
+match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$"
+
+# previous release
+previous = "v1.4.8"
+
+pre_release = false
+
+preface = """\
+The ninth patch release for containerd 1.4 updates runc to 1.0.1 and contains
+other minor updates.
+
+### Notable Updates
+
+* **Update runc binary to 1.0.1** [#5751](https://github.com/containerd/containerd/pull/5751)
+* **Update pull authorization logic on redirect** [#5504](https://github.com/containerd/containerd/pull/5504)
+* **Fix user agent used for fetching registry authentication tokens** [#5761](https://github.com/containerd/containerd/pull/5761)
+
+See the changelog for complete list of changes"""
diff -Nru containerd-1.4.5~ds1/remotes/docker/authorizer.go containerd-1.4.12~ds1/remotes/docker/authorizer.go
--- containerd-1.4.5~ds1/remotes/docker/authorizer.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/authorizer.go	2021-11-18 03:52:12.000000000 +0800
@@ -31,6 +31,7 @@
 
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/log"
+	"github.com/containerd/containerd/version"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/net/context/ctxhttp"
@@ -356,6 +357,9 @@
 			req.Header[k] = append(req.Header[k], v...)
 		}
 	}
+	if len(req.Header.Get("User-Agent")) == 0 {
+		req.Header.Set("User-Agent", "containerd/"+version.Version)
+	}
 
 	resp, err := ctxhttp.Do(ctx, ah.client, req)
 	if err != nil {
@@ -408,6 +412,9 @@
 			req.Header[k] = append(req.Header[k], v...)
 		}
 	}
+	if len(req.Header.Get("User-Agent")) == 0 {
+		req.Header.Set("User-Agent", "containerd/"+version.Version)
+	}
 
 	reqParams := req.URL.Query()
 
diff -Nru containerd-1.4.5~ds1/remotes/docker/fetcher.go containerd-1.4.12~ds1/remotes/docker/fetcher.go
--- containerd-1.4.5~ds1/remotes/docker/fetcher.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/fetcher.go	2021-11-18 03:52:12.000000000 +0800
@@ -60,6 +60,10 @@
 				log.G(ctx).WithError(err).Debug("failed to parse")
 				continue
 			}
+			if u.Scheme != "http" && u.Scheme != "https" {
+				log.G(ctx).Debug("non-http(s) alternative url is unsupported")
+				continue
+			}
 			log.G(ctx).Debug("trying alternative url")
 
 			// Try this first, parse it
@@ -148,7 +152,7 @@
 	})
 }
 
-func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64) (io.ReadCloser, error) {
+func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64) (_ io.ReadCloser, retErr error) {
 	req.header.Set("Accept", strings.Join([]string{mediatype, `*/*`}, ", "))
 
 	if offset > 0 {
@@ -162,13 +166,17 @@
 	if err != nil {
 		return nil, err
 	}
+	defer func() {
+		if retErr != nil {
+			resp.Body.Close()
+		}
+	}()
 
 	if resp.StatusCode > 299 {
 		// TODO(stevvooe): When doing a offset specific request, we should
 		// really distinguish between a 206 and a 200. In the case of 200, we
 		// can discard the bytes, hiding the seek behavior from the
 		// implementation.
-		defer resp.Body.Close()
 
 		if resp.StatusCode == http.StatusNotFound {
 			return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", req.String())
diff -Nru containerd-1.4.5~ds1/remotes/docker/pusher.go containerd-1.4.12~ds1/remotes/docker/pusher.go
--- containerd-1.4.5~ds1/remotes/docker/pusher.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/pusher.go	2021-11-18 03:52:12.000000000 +0800
@@ -109,12 +109,15 @@
 						// TODO: Set updated time?
 					},
 				})
+				resp.Body.Close()
 				return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
 			}
 		} else if resp.StatusCode != http.StatusNotFound {
+			resp.Body.Close()
 			// TODO: log error
 			return nil, errors.Errorf("unexpected response: %s", resp.Status)
 		}
+		resp.Body.Close()
 	}
 
 	if isManifest {
@@ -155,6 +158,7 @@
 				return nil, err
 			}
 		}
+		defer resp.Body.Close()
 
 		switch resp.StatusCode {
 		case http.StatusOK, http.StatusAccepted, http.StatusNoContent:
@@ -338,6 +342,7 @@
 	if resp == nil {
 		return errors.New("no response")
 	}
+	defer resp.Body.Close()
 
 	// 201 is specified return status, some registries return
 	// 200, 202 or 204.
diff -Nru containerd-1.4.5~ds1/remotes/docker/resolver.go containerd-1.4.12~ds1/remotes/docker/resolver.go
--- containerd-1.4.5~ds1/remotes/docker/resolver.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/resolver.go	2021-11-18 03:52:12.000000000 +0800
@@ -238,10 +238,10 @@
 	}
 
 	var (
-		lastErr error
-		paths   [][]string
-		dgst    = refspec.Digest()
-		caps    = HostCapabilityPull
+		firstErr error
+		paths    [][]string
+		dgst     = refspec.Digest()
+		caps     = HostCapabilityPull
 	)
 
 	if dgst != "" {
@@ -292,8 +292,8 @@
 					err = errors.Wrapf(err, "pull access denied, repository does not exist or may require authorization")
 				}
 				// Store the error for referencing later
-				if lastErr == nil {
-					lastErr = err
+				if firstErr == nil {
+					firstErr = err
 				}
 				log.G(ctx).WithError(err).Info("trying next host")
 				continue // try another host
@@ -305,7 +305,14 @@
 					log.G(ctx).Info("trying next host - response was http.StatusNotFound")
 					continue
 				}
-				return "", ocispec.Descriptor{}, errors.Errorf("unexpected status code %v: %v", u, resp.Status)
+				if resp.StatusCode > 399 {
+					// Set firstErr when encountering the first non-404 status code.
+					if firstErr == nil {
+						firstErr = errors.Errorf("pulling from host %s failed with status code %v: %v", host.Host, u, resp.Status)
+					}
+					continue // try another host
+				}
+				return "", ocispec.Descriptor{}, errors.Errorf("pulling from host %s failed with unexpected status code %v: %v", host.Host, u, resp.Status)
 			}
 			size := resp.ContentLength
 			contentType := getManifestMediaType(resp)
@@ -368,8 +375,8 @@
 			}
 			// Prevent resolving to excessively large manifests
 			if size > MaxManifestSize {
-				if lastErr == nil {
-					lastErr = errors.Wrapf(errdefs.ErrNotFound, "rejecting %d byte manifest for %s", size, ref)
+				if firstErr == nil {
+					firstErr = errors.Wrapf(errdefs.ErrNotFound, "rejecting %d byte manifest for %s", size, ref)
 				}
 				continue
 			}
@@ -385,11 +392,15 @@
 		}
 	}
 
-	if lastErr == nil {
-		lastErr = errors.Wrap(errdefs.ErrNotFound, ref)
+	// If above loop terminates without return, then there was an error.
+	// "firstErr" contains the first non-404 error. That is, "firstErr == nil"
+	// means that either no registries were given or each registry returned 404.
+
+	if firstErr == nil {
+		firstErr = errors.Wrap(errdefs.ErrNotFound, ref)
 	}
 
-	return "", ocispec.Descriptor{}, lastErr
+	return "", ocispec.Descriptor{}, firstErr
 }
 
 func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
@@ -548,7 +559,21 @@
 	if err := r.authorize(ctx, req); err != nil {
 		return nil, errors.Wrap(err, "failed to authorize")
 	}
-	resp, err := ctxhttp.Do(ctx, r.host.Client, req)
+
+	var client = &http.Client{}
+	if r.host.Client != nil {
+		*client = *r.host.Client
+	}
+	if client.CheckRedirect == nil {
+		client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+			if len(via) >= 10 {
+				return errors.New("stopped after 10 redirects")
+			}
+			return errors.Wrap(r.authorize(ctx, req), "failed to authorize redirect")
+		}
+	}
+
+	resp, err := ctxhttp.Do(ctx, client, req)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to do request")
 	}
diff -Nru containerd-1.4.5~ds1/remotes/docker/schema1/converter.go containerd-1.4.12~ds1/remotes/docker/schema1/converter.go
--- containerd-1.4.5~ds1/remotes/docker/schema1/converter.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/remotes/docker/schema1/converter.go	2021-11-18 03:52:12.000000000 +0800
@@ -256,6 +256,9 @@
 	if err := json.Unmarshal(b, &m); err != nil {
 		return err
 	}
+	if len(m.Manifests) != 0 || len(m.Layers) != 0 {
+		return errors.New("converter: expected schema1 document but found extra keys")
+	}
 	c.pulledManifest = &m
 
 	return nil
@@ -472,8 +475,10 @@
 }
 
 type manifest struct {
-	FSLayers []fsLayer `json:"fsLayers"`
-	History  []history `json:"history"`
+	FSLayers  []fsLayer       `json:"fsLayers"`
+	History   []history       `json:"history"`
+	Layers    json.RawMessage `json:"layers,omitempty"`    // OCI manifest
+	Manifests json.RawMessage `json:"manifests,omitempty"` // OCI index
 }
 
 type v1History struct {
diff -Nru containerd-1.4.5~ds1/runtime/v1/linux/bundle.go containerd-1.4.12~ds1/runtime/v1/linux/bundle.go
--- containerd-1.4.5~ds1/runtime/v1/linux/bundle.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v1/linux/bundle.go	2021-11-18 03:52:12.000000000 +0800
@@ -21,6 +21,7 @@
 import (
 	"context"
 	"crypto/sha256"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -30,6 +31,7 @@
 	"github.com/containerd/containerd/runtime/linux/runctypes"
 	"github.com/containerd/containerd/runtime/v1/shim"
 	"github.com/containerd/containerd/runtime/v1/shim/client"
+	"github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 )
 
@@ -48,7 +50,7 @@
 		return nil, err
 	}
 	path = filepath.Join(path, id)
-	if err := os.Mkdir(path, 0711); err != nil {
+	if err := os.Mkdir(path, 0700); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -56,6 +58,9 @@
 			os.RemoveAll(path)
 		}
 	}()
+	if err := prepareBundleDirectoryPermissions(path, spec); err != nil {
+		return nil, err
+	}
 	workDir = filepath.Join(workDir, id)
 	if err := os.MkdirAll(workDir, 0711); err != nil {
 		return nil, err
@@ -77,6 +82,55 @@
 	}, err
 }
 
+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
+// directory. When user namespaces are enabled, the permissions are modified
+// to allow the remapped root GID to access the bundle.
+func prepareBundleDirectoryPermissions(path string, spec []byte) error {
+	gid, err := remappedGID(spec)
+	if err != nil {
+		return err
+	}
+	if gid == 0 {
+		return nil
+	}
+	if err := os.Chown(path, -1, int(gid)); err != nil {
+		return err
+	}
+	return os.Chmod(path, 0710)
+}
+
+// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
+// unmarshal.
+type ociSpecUserNS struct {
+	Linux *linuxSpecUserNS
+}
+
+// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
+// unmarshal.
+type linuxSpecUserNS struct {
+	GIDMappings []specs.LinuxIDMapping
+}
+
+// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
+// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
+// remappedGID returns an error.
+func remappedGID(spec []byte) (uint32, error) {
+	var ociSpec ociSpecUserNS
+	err := json.Unmarshal(spec, &ociSpec)
+	if err != nil {
+		return 0, err
+	}
+	if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
+		return 0, nil
+	}
+	for _, mapping := range ociSpec.Linux.GIDMappings {
+		if mapping.ContainerID == 0 {
+			return mapping.HostID, nil
+		}
+	}
+	return 0, nil
+}
+
 type bundle struct {
 	id      string
 	path    string
diff -Nru containerd-1.4.5~ds1/runtime/v1/linux/bundle_test.go containerd-1.4.12~ds1/runtime/v1/linux/bundle_test.go
--- containerd-1.4.5~ds1/runtime/v1/linux/bundle_test.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v1/linux/bundle_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,166 @@
+//go:build linux
+// +build linux
+
+/*
+   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 linux
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"syscall"
+	"testing"
+
+	"github.com/containerd/containerd/oci"
+	"github.com/containerd/continuity/testutil"
+	"github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func TestNewBundle(t *testing.T) {
+	testutil.RequiresRoot(t)
+	tests := []struct {
+		userns bool
+	}{{
+		userns: false,
+	}, {
+		userns: true,
+	}}
+	const usernsGID = 4200
+
+	for i, tc := range tests {
+		t.Run(strconv.Itoa(i), func(t *testing.T) {
+			dir, err := ioutil.TempDir("", "test-new-bundle")
+			if err != nil {
+				t.Fatal("failed to create test directory", err)
+			}
+			defer os.RemoveAll(dir)
+			work := filepath.Join(dir, "work")
+			state := filepath.Join(dir, "state")
+			id := fmt.Sprintf("new-bundle-%d", i)
+			spec := oci.Spec{}
+			if tc.userns {
+				spec.Linux = &specs.Linux{
+					GIDMappings: []specs.LinuxIDMapping{{ContainerID: 0, HostID: usernsGID}},
+				}
+			}
+			specBytes, err := json.Marshal(&spec)
+			if err != nil {
+				t.Fatal("failed to marshal spec", err)
+			}
+
+			b, err := newBundle(id, work, state, specBytes)
+			if err != nil {
+				t.Fatal("newBundle should succeed", err)
+			}
+			if b == nil {
+				t.Fatal("bundle should not be nil")
+			}
+
+			fi, err := os.Stat(b.path)
+			if err != nil {
+				t.Error("should be able to stat bundle path", err)
+			}
+			if tc.userns {
+				if fi.Mode() != os.ModeDir|0710 {
+					t.Error("bundle path should be a directory with perm 0710")
+				}
+			} else {
+				if fi.Mode() != os.ModeDir|0700 {
+					t.Error("bundle path should be a directory with perm 0700")
+				}
+			}
+			stat, ok := fi.Sys().(*syscall.Stat_t)
+			if !ok {
+				t.Fatal("should assert to *syscall.Stat_t")
+			}
+			expectedGID := uint32(0)
+			if tc.userns {
+				expectedGID = usernsGID
+			}
+			if stat.Gid != expectedGID {
+				t.Error("gid should match", expectedGID, stat.Gid)
+			}
+		})
+	}
+}
+
+func TestRemappedGID(t *testing.T) {
+	tests := []struct {
+		spec oci.Spec
+		gid  uint32
+	}{{
+		// empty spec
+		spec: oci.Spec{},
+		gid:  0,
+	}, {
+		// empty Linux section
+		spec: oci.Spec{
+			Linux: &specs.Linux{},
+		},
+		gid: 0,
+	}, {
+		// empty ID mappings
+		spec: oci.Spec{
+			Linux: &specs.Linux{
+				GIDMappings: make([]specs.LinuxIDMapping, 0),
+			},
+		},
+		gid: 0,
+	}, {
+		// valid ID mapping
+		spec: oci.Spec{
+			Linux: &specs.Linux{
+				GIDMappings: []specs.LinuxIDMapping{{
+					ContainerID: 0,
+					HostID:      1000,
+				}},
+			},
+		},
+		gid: 1000,
+	}, {
+		// missing ID mapping
+		spec: oci.Spec{
+			Linux: &specs.Linux{
+				GIDMappings: []specs.LinuxIDMapping{{
+					ContainerID: 100,
+					HostID:      1000,
+				}},
+			},
+		},
+		gid: 0,
+	}}
+
+	for i, tc := range tests {
+		t.Run(strconv.Itoa(i), func(t *testing.T) {
+			s, err := json.Marshal(tc.spec)
+			if err != nil {
+				t.Fatal("failed to marshal spec", err)
+			}
+			gid, err := remappedGID(s)
+			if err != nil {
+				t.Error("should unmarshal successfully", err)
+			}
+			if gid != tc.gid {
+				t.Error("expected GID to match", tc.gid, gid)
+			}
+		})
+	}
+}
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle_default.go containerd-1.4.12~ds1/runtime/v2/bundle_default.go
--- containerd-1.4.5~ds1/runtime/v2/bundle_default.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle_default.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,24 @@
+//go:build !linux
+// +build !linux
+
+/*
+   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 v2
+
+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
+// directory according to the needs of the current platform.
+func prepareBundleDirectoryPermissions(path string, spec []byte) error { return nil }
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle.go containerd-1.4.12~ds1/runtime/v2/bundle.go
--- containerd-1.4.5~ds1/runtime/v2/bundle.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle.go	2021-11-18 03:52:12.000000000 +0800
@@ -72,7 +72,10 @@
 	if err := os.MkdirAll(filepath.Dir(b.Path), 0711); err != nil {
 		return nil, err
 	}
-	if err := os.Mkdir(b.Path, 0711); err != nil {
+	if err := os.Mkdir(b.Path, 0700); err != nil {
+		return nil, err
+	}
+	if err := prepareBundleDirectoryPermissions(b.Path, spec); err != nil {
 		return nil, err
 	}
 	paths = append(paths, b.Path)
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle_linux.go containerd-1.4.12~ds1/runtime/v2/bundle_linux.go
--- containerd-1.4.5~ds1/runtime/v2/bundle_linux.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle_linux.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,74 @@
+/*
+   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 v2
+
+import (
+	"encoding/json"
+	"os"
+
+	"github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// prepareBundleDirectoryPermissions prepares the permissions of the bundle
+// directory according to the needs of the current platform.
+// On Linux when user namespaces are enabled, the permissions are modified to
+// allow the remapped root GID to access the bundle.
+func prepareBundleDirectoryPermissions(path string, spec []byte) error {
+	gid, err := remappedGID(spec)
+	if err != nil {
+		return err
+	}
+	if gid == 0 {
+		return nil
+	}
+	if err := os.Chown(path, -1, int(gid)); err != nil {
+		return err
+	}
+	return os.Chmod(path, 0710)
+}
+
+// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
+// unmarshal.
+type ociSpecUserNS struct {
+	Linux *linuxSpecUserNS
+}
+
+// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
+// unmarshal.
+type linuxSpecUserNS struct {
+	GIDMappings []specs.LinuxIDMapping
+}
+
+// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
+// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
+// remappedGID returns an error.
+func remappedGID(spec []byte) (uint32, error) {
+	var ociSpec ociSpecUserNS
+	err := json.Unmarshal(spec, &ociSpec)
+	if err != nil {
+		return 0, err
+	}
+	if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
+		return 0, nil
+	}
+	for _, mapping := range ociSpec.Linux.GIDMappings {
+		if mapping.ContainerID == 0 {
+			return mapping.HostID, nil
+		}
+	}
+	return 0, nil
+}
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle_linux_test.go containerd-1.4.12~ds1/runtime/v2/bundle_linux_test.go
--- containerd-1.4.5~ds1/runtime/v2/bundle_linux_test.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle_linux_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,166 @@
+/*
+   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 v2
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"syscall"
+	"testing"
+
+	"github.com/containerd/containerd/namespaces"
+	"github.com/containerd/containerd/oci"
+	"github.com/containerd/containerd/pkg/testutil"
+	"github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func TestNewBundle(t *testing.T) {
+	testutil.RequiresRoot(t)
+	tests := []struct {
+		userns bool
+	}{{
+		userns: false,
+	}, {
+		userns: true,
+	}}
+	const usernsGID = 4200
+
+	for i, tc := range tests {
+		t.Run(strconv.Itoa(i), func(t *testing.T) {
+			dir, err := ioutil.TempDir("", "test-new-bundle")
+			if err != nil {
+				t.Fatal("failed to create test directory", err)
+			}
+			defer os.RemoveAll(dir)
+			work := filepath.Join(dir, "work")
+			state := filepath.Join(dir, "state")
+			id := fmt.Sprintf("new-bundle-%d", i)
+			spec := oci.Spec{}
+			if tc.userns {
+				spec.Linux = &specs.Linux{
+					GIDMappings: []specs.LinuxIDMapping{{ContainerID: 0, HostID: usernsGID}},
+				}
+			}
+			specBytes, err := json.Marshal(&spec)
+			if err != nil {
+				t.Fatal("failed to marshal spec", err)
+			}
+
+			ctx := namespaces.WithNamespace(context.TODO(), namespaces.Default)
+			b, err := NewBundle(ctx, work, state, id, specBytes)
+			if err != nil {
+				t.Fatal("NewBundle should succeed", err)
+			}
+			if b == nil {
+				t.Fatal("bundle should not be nil")
+			}
+
+			fi, err := os.Stat(b.Path)
+			if err != nil {
+				t.Error("should be able to stat bundle path", err)
+			}
+			if tc.userns {
+				if fi.Mode() != os.ModeDir|0710 {
+					t.Error("bundle path should be a directory with perm 0710")
+				}
+			} else {
+				if fi.Mode() != os.ModeDir|0700 {
+					t.Error("bundle path should be a directory with perm 0700")
+				}
+			}
+			stat, ok := fi.Sys().(*syscall.Stat_t)
+			if !ok {
+				t.Fatal("should assert to *syscall.Stat_t")
+			}
+			expectedGID := uint32(0)
+			if tc.userns {
+				expectedGID = usernsGID
+			}
+			if expectedGID != stat.Gid {
+				t.Error("gid should match", expectedGID, stat.Gid)
+			}
+		})
+	}
+}
+
+func TestRemappedGID(t *testing.T) {
+	tests := []struct {
+		spec oci.Spec
+		gid  uint32
+	}{{
+		// empty spec
+		spec: oci.Spec{},
+		gid:  0,
+	}, {
+		// empty Linux section
+		spec: oci.Spec{
+			Linux: &specs.Linux{},
+		},
+		gid: 0,
+	}, {
+		// empty ID mappings
+		spec: oci.Spec{
+			Linux: &specs.Linux{
+				GIDMappings: make([]specs.LinuxIDMapping, 0),
+			},
+		},
+		gid: 0,
+	}, {
+		// valid ID mapping
+		spec: oci.Spec{
+			Linux: &specs.Linux{
+				GIDMappings: []specs.LinuxIDMapping{{
+					ContainerID: 0,
+					HostID:      1000,
+				}},
+			},
+		},
+		gid: 1000,
+	}, {
+		// missing ID mapping
+		spec: oci.Spec{
+			Linux: &specs.Linux{
+				GIDMappings: []specs.LinuxIDMapping{{
+					ContainerID: 100,
+					HostID:      1000,
+				}},
+			},
+		},
+		gid: 0,
+	}}
+
+	for i, tc := range tests {
+		t.Run(strconv.Itoa(i), func(t *testing.T) {
+			s, err := json.Marshal(tc.spec)
+			if err != nil {
+				t.Fatal("failed to marshal spec", err)
+			}
+			gid, err := remappedGID(s)
+			if err != nil {
+				t.Error("should unmarshal successfully", err)
+			}
+			if tc.gid != gid {
+				t.Error("expected GID to match", tc.gid, gid)
+			}
+		})
+	}
+}
diff -Nru containerd-1.4.5~ds1/runtime/v2/bundle_test.go containerd-1.4.12~ds1/runtime/v2/bundle_test.go
--- containerd-1.4.5~ds1/runtime/v2/bundle_test.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/runtime/v2/bundle_test.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,23 @@
+/*
+   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 v2
+
+import (
+	// When testutil is imported for one platform (bundle_linux_test.go) it
+	// should be imported for all platforms.
+	_ "github.com/containerd/containerd/pkg/testutil"
+)
diff -Nru containerd-1.4.5~ds1/script/setup/runc-version containerd-1.4.12~ds1/script/setup/runc-version
--- containerd-1.4.5~ds1/script/setup/runc-version	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/script/setup/runc-version	2021-11-18 03:52:12.000000000 +0800
@@ -1 +1 @@
-v1.0.0-rc94
+v1.0.2
diff -Nru containerd-1.4.5~ds1/snapshots/btrfs/btrfs.go containerd-1.4.12~ds1/snapshots/btrfs/btrfs.go
--- containerd-1.4.5~ds1/snapshots/btrfs/btrfs.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/snapshots/btrfs/btrfs.go	2021-11-18 03:52:12.000000000 +0800
@@ -63,11 +63,15 @@
 // root needs to be a mount point of btrfs.
 func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
 	// If directory does not exist, create it
-	if _, err := os.Stat(root); err != nil {
+	if st, err := os.Stat(root); err != nil {
 		if !os.IsNotExist(err) {
 			return nil, err
 		}
-		if err := os.Mkdir(root, 0755); err != nil {
+		if err := os.Mkdir(root, 0700); err != nil {
+			return nil, err
+		}
+	} else if st.Mode()&os.ModePerm != 0700 {
+		if err := os.Chmod(root, 0700); err != nil {
 			return nil, err
 		}
 	}
diff -Nru containerd-1.4.5~ds1/snapshots/devmapper/snapshotter.go containerd-1.4.12~ds1/snapshots/devmapper/snapshotter.go
--- containerd-1.4.5~ds1/snapshots/devmapper/snapshotter.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/snapshots/devmapper/snapshotter.go	2021-11-18 03:52:12.000000000 +0800
@@ -232,7 +232,7 @@
 
 // View creates readonly thin device for the given snapshot key
 func (s *Snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
-	log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("prepare")
+	log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("view")
 
 	var (
 		mounts []mount.Mount
@@ -511,6 +511,8 @@
 }
 
 func (s *Snapshotter) Cleanup(ctx context.Context) error {
+	log.G(ctx).Debug("cleanup")
+
 	var removedDevices []*DeviceInfo
 
 	if !s.config.AsyncRemove {
diff -Nru containerd-1.4.5~ds1/.travis.yml containerd-1.4.12~ds1/.travis.yml
--- containerd-1.4.5~ds1/.travis.yml	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.travis.yml	2021-11-18 03:52:12.000000000 +0800
@@ -15,7 +15,7 @@
 - linux
 
 go:
-  - "1.15.11"
+  - "1.16.10"
 
 env:
   - TRAVIS_GOOS=linux TEST_RUNTIME=io.containerd.runc.v1 TRAVIS_CGO_ENABLED=1 TRAVIS_DISTRO=bionic GOPROXY=direct
diff -Nru containerd-1.4.5~ds1/Vagrantfile containerd-1.4.12~ds1/Vagrantfile
--- containerd-1.4.5~ds1/Vagrantfile	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/Vagrantfile	2021-11-18 03:52:12.000000000 +0800
@@ -77,7 +77,7 @@
   config.vm.provision "install-golang", type: "shell", run: "once" do |sh|
     sh.upload_path = "/tmp/vagrant-install-golang"
     sh.env = {
-        'GO_VERSION': ENV['GO_VERSION'] || "1.15.11",
+        'GO_VERSION': ENV['GO_VERSION'] || "1.16.10",
     }
     sh.inline = <<~SHELL
         #!/usr/bin/env bash
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os.go	2021-11-18 03:52:12.000000000 +0800
@@ -20,7 +20,6 @@
 	"io"
 	"io/ioutil"
 	"os"
-	"path/filepath"
 
 	"github.com/docker/docker/pkg/symlink"
 )
@@ -56,18 +55,6 @@
 	return os.Stat(name)
 }
 
-// ResolveSymbolicLink will follow any symbolic links
-func (RealOS) ResolveSymbolicLink(path string) (string, error) {
-	info, err := os.Lstat(path)
-	if err != nil {
-		return "", err
-	}
-	if info.Mode()&os.ModeSymlink != os.ModeSymlink {
-		return path, nil
-	}
-	return filepath.EvalSymlinks(path)
-}
-
 // FollowSymlinkInScope will call symlink.FollowSymlinkInScope.
 func (RealOS) FollowSymlinkInScope(path, scope string) (string, error) {
 	return symlink.FollowSymlinkInScope(path, scope)
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os_unix.go	2021-11-18 03:52:12.000000000 +0800
@@ -19,6 +19,9 @@
 package os
 
 import (
+	"os"
+	"path/filepath"
+
 	"github.com/containerd/containerd/mount"
 	"golang.org/x/sys/unix"
 )
@@ -57,3 +60,15 @@
 
 	return err
 }
+
+// ResolveSymbolicLink will follow any symbolic links
+func (RealOS) ResolveSymbolicLink(path string) (string, error) {
+	info, err := os.Lstat(path)
+	if err != nil {
+		return "", err
+	}
+	if info.Mode()&os.ModeSymlink != os.ModeSymlink {
+		return path, nil
+	}
+	return filepath.EvalSymlinks(path)
+}
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os_windows.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os_windows.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/os/os_windows.go	1970-01-01 08:00:00.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/os/os_windows.go	2021-11-18 03:52:12.000000000 +0800
@@ -0,0 +1,182 @@
+// +build windows
+
+/*
+   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 os
+
+import (
+	"os"
+	"strings"
+	"sync"
+	"unicode/utf16"
+
+	"golang.org/x/sys/windows"
+)
+
+// openPath takes a path, opens it, and returns the resulting handle.
+// It works for both file and directory paths.
+//
+// We are not able to use builtin Go functionality for opening a directory path:
+// - os.Open on a directory returns a os.File where Fd() is a search handle from FindFirstFile.
+// - syscall.Open does not provide a way to specify FILE_FLAG_BACKUP_SEMANTICS, which is needed to
+//   open a directory.
+// We could use os.Open if the path is a file, but it's easier to just use the same code for both.
+// Therefore, we call windows.CreateFile directly.
+func openPath(path string) (windows.Handle, error) {
+	u16, err := windows.UTF16PtrFromString(path)
+	if err != nil {
+		return 0, err
+	}
+	h, err := windows.CreateFile(
+		u16,
+		0,
+		windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
+		nil,
+		windows.OPEN_EXISTING,
+		windows.FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory handle.
+		0)
+	if err != nil {
+		return 0, &os.PathError{
+			Op:   "CreateFile",
+			Path: path,
+			Err:  err,
+		}
+	}
+	return h, nil
+}
+
+// GetFinalPathNameByHandle flags.
+//nolint:golint
+const (
+	cFILE_NAME_OPENED = 0x8
+
+	cVOLUME_NAME_DOS  = 0x0
+	cVOLUME_NAME_GUID = 0x1
+)
+
+var pool = sync.Pool{
+	New: func() interface{} {
+		// Size of buffer chosen somewhat arbitrarily to accommodate a large number of path strings.
+		// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
+		b := make([]uint16, 310)
+		return &b
+	},
+}
+
+// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
+// with the given handle and flags. It transparently takes care of creating a buffer of the
+// correct size for the call.
+func getFinalPathNameByHandle(h windows.Handle, flags uint32) (string, error) {
+	b := *(pool.Get().(*[]uint16))
+	defer func() { pool.Put(&b) }()
+	for {
+		n, err := windows.GetFinalPathNameByHandle(h, &b[0], uint32(len(b)), flags)
+		if err != nil {
+			return "", err
+		}
+		// If the buffer wasn't large enough, n will be the total size needed (including null terminator).
+		// Resize and try again.
+		if n > uint32(len(b)) {
+			b = make([]uint16, n)
+			continue
+		}
+		// If the buffer is large enough, n will be the size not including the null terminator.
+		// Convert to a Go string and return.
+		return string(utf16.Decode(b[:n])), nil
+	}
+}
+
+// resolvePath implements path resolution for Windows. It attempts to return the "real" path to the
+// file or directory represented by the given path.
+// The resolution works by using the Windows API GetFinalPathNameByHandle, which takes a handle and
+// returns the final path to that file.
+func resolvePath(path string) (string, error) {
+	h, err := openPath(path)
+	if err != nil {
+		return "", err
+	}
+	defer windows.CloseHandle(h)
+
+	// We use the Windows API GetFinalPathNameByHandle to handle path resolution. GetFinalPathNameByHandle
+	// returns a resolved path name for a file or directory. The returned path can be in several different
+	// formats, based on the flags passed. There are several goals behind the design here:
+	// - Do as little manual path manipulation as possible. Since Windows path formatting can be quite
+	//   complex, we try to just let the Windows APIs handle that for us.
+	// - Retain as much compatibility with existing Go path functions as we can. In particular, we try to
+	//   ensure paths returned from resolvePath can be passed to EvalSymlinks.
+	//
+	// First, we query for the VOLUME_NAME_GUID path of the file. This will return a path in the form
+	// "\\?\Volume{8a25748f-cf34-4ac6-9ee2-c89400e886db}\dir\file.txt". If the path is a UNC share
+	// (e.g. "\\server\share\dir\file.txt"), then the VOLUME_NAME_GUID query will fail with ERROR_PATH_NOT_FOUND.
+	// In this case, we will next try a VOLUME_NAME_DOS query. This query will return a path for a UNC share
+	// in the form "\\?\UNC\server\share\dir\file.txt". This path will work with most functions, but EvalSymlinks
+	// fails on it. Therefore, we rewrite the path to the form "\\server\share\dir\file.txt" before returning it.
+	// This path rewrite may not be valid in all cases (see the notes in the next paragraph), but those should
+	// be very rare edge cases, and this case wouldn't have worked with EvalSymlinks anyways.
+	//
+	// The "\\?\" prefix indicates that no path parsing or normalization should be performed by Windows.
+	// Instead the path is passed directly to the object manager. The lack of parsing means that "." and ".." are
+	// interpreted literally and "\"" must be used as a path separator. Additionally, because normalization is
+	// not done, certain paths can only be represented in this format. For instance, "\\?\C:\foo." (with a trailing .)
+	// cannot be written as "C:\foo.", because path normalization will remove the trailing ".".
+	//
+	// We use FILE_NAME_OPENED instead of FILE_NAME_NORMALIZED, as FILE_NAME_NORMALIZED can fail on some
+	// UNC paths based on access restrictions. The additional normalization done is also quite minimal in
+	// most cases.
+	//
+	// Querying for VOLUME_NAME_DOS first instead of VOLUME_NAME_GUID would yield a "nicer looking" path in some cases.
+	// For instance, it could return "\\?\C:\dir\file.txt" instead of "\\?\Volume{8a25748f-cf34-4ac6-9ee2-c89400e886db}\dir\file.txt".
+	// However, we query for VOLUME_NAME_GUID first for two reasons:
+	// - The volume GUID path is more stable. A volume's mount point can change when it is remounted, but its
+	//   volume GUID should not change.
+	// - If the volume is mounted at a non-drive letter path (e.g. mounted to "C:\mnt"), then VOLUME_NAME_DOS
+	//   will return the mount path. EvalSymlinks fails on a path like this due to a bug.
+	//
+	// References:
+	// - GetFinalPathNameByHandle: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea
+	// - Naming Files, Paths, and Namespaces: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+	// - Naming a Volume: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-volume
+
+	rPath, err := getFinalPathNameByHandle(h, cFILE_NAME_OPENED|cVOLUME_NAME_GUID)
+	if err == windows.ERROR_PATH_NOT_FOUND {
+		// ERROR_PATH_NOT_FOUND is returned from the VOLUME_NAME_GUID query if the path is a
+		// network share (UNC path). In this case, query for the DOS name instead, then translate
+		// the returned path to make it more palatable to other path functions.
+		rPath, err = getFinalPathNameByHandle(h, cFILE_NAME_OPENED|cVOLUME_NAME_DOS)
+		if err != nil {
+			return "", err
+		}
+		if strings.HasPrefix(rPath, `\\?\UNC\`) {
+			// Convert \\?\UNC\server\share -> \\server\share. The \\?\UNC syntax does not work with
+			// some Go filepath functions such as EvalSymlinks. In the future if other components
+			// move away from EvalSymlinks and use GetFinalPathNameByHandle instead, we could remove
+			// this path munging.
+			rPath = `\\` + rPath[len(`\\?\UNC\`):]
+		}
+	} else if err != nil {
+		return "", err
+	}
+	return rPath, nil
+}
+
+// ResolveSymbolicLink will follow any symbolic links
+func (RealOS) ResolveSymbolicLink(path string) (string, error) {
+	// filepath.EvalSymlinks does not work very well on Windows, so instead we resolve the path
+	// via resolvePath which uses GetFinalPathNameByHandle. This returns either a path prefixed with `\\?\`,
+	// or a remote share path in the form \\server\share. These should work with most Go and Windows APIs.
+	return resolvePath(path)
+}
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/pkg/server/sandbox_run.go	2021-11-18 03:52:12.000000000 +0800
@@ -124,8 +124,10 @@
 		sandbox.NetNSPath = sandbox.NetNS.GetPath()
 		defer func() {
 			if retErr != nil {
+				deferCtx, deferCancel := ctrdutil.DeferContext()
+				defer deferCancel()
 				// Teardown network if an error is returned.
-				if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
+				if err := c.teardownPodNetwork(deferCtx, sandbox); err != nil {
 					log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id)
 				}
 
diff -Nru containerd-1.4.5~ds1/vendor/github.com/containerd/cri/vendor.conf containerd-1.4.12~ds1/vendor/github.com/containerd/cri/vendor.conf
--- containerd-1.4.5~ds1/vendor/github.com/containerd/cri/vendor.conf	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor/github.com/containerd/cri/vendor.conf	2021-11-18 03:52:12.000000000 +0800
@@ -10,8 +10,8 @@
 github.com/cespare/xxhash/v2                        v2.1.1
 github.com/containerd/cgroups                       318312a373405e5e91134d8063d04d59768a1bff
 github.com/containerd/console                       v1.0.0
-github.com/containerd/containerd                    v1.4.1
-github.com/containerd/continuity                    efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
+github.com/containerd/containerd                    8263eb3eaee447b16856eeb8839d5df4c9cca71a
+github.com/containerd/continuity                    1d9893e5674b5260c3fc11316d0d5fc0d12ea9e2
 github.com/containerd/fifo                          f15a3290365b9d2627d189e619ab4008e0069caf
 github.com/containerd/go-runc                       7016d3ce2328dd2cb1192b2076ebd565c4e8df0c
 github.com/containerd/ttrpc                         v1.0.1
@@ -33,11 +33,11 @@
 github.com/imdario/mergo                            v0.3.7
 github.com/konsorten/go-windows-terminal-sequences  v1.0.3
 github.com/matttproud/golang_protobuf_extensions    v1.0.1
-github.com/Microsoft/go-winio                       v0.4.14
-github.com/Microsoft/hcsshim                        v0.8.9
+github.com/Microsoft/go-winio                       v0.4.19
+github.com/Microsoft/hcsshim                        v0.8.16
 github.com/opencontainers/go-digest                 v1.0.0
 github.com/opencontainers/image-spec                v1.0.1
-github.com/opencontainers/runc                      v1.0.0-rc92
+github.com/opencontainers/runc                      v1.0.0-rc94
 github.com/opencontainers/runtime-spec              4d89ac9fbff6c455f46a5bb59c6b1bb7184a5e43 # v1.0.3-0.20200728170252-4d89ac9fbff6
 github.com/pkg/errors                               v0.9.1
 github.com/prometheus/client_golang                 v1.6.0
@@ -51,9 +51,9 @@
 github.com/urfave/cli                               v1.22.1 # NOTE: urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092
 go.etcd.io/bbolt                                    v1.3.5
 go.opencensus.io                                    v0.22.0
-golang.org/x/net                                    ab34263943818b32f575efc978a3d24e80b04bd7
-golang.org/x/sync                                   42b317875d0fa942474b76e1b46a6060d720ae6e
-golang.org/x/sys                                    ed371f2e16b4b305ee99df548828de367527b76b
+golang.org/x/net                                    69a78807bb2bb6d1599c68698c6b009505012083
+golang.org/x/sync                                   67f06af15bc961c363a7260195bcd53487529a21
+golang.org/x/sys                                    d19ff857e887eacb631721f188c7d365c2331456
 golang.org/x/text                                   v0.3.3
 google.golang.org/genproto                          e50cd9704f63023d62cd06a1994b98227fc4d21a
 google.golang.org/grpc                              v1.27.1
@@ -77,15 +77,15 @@
 golang.org/x/time                                   555d28b269f0569763d25dbe1a237ae74c6bcc82
 gopkg.in/inf.v0                                     v0.9.1
 gopkg.in/yaml.v2                                    v2.2.8
-k8s.io/api                                          v0.19.4
-k8s.io/apiserver                                    v0.19.4
-k8s.io/apimachinery                                 v0.19.4
-k8s.io/client-go                                    v0.19.4
-k8s.io/component-base                               v0.19.4
-k8s.io/cri-api                                      v0.19.4
+k8s.io/api                                          v0.19.10
+k8s.io/apiserver                                    v0.19.10
+k8s.io/apimachinery                                 v0.19.10
+k8s.io/client-go                                    v0.19.10
+k8s.io/component-base                               v0.19.10
+k8s.io/cri-api                                      v0.19.10
 k8s.io/klog/v2                                      v2.2.0
 k8s.io/utils                                        d5654de09c73da55eb19ae4ab4f734f7a61747a6
-sigs.k8s.io/structured-merge-diff/v4                v4.0.1
+sigs.k8s.io/structured-merge-diff/v4                v4.0.3
 sigs.k8s.io/yaml                                    v1.2.0
 
 # cni dependencies
diff -Nru containerd-1.4.5~ds1/vendor.conf containerd-1.4.12~ds1/vendor.conf
--- containerd-1.4.5~ds1/vendor.conf	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/vendor.conf	2021-11-18 03:52:12.000000000 +0800
@@ -27,11 +27,11 @@
 github.com/imdario/mergo                            v0.3.7
 github.com/konsorten/go-windows-terminal-sequences  v1.0.3
 github.com/matttproud/golang_protobuf_extensions    v1.0.1
-github.com/Microsoft/go-winio                       v0.4.14
-github.com/Microsoft/hcsshim                        v0.8.10
+github.com/Microsoft/go-winio                       v0.4.19
+github.com/Microsoft/hcsshim                        v0.8.21
 github.com/opencontainers/go-digest                 v1.0.0
 github.com/opencontainers/image-spec                v1.0.1
-github.com/opencontainers/runc                      v1.0.0-rc92
+github.com/opencontainers/runc                      v1.0.0-rc94
 github.com/opencontainers/runtime-spec              4d89ac9fbff6c455f46a5bb59c6b1bb7184a5e43 # v1.0.3-0.20200728170252-4d89ac9fbff6
 github.com/pkg/errors                               v0.9.1
 github.com/prometheus/client_golang                 v1.6.0
@@ -47,7 +47,7 @@
 go.opencensus.io                                    v0.22.0
 golang.org/x/net                                    69a78807bb2bb6d1599c68698c6b009505012083
 golang.org/x/sync                                   67f06af15bc961c363a7260195bcd53487529a21
-golang.org/x/sys                                    5cba982894dd4e8879e3ef0a0c308ceff39f6154
+golang.org/x/sys                                    d19ff857e887eacb631721f188c7d365c2331456
 golang.org/x/text                                   v0.3.3
 google.golang.org/genproto                          e50cd9704f63023d62cd06a1994b98227fc4d21a
 google.golang.org/grpc                              v1.27.1
@@ -57,7 +57,7 @@
 github.com/cilium/ebpf                              1c8d4c9ef7759622653a1d319284a44652333b28
 
 # cri dependencies
-github.com/containerd/cri                           1360416eca4fef15c763444914e60fe1eaedbc3d # release/1.4
+github.com/containerd/cri                           3b02bec1603179debe2cde54509b2bfc45fc27d3 # release/1.4
 github.com/davecgh/go-spew                          v1.1.1
 github.com/docker/docker                            4634ce647cf2ce2c6031129ccd109e557244986f
 github.com/docker/spdystream                        449fdfce4d962303d702fec724ef0ad181c92528
diff -Nru containerd-1.4.5~ds1/version/version.go containerd-1.4.12~ds1/version/version.go
--- containerd-1.4.5~ds1/version/version.go	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/version/version.go	2021-11-18 03:52:12.000000000 +0800
@@ -23,7 +23,7 @@
 	Package = "github.com/containerd/containerd"
 
 	// Version holds the complete version number. Filled in at linking time.
-	Version = "1.4.5+unknown"
+	Version = "1.4.12+unknown"
 
 	// Revision is filled with the VCS (e.g. git) revision being used to build
 	// the program at linking time.
diff -Nru containerd-1.4.5~ds1/.zuul/playbooks/containerd-build/run.yaml containerd-1.4.12~ds1/.zuul/playbooks/containerd-build/run.yaml
--- containerd-1.4.5~ds1/.zuul/playbooks/containerd-build/run.yaml	2021-05-12 12:30:30.000000000 +0800
+++ containerd-1.4.12~ds1/.zuul/playbooks/containerd-build/run.yaml	2021-11-18 03:52:12.000000000 +0800
@@ -2,7 +2,7 @@
   become: yes
   roles:
   - role: config-golang
-    go_version: '1.15.11'
+    go_version: '1.16.10'
     arch: arm64
   tasks:
   - name: Build containerd

Reply to: