VYPR
High severityNVD Advisory· Published May 20, 2025· Updated May 20, 2025

Containerd vulnerable to host filesystem access during image unpack

CVE-2025-47290

Description

containerd is a container runtime. A time-of-check to time-of-use (TOCTOU) vulnerability was found in containerd v2.1.0. While unpacking an image during an image pull, specially crafted container images could arbitrarily modify the host file system. The only affected version of containerd is 2.1.0. Other versions of containerd are not affected. This bug has been fixed in containerd 2.1.1. Users should update to this version to resolve the issue. As a workaround, ensure that only trusted images are used and that only trusted users have permissions to import images.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/containerd/containerd/v2Go
>= 2.1.0, < 2.1.12.1.1

Affected products

1

Patches

1
cada13298fba

Merge commit from fork

https://github.com/containerd/containerdDerek McGowanMay 20, 2025via ghsa
1 file changed · +10 35
  • pkg/archive/tar.go+10 35 modified
    @@ -157,31 +157,6 @@ func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int
     	return options.applyFunc(ctx, root, r, options)
     }
     
    -// cachedRootPath will memoize root paths, avoiding redundant checks.
    -type cachedRootPath struct {
    -	root  string
    -	cache map[string]string
    -}
    -
    -func newCachedRootPath(root string) *cachedRootPath {
    -	return &cachedRootPath{
    -		root:  root,
    -		cache: make(map[string]string),
    -	}
    -}
    -
    -func (c *cachedRootPath) get(path string) (string, error) {
    -	if hit, ok := c.cache[path]; ok {
    -		return hit, nil
    -	}
    -	p, err := fs.RootPath(c.root, path)
    -	if err != nil {
    -		return "", err
    -	}
    -	c.cache[path] = p
    -	return p, nil
    -}
    -
     // applyNaive applies a tar stream of an OCI style diff tar to a directory
     // applying each file as either a whole file or whiteout.
     // See https://github.com/opencontainers/image-spec/blob/main/layer.md#applying-changesets
    @@ -239,7 +214,6 @@ func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOpti
     	}
     
     	// Iterate through the files in the archive.
    -	rootPath := newCachedRootPath(root)
     	for {
     		select {
     		case <-ctx.Done():
    @@ -276,7 +250,7 @@ func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOpti
     
     		// Split name and resolve symlinks for root directory.
     		ppath, base := filepath.Split(hdr.Name)
    -		ppath, err = rootPath.get(ppath)
    +		ppath, err = fs.RootPath(root, ppath)
     		if err != nil {
     			return 0, fmt.Errorf("failed to get root path: %w", err)
     		}
    @@ -328,7 +302,7 @@ func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOpti
     		srcData := io.Reader(tr)
     		srcHdr := hdr
     
    -		if err := rootPath.createTarFile(ctx, path, srcHdr, srcData, options.NoSameOwner); err != nil {
    +		if err := createTarFile(ctx, path, root, srcHdr, srcData, options.NoSameOwner); err != nil {
     			return 0, err
     		}
     
    @@ -341,7 +315,7 @@ func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOpti
     	}
     
     	for _, hdr := range dirs {
    -		path, err := rootPath.get(hdr.Name)
    +		path, err := fs.RootPath(root, hdr.Name)
     		if err != nil {
     			return 0, err
     		}
    @@ -353,7 +327,7 @@ func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOpti
     	return size, nil
     }
     
    -func (c *cachedRootPath) createTarFile(ctx context.Context, path string, hdr *tar.Header, reader io.Reader, noSameOwner bool) error {
    +func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header, reader io.Reader, noSameOwner bool) error {
     	// hdr.Mode is in linux format, which we can use for syscalls,
     	// but for os.Foo() calls we need the mode converted to os.FileMode,
     	// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
    @@ -397,7 +371,7 @@ func (c *cachedRootPath) createTarFile(ctx context.Context, path string, hdr *ta
     		}
     
     	case tar.TypeLink:
    -		targetPath, err := c.hardlinkRootPath(hdr.Linkname)
    +		targetPath, err := hardlinkRootPath(extractDir, hdr.Linkname)
     		if err != nil {
     			return err
     		}
    @@ -792,6 +766,7 @@ func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written in
     		}
     	}
     	return written, err
    +
     }
     
     // hardlinkRootPath returns target linkname, evaluating and bounding any
    @@ -804,16 +779,16 @@ func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written in
     //	ln /tmp/xxx /tmp/yyy
     //
     // /tmp/yyy should be softlink which be same of /tmp/xxx, not /tmp/zzz.
    -func (c *cachedRootPath) hardlinkRootPath(linkname string) (string, error) {
    +func hardlinkRootPath(root, linkname string) (string, error) {
     	ppath, base := filepath.Split(linkname)
    -	ppath, err := c.get(ppath)
    +	ppath, err := fs.RootPath(root, ppath)
     	if err != nil {
     		return "", err
     	}
     
     	targetPath := filepath.Join(ppath, base)
    -	if !strings.HasPrefix(targetPath, c.root) {
    -		targetPath = c.root
    +	if !strings.HasPrefix(targetPath, root) {
    +		targetPath = root
     	}
     	return targetPath, nil
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.