Security fix - containerd to 1.7.27

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (OpenFaaS Ltd)
2025-03-18 10:25:34 +00:00
parent 087a299f4c
commit a2254ca1ff
271 changed files with 10022 additions and 4233 deletions

View File

@ -22,7 +22,7 @@ import (
"os"
"path/filepath"
"github.com/sirupsen/logrus"
"github.com/containerd/log"
)
// XAttrErrorHandler transform a non-nil xattr error.
@ -103,11 +103,6 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
}
}
entries, err := os.ReadDir(src)
if err != nil {
return fmt.Errorf("failed to read %s: %w", src, err)
}
if err := copyFileInfo(stat, src, dst); err != nil {
return fmt.Errorf("failed to copy file info for %s: %w", dst, err)
}
@ -116,7 +111,15 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
return fmt.Errorf("failed to copy xattrs: %w", err)
}
for _, entry := range entries {
f, err := os.Open(src)
if err != nil {
return err
}
defer f.Close()
dr := &dirReader{f: f}
handleEntry := func(entry os.DirEntry) error {
source := filepath.Join(src, entry.Name())
target := filepath.Join(dst, entry.Name())
@ -130,7 +133,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
if err := copyDirectory(target, source, inodes, o); err != nil {
return err
}
continue
return nil
case (fileInfo.Mode() & os.ModeType) == 0:
link, err := getLinkSource(target, fileInfo, inodes)
if err != nil {
@ -158,8 +161,8 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
return fmt.Errorf("failed to create irregular file: %w", err)
}
default:
logrus.Warnf("unsupported mode: %s: %s", source, fileInfo.Mode())
continue
log.L.Warnf("unsupported mode: %s: %s", source, fileInfo.Mode())
return nil
}
if err := copyFileInfo(fileInfo, source, target); err != nil {
@ -169,9 +172,20 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
if err := copyXAttrs(target, source, o.xex, o.xeh); err != nil {
return fmt.Errorf("failed to copy xattrs: %w", err)
}
return nil
}
return nil
for {
entry := dr.Next()
if entry == nil {
break
}
if err := handleEntry(entry); err != nil {
return err
}
}
return dr.Err()
}
// CopyFile copies the source file to the target.

View File

@ -1,5 +1,4 @@
//go:build !windows && !freebsd
// +build !windows,!freebsd
/*
Copyright The containerd Authors.

View File

@ -17,6 +17,7 @@
package fs
import (
"errors"
"fmt"
"os"
"syscall"
@ -64,6 +65,9 @@ func copyFileInfo(fi os.FileInfo, src, name string) error {
func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
if errors.Is(err, unix.ENOTSUP) {
return nil
}
e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
if errorHandler != nil {
e = errorHandler(dst, src, "", e)

View File

@ -1,5 +1,4 @@
//go:build !darwin
// +build !darwin
/*
Copyright The containerd Authors.

View File

@ -1,5 +1,4 @@
//go:build darwin || freebsd || openbsd || netbsd || dragonfly || solaris
// +build darwin freebsd openbsd netbsd dragonfly solaris
/*
Copyright The containerd Authors.
@ -20,12 +19,14 @@
package fs
import (
"errors"
"fmt"
"os"
"runtime"
"syscall"
"github.com/containerd/continuity/sysx"
"golang.org/x/sys/unix"
)
func copyFileInfo(fi os.FileInfo, src, name string) error {
@ -67,6 +68,9 @@ func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAtt
// On darwin, character devices do not permit listing xattrs
return nil
}
if errors.Is(err, unix.ENOTSUP) {
return nil
}
e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
if errorHandler != nil {
e = errorHandler(dst, src, "", e)

View File

@ -18,11 +18,12 @@ package fs
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"github.com/sirupsen/logrus"
"github.com/containerd/log"
"golang.org/x/sync/errgroup"
)
@ -100,14 +101,11 @@ type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
// is to account for timestamp truncation during archiving.
func Changes(ctx context.Context, a, b string, changeFn ChangeFunc) error {
if a == "" {
logrus.Debugf("Using single walk diff for %s", b)
log.G(ctx).Debugf("Using single walk diff for %s", b)
return addDirChanges(ctx, changeFn, b)
} else if diffOptions := detectDirDiff(b, a); diffOptions != nil {
logrus.Debugf("Using single walk diff for %s from %s", diffOptions.diffDir, a)
return diffDirChanges(ctx, changeFn, a, diffOptions)
}
logrus.Debugf("Using double walk diff for %s from %s", b, a)
log.G(ctx).Debugf("Using double walk diff for %s from %s", b, a)
return doubleWalkDiff(ctx, changeFn, a, b)
}
@ -134,24 +132,53 @@ func addDirChanges(ctx context.Context, changeFn ChangeFunc, root string) error
})
}
// DiffChangeSource is the source of diff directory.
type DiffSource int
const (
// DiffSourceOverlayFS indicates that a diff directory is from
// OverlayFS.
DiffSourceOverlayFS DiffSource = iota
)
// diffDirOptions is used when the diff can be directly calculated from
// a diff directory to its base, without walking both trees.
type diffDirOptions struct {
diffDir string
skipChange func(string) (bool, error)
deleteChange func(string, string, os.FileInfo) (string, error)
skipChange func(string, os.FileInfo) (bool, error)
deleteChange func(string, string, os.FileInfo, ChangeFunc) (bool, error)
}
// diffDirChanges walks the diff directory and compares changes against the base.
func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *diffDirOptions) error {
// DiffDirChanges walks the diff directory and compares changes against the base.
//
// NOTE: If all the children of a dir are removed, or that dir are recreated
// after remove, we will mark non-existing `.wh..opq` file as deleted. It's
// unlikely to create explicit whiteout files for all the children and all
// descendants. And based on OCI spec, it's not possible to create a file or
// dir with a name beginning with `.wh.`. So, after `.wh..opq` file has been
// deleted, the ChangeFunc, the receiver will add whiteout prefix to create a
// opaque whiteout `.wh..wh..opq`.
//
// REF: https://github.com/opencontainers/image-spec/blob/v1.0/layer.md#whiteouts
func DiffDirChanges(ctx context.Context, baseDir, diffDir string, source DiffSource, changeFn ChangeFunc) error {
var o *diffDirOptions
switch source {
case DiffSourceOverlayFS:
o = &diffDirOptions{
deleteChange: overlayFSWhiteoutConvert,
}
default:
return errors.New("unknown diff change source")
}
changedDirs := make(map[string]struct{})
return filepath.Walk(o.diffDir, func(path string, f os.FileInfo, err error) error {
return filepath.Walk(diffDir, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
// Rebase path
path, err = filepath.Rel(o.diffDir, path)
path, err = filepath.Rel(diffDir, path)
if err != nil {
return err
}
@ -163,38 +190,45 @@ func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *di
return nil
}
// TODO: handle opaqueness, start new double walker at this
// location to get deletes, and skip tree in single walker
if o.skipChange != nil {
if skip, err := o.skipChange(path); skip {
if skip, err := o.skipChange(path, f); skip {
return err
}
}
var kind ChangeKind
deletedFile, err := o.deleteChange(o.diffDir, path, f)
if err != nil {
return err
deletedFile := false
if o.deleteChange != nil {
deletedFile, err = o.deleteChange(diffDir, path, f, changeFn)
if err != nil {
return err
}
_, err = os.Stat(filepath.Join(baseDir, path))
if err != nil {
if !os.IsNotExist(err) {
return err
}
deletedFile = false
}
}
// Find out what kind of modification happened
if deletedFile != "" {
path = deletedFile
if deletedFile {
kind = ChangeKindDelete
f = nil
} else {
// Otherwise, the file was added
kind = ChangeKindAdd
// ...Unless it already existed in a base, in which case, it's a modification
stat, err := os.Stat(filepath.Join(base, path))
// ...Unless it already existed in a baseDir, in which case, it's a modification
stat, err := os.Stat(filepath.Join(baseDir, path))
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil {
// The file existed in the base, so that's a modification
// The file existed in the baseDir, so that's a modification
// However, if it's a directory, maybe it wasn't actually modified.
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
@ -215,10 +249,12 @@ func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *di
if f.IsDir() {
changedDirs[path] = struct{}{}
}
if kind == ChangeKindAdd || kind == ChangeKindDelete {
parent := filepath.Dir(path)
if _, ok := changedDirs[parent]; !ok && parent != "/" {
pi, err := os.Stat(filepath.Join(o.diffDir, parent))
pi, err := os.Stat(filepath.Join(diffDir, parent))
if err := changeFn(ChangeKindModify, parent, pi, err); err != nil {
return err
}
@ -226,6 +262,9 @@ func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *di
}
}
if kind == ChangeKindDelete {
f = nil
}
return changeFn(kind, path, f, nil)
})
}

View File

@ -0,0 +1,101 @@
/*
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 fs
import (
"errors"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/containerd/continuity/devices"
"github.com/containerd/continuity/sysx"
"golang.org/x/sys/unix"
)
const (
// whiteoutPrefix prefix means file is a whiteout. If this is followed
// by a filename this means that file has been removed from the base
// layer.
//
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#whiteouts
whiteoutPrefix = ".wh."
)
// overlayFSWhiteoutConvert detects whiteouts and opaque directories.
//
// It returns deleted indicator if the file is a character device with 0/0
// device number. And call changeFn with ChangeKindDelete for opaque
// directories.
//
// Check: https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt
func overlayFSWhiteoutConvert(diffDir, path string, f os.FileInfo, changeFn ChangeFunc) (deleted bool, _ error) {
if f.Mode()&os.ModeCharDevice != 0 {
if _, ok := f.Sys().(*syscall.Stat_t); !ok {
return false, nil
}
maj, min, err := devices.DeviceInfo(f)
if err != nil {
return false, err
}
return (maj == 0 && min == 0), nil
}
if f.IsDir() {
originalPath := filepath.Join(diffDir, path)
opaque, err := getOpaqueValue(originalPath)
if err != nil {
if errors.Is(err, unix.ENODATA) {
return false, nil
}
return false, err
}
if len(opaque) == 1 && opaque[0] == 'y' {
opaqueDirPath := filepath.Join(path, whiteoutPrefix+".opq")
return false, changeFn(ChangeKindDelete, opaqueDirPath, nil, nil)
}
}
return false, nil
}
// getOpaqueValue returns opaque value for a given file.
func getOpaqueValue(filePath string) ([]byte, error) {
for _, xattr := range []string{
"trusted.overlay.opaque",
// TODO(fuweid):
//
// user.overlay.* is available since 5.11. We should check
// kernel version before read.
//
// REF: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
"user.overlay.opaque",
} {
opaque, err := sysx.LGetxattr(filePath, xattr)
if err != nil {
if errors.Is(err, unix.ENODATA) || errors.Is(err, unix.ENOTSUP) {
continue
}
return nil, fmt.Errorf("failed to retrieve %s attr: %w", xattr, err)
}
return opaque, nil
}
return nil, unix.ENODATA
}

View File

@ -0,0 +1,28 @@
//go: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 fs
import (
"errors"
"os"
)
func overlayFSWhiteoutConvert(string, string, os.FileInfo, ChangeFunc) (bool, error) {
return false, errors.New("unsupported")
}

View File

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
/*
Copyright The containerd Authors.
@ -28,16 +27,6 @@ import (
"github.com/containerd/continuity/sysx"
)
// detectDirDiff returns diff dir options if a directory could
// be found in the mount info for upper which is the direct
// diff with the provided lower directory
func detectDirDiff(upper, lower string) *diffDirOptions {
// TODO: get mount options for upper
// TODO: detect AUFS
// TODO: detect overlay
return nil
}
// compareSysStat returns whether the stats are equivalent,
// whether the files are considered the same file, and
// an error

View File

@ -22,10 +22,6 @@ import (
"golang.org/x/sys/windows"
)
func detectDirDiff(upper, lower string) *diffDirOptions {
return nil
}
func compareSysStat(s1, s2 interface{}) (bool, error) {
f1, ok := s1.(windows.Win32FileAttributeData)
if !ok {

53
vendor/github.com/containerd/continuity/fs/dir.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
/*
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 fs
import (
"io"
"os"
)
type dirReader struct {
buf []os.DirEntry
f *os.File
err error
}
func (r *dirReader) Next() os.DirEntry {
if len(r.buf) == 0 {
infos, err := r.f.ReadDir(32)
if err != nil {
if err != io.EOF {
r.err = err
}
return nil
}
r.buf = infos
}
if len(r.buf) == 0 {
return nil
}
out := r.buf[0]
r.buf[0] = nil
r.buf = r.buf[1:]
return out
}
func (r *dirReader) Err() error {
return r.err
}

View File

@ -1,5 +1,4 @@
//go:build linux
// +build linux
/*
Copyright The containerd Authors.

View File

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
/*
Copyright The containerd Authors.

View File

@ -1,5 +1,4 @@
//go:build windows
// +build windows
/*
Copyright The containerd Authors.

View File

@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
/*
Copyright The containerd Authors.

View File

@ -0,0 +1,123 @@
/*
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.
*/
/*
Copyright 2013-2018 Docker, Inc.
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
https://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.
*/
// Original source: https://github.com/moby/moby/blob/v26.0.0/daemon/graphdriver/driver_linux.go
package fs
import (
"path/filepath"
"syscall"
)
// Magic unsigned id of the filesystem in use.
type Magic uint32
const (
// MagicUnsupported is a predefined constant value other than a valid filesystem id.
MagicUnsupported = Magic(0x00000000)
)
const (
// MagicAufs filesystem id for Aufs
MagicAufs = Magic(0x61756673)
// MagicBtrfs filesystem id for Btrfs
MagicBtrfs = Magic(0x9123683E)
// MagicCramfs filesystem id for Cramfs
MagicCramfs = Magic(0x28cd3d45)
// MagicEcryptfs filesystem id for eCryptfs
MagicEcryptfs = Magic(0xf15f)
// MagicExtfs filesystem id for Extfs
MagicExtfs = Magic(0x0000EF53)
// MagicF2fs filesystem id for F2fs
MagicF2fs = Magic(0xF2F52010)
// MagicGPFS filesystem id for GPFS
MagicGPFS = Magic(0x47504653)
// MagicJffs2Fs filesystem if for Jffs2Fs
MagicJffs2Fs = Magic(0x000072b6)
// MagicJfs filesystem id for Jfs
MagicJfs = Magic(0x3153464a)
// MagicNfsFs filesystem id for NfsFs
MagicNfsFs = Magic(0x00006969)
// MagicRAMFs filesystem id for RamFs
MagicRAMFs = Magic(0x858458f6)
// MagicReiserFs filesystem id for ReiserFs
MagicReiserFs = Magic(0x52654973)
// MagicSmbFs filesystem id for SmbFs
MagicSmbFs = Magic(0x0000517B)
// MagicSquashFs filesystem id for SquashFs
MagicSquashFs = Magic(0x73717368)
// MagicTmpFs filesystem id for TmpFs
MagicTmpFs = Magic(0x01021994)
// MagicVxFS filesystem id for VxFs
MagicVxFS = Magic(0xa501fcf5)
// MagicXfs filesystem id for Xfs
MagicXfs = Magic(0x58465342)
// MagicZfs filesystem id for Zfs
MagicZfs = Magic(0x2fc12fc1)
// MagicOverlay filesystem id for overlay
MagicOverlay = Magic(0x794C7630)
)
var (
// FsNames maps filesystem id to name of the filesystem.
FsNames = map[Magic]string{
MagicAufs: "aufs",
MagicBtrfs: "btrfs",
MagicCramfs: "cramfs",
MagicExtfs: "extfs",
MagicF2fs: "f2fs",
MagicGPFS: "gpfs",
MagicJffs2Fs: "jffs2",
MagicJfs: "jfs",
MagicNfsFs: "nfs",
MagicOverlay: "overlayfs",
MagicRAMFs: "ramfs",
MagicReiserFs: "reiserfs",
MagicSmbFs: "smb",
MagicSquashFs: "squashfs",
MagicTmpFs: "tmpfs",
MagicUnsupported: "unsupported",
MagicVxFS: "vxfs",
MagicXfs: "xfs",
MagicZfs: "zfs",
}
)
// GetMagic returns the filesystem id given the path.
func GetMagic(rootpath string) (Magic, error) {
var buf syscall.Statfs_t
if err := syscall.Statfs(filepath.Dir(rootpath), &buf); err != nil {
return 0, err
}
return Magic(buf.Type), nil
}

View File

@ -1,5 +1,4 @@
//go:build darwin || freebsd || netbsd
// +build darwin freebsd netbsd
/*
Copyright The containerd Authors.

View File

@ -1,5 +1,4 @@
//go:build linux || openbsd || dragonfly || solaris
// +build linux openbsd dragonfly solaris
/*
Copyright The containerd Authors.
@ -20,10 +19,36 @@
package fs
import (
"fmt"
"io/fs"
"syscall"
"time"
)
func Atime(st fs.FileInfo) (time.Time, error) {
stSys, ok := st.Sys().(*syscall.Stat_t)
if !ok {
return time.Time{}, fmt.Errorf("expected st.Sys() to be *syscall.Stat_t, got %T", st.Sys())
}
return StatATimeAsTime(stSys), nil
}
func Ctime(st fs.FileInfo) (time.Time, error) {
stSys, ok := st.Sys().(*syscall.Stat_t)
if !ok {
return time.Time{}, fmt.Errorf("expected st.Sys() to be *syscall.Stat_t, got %T", st.Sys())
}
return time.Unix(stSys.Atim.Unix()), nil
}
func Mtime(st fs.FileInfo) (time.Time, error) {
stSys, ok := st.Sys().(*syscall.Stat_t)
if !ok {
return time.Time{}, fmt.Errorf("expected st.Sys() to be *syscall.Stat_t, got %T", st.Sys())
}
return time.Unix(stSys.Mtim.Unix()), nil
}
// StatAtime returns the Atim
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
return st.Atim

View File

@ -0,0 +1,33 @@
/*
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 fs
import (
"fmt"
"io/fs"
"syscall"
"time"
)
func Atime(st fs.FileInfo) (time.Time, error) {
stSys, ok := st.Sys().(*syscall.Win32FileAttributeData)
if !ok {
return time.Time{}, fmt.Errorf("expected st.Sys() to be *syscall.Win32FileAttributeData, got %T", st.Sys())
}
// ref: https://github.com/golang/go/blob/go1.19.2/src/os/types_windows.go#L230
return time.Unix(0, stSys.LastAccessTime.Nanoseconds()), nil
}

View File

@ -1,5 +1,4 @@
//go:build !(windows || linux)
// +build !windows,!linux
/*
Copyright The containerd Authors.