diff --git a/apply-patch b/apply-patch index 7080a3d6a9ea40466f9175ef7d139a1695d26538..47718c81d3919ea860c0b6e28cd176a87734339a 100755 --- a/apply-patch +++ b/apply-patch @@ -17,8 +17,9 @@ fi series=$cwd/series.conf while IPF= read -r line do - if [[ "$line" =~ ^0.* ]]; then - cd $src && patch -p1 < $cwd/patch/$line + if [[ "$line" =~ ^patch* ]]; then + echo patch -p1 $cwd/$line + cd $src && patch -p1 < $cwd/$line fi done <"$series" diff --git a/git-commit b/git-commit index ca6622166553bc5eedbec153223778f93e3eb7f1..cc62da00bd18417304fa0f2acf0a06ea5a4799e7 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -93d1288dd83d4c83232730dcd6d5de181e6696ad +efce815f50e77075d10070d724aeec93660630a7 diff --git a/patch/0126-runc-add-check-in-spec.patch b/patch/0126-runc-add-check-in-spec.patch new file mode 100644 index 0000000000000000000000000000000000000000..14e28ee2b0fc83a63314ab6549429b7f5209def0 --- /dev/null +++ b/patch/0126-runc-add-check-in-spec.patch @@ -0,0 +1,27 @@ +From 34e659c12eb4ae543e3c7a6539a3d51ec2ec295b Mon Sep 17 00:00:00 2001 +From: xiadanni +Date: Fri, 19 Feb 2021 11:18:25 +0800 +Subject: [PATCH] runc: add check in spec + +Signed-off-by: xiadanni +--- + Makefile | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/Makefile b/Makefile +index fcf34ea..364ac89 100644 +--- a/Makefile ++++ b/Makefile +@@ -89,6 +89,9 @@ man: + runcimage: + docker build -t $(RUNC_IMAGE) . + ++check: ++ go test -v ./libcontainer ++ + test: + make unittest integration rootlessintegration + +-- +1.8.3.1 + diff --git a/patch/0127-runc-add-mount-destination-validation-fix-CVE-2021.patch b/patch/0127-runc-add-mount-destination-validation-fix-CVE-2021.patch new file mode 100644 index 0000000000000000000000000000000000000000..5649b87b1f5ac2908054b64f7a06f86422035dae --- /dev/null +++ b/patch/0127-runc-add-mount-destination-validation-fix-CVE-2021.patch @@ -0,0 +1,1344 @@ +From d0b1d58edca28b6e4264abb1645fa8f7e56fc03e Mon Sep 17 00:00:00 2001 +From: xiadanni +Date: Thu, 3 Jun 2021 19:58:55 +0800 +Subject: [PATCH] rootfs:add mount destination validation(fix CVE-2021-30465) + +Conflict:NA +Reference:https://github.com/opencontainers/runc/commit/0ca91f44f1664da834bc61115a849b56d22f595f + +Signed-off-by: xiadanni +--- + libcontainer/init_linux.go | 1 + + libcontainer/rootfs_linux.go | 38 ++- + libcontainer/utils/utils.go | 69 +++++ + .../cyphar/filepath-securejoin/.travis.yml | 19 ++ + .../cyphar/filepath-securejoin/LICENSE | 28 ++ + .../cyphar/filepath-securejoin/README.md | 65 ++++ + .../cyphar/filepath-securejoin/VERSION | 1 + + .../cyphar/filepath-securejoin/join.go | 134 ++++++++ + .../cyphar/filepath-securejoin/vendor.conf | 1 + + .../cyphar/filepath-securejoin/vfs.go | 41 +++ + vendor/github.com/pkg/errors/.gitignore | 24 ++ + vendor/github.com/pkg/errors/.travis.yml | 10 + + vendor/github.com/pkg/errors/LICENSE | 23 ++ + vendor/github.com/pkg/errors/Makefile | 44 +++ + vendor/github.com/pkg/errors/README.md | 59 ++++ + vendor/github.com/pkg/errors/appveyor.yml | 32 ++ + vendor/github.com/pkg/errors/errors.go | 288 ++++++++++++++++++ + vendor/github.com/pkg/errors/go113.go | 38 +++ + vendor/github.com/pkg/errors/stack.go | 177 +++++++++++ + 19 files changed, 1080 insertions(+), 12 deletions(-) + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/.travis.yml + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/LICENSE + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/README.md + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/VERSION + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/join.go + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/vendor.conf + create mode 100644 vendor/github.com/cyphar/filepath-securejoin/vfs.go + create mode 100644 vendor/github.com/pkg/errors/.gitignore + create mode 100644 vendor/github.com/pkg/errors/.travis.yml + create mode 100644 vendor/github.com/pkg/errors/LICENSE + create mode 100644 vendor/github.com/pkg/errors/Makefile + create mode 100644 vendor/github.com/pkg/errors/README.md + create mode 100644 vendor/github.com/pkg/errors/appveyor.yml + create mode 100644 vendor/github.com/pkg/errors/errors.go + create mode 100644 vendor/github.com/pkg/errors/go113.go + create mode 100644 vendor/github.com/pkg/errors/stack.go + +diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go +index 73505ef9..ee632b4c 100644 +--- a/libcontainer/init_linux.go ++++ b/libcontainer/init_linux.go +@@ -71,6 +71,7 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDi + if err := json.NewDecoder(pipe).Decode(&config); err != nil { + return nil, err + } ++ utils.ConfigRootfs = config.Config.Rootfs + if err := populateProcessEnvironment(config.Env); err != nil { + return nil, err + } +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index 4c184822..908e9d53 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -14,6 +14,7 @@ import ( + "syscall" + "time" + ++ securejoin "github.com/cyphar/filepath-securejoin" + "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/symlink" + "github.com/mrunalp/fileutils" +@@ -161,9 +162,13 @@ func mountCmd(cmd configs.Command) error { + func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { + var ( + dest = m.Destination ++ err error + ) + if !strings.HasPrefix(dest, rootfs) { +- dest = filepath.Join(rootfs, dest) ++ dest, err = securejoin.SecureJoin(rootfs, m.Destination) ++ if err != nil { ++ return err ++ } + } + + switch m.Device { +@@ -216,7 +221,7 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { + } + return errMsg + } +- if err := syscall.Mount(tmpDir, dest, "", syscall.MS_MOVE, ""); err != nil { ++ if err := libcontainerUtils.Mount(tmpDir, dest, "", syscall.MS_MOVE, ""); err != nil { + errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err) + if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { + return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) +@@ -494,12 +499,15 @@ func bindMountDeviceNode(dest string, node *configs.Device) error { + if f != nil { + f.Close() + } +- return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") ++ return libcontainerUtils.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") + } + + // Creates the device node in the rootfs of the container. + func createDeviceNode(rootfs string, node *configs.Device, bind bool) error { +- dest := filepath.Join(rootfs, node.Path) ++ dest, err := securejoin.SecureJoin(rootfs, node.Path) ++ if err != nil { ++ return err ++ } + if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { + return err + } +@@ -815,14 +823,16 @@ func writeSystemProperty(key, value string) error { + func remount(m *configs.Mount, rootfs string) error { + var ( + dest = m.Destination ++ err error + ) + if !strings.HasPrefix(dest, rootfs) { +- dest = filepath.Join(rootfs, dest) +- } +- if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { +- return err ++ dest, err = securejoin.SecureJoin(rootfs, dest) ++ if err != nil { ++ return err ++ } + } +- return nil ++ ++ return libcontainerUtils.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), "") + } + + // Do the mount operation followed by additional mounts required to take care +@@ -832,6 +842,7 @@ func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error { + dest = m.Destination + data = label.FormatMountLabel(m.Data, mountLabel) + flags = m.Flags ++ err error + ) + if libcontainerUtils.CleanPath(dest) == "/dev" { + flags &= ^syscall.MS_RDONLY +@@ -839,15 +850,18 @@ func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error { + + copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP + if !(copyUp || strings.HasPrefix(dest, rootfs)) { +- dest = filepath.Join(rootfs, dest) ++ dest, err = securejoin.SecureJoin(rootfs, dest) ++ if err != nil { ++ return err ++ } + } + +- if err := syscall.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil { ++ if err = libcontainerUtils.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil { + return err + } + + for _, pflag := range m.PropagationFlags { +- if err := syscall.Mount("", dest, "", uintptr(pflag), ""); err != nil { ++ if err := libcontainerUtils.Mount("", dest, "", uintptr(pflag), ""); err != nil { + return err + } + } +diff --git a/libcontainer/utils/utils.go b/libcontainer/utils/utils.go +index 2b35b9a7..bdd13d49 100644 +--- a/libcontainer/utils/utils.go ++++ b/libcontainer/utils/utils.go +@@ -4,18 +4,25 @@ import ( + "crypto/rand" + "encoding/hex" + "encoding/json" ++ "fmt" + "io" + "os" + "path/filepath" ++ "strconv" + "strings" + "syscall" + "unsafe" ++ ++ securejoin "github.com/cyphar/filepath-securejoin" ++ "golang.org/x/sys/unix" + ) + + const ( + exitSignalOffset = 128 + ) + ++var ConfigRootfs string ++ + // GenerateRandomName returns a new name joined with a prefix. This size + // specified is used to truncate the randomly generated value + func GenerateRandomName(prefix string, size int) (string, error) { +@@ -124,3 +131,65 @@ func Annotations(labels []string) (bundle string, userAnnotations map[string]str + func GetIntSize() int { + return int(unsafe.Sizeof(1)) + } ++ ++func Mount(source, target, fstype string, flags uintptr, data string) error { ++ if ConfigRootfs == "" { ++ panic("rootfs is not inited from config.json") ++ } ++ return WithProcfd(ConfigRootfs, target, func(procfd string) error { ++ return syscall.Mount(source, procfd, fstype, flags, data) ++ }) ++} ++ ++// stripRoot returns the passed path, stripping the root path if it was ++// (lexicially) inside it. Note that both passed paths will always be treated ++// as absolute, and the returned path will also always be absolute. In ++// addition, the paths are cleaned before stripping the root. ++func stripRoot(root, path string) string { ++ // Make the paths clean and absolute. ++ root, path = CleanPath("/"+root), CleanPath("/"+path) ++ switch { ++ case path == root: ++ path = "/" ++ case root == "/": ++ // do nothing ++ case strings.HasPrefix(path, root+"/"): ++ path = strings.TrimPrefix(path, root+"/") ++ default: ++ panic("invalid path to mount, path not begin with rootfs") ++ } ++ return CleanPath("/" + path) ++} ++ ++// WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) ++// corresponding to the unsafePath resolved within the root. Before passing the ++// fd, this path is verified to have been inside the root -- so operating on it ++// through the passed fdpath should be safe. Do not access this path through ++// the original path strings, and do not attempt to use the pathname outside of ++// the passed closure (the file handle will be freed once the closure returns). ++func WithProcfd(root, unsafePath string, fn func(procfd string) error) error { ++ // Remove the root then forcefully resolve inside the root. ++ unsafePath = stripRoot(root, unsafePath) ++ path, err := securejoin.SecureJoin(root, unsafePath) ++ if err != nil { ++ return fmt.Errorf("resolving path inside rootfs failed: %v", err) ++ } ++ ++ // Open the target path. ++ fh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return fmt.Errorf("open o_path procfd: %w", err) ++ } ++ defer fh.Close() ++ ++ // Double-check the path is the one we expected. ++ procfd := "/proc/self/fd/" + strconv.Itoa(int(fh.Fd())) ++ if realpath, err := os.Readlink(procfd); err != nil { ++ return fmt.Errorf("procfd verification failed: %w", err) ++ } else if realpath != path { ++ return fmt.Errorf("possibly malicious path detected -- refusing to operate on %s", realpath) ++ } ++ ++ // Run the closure. ++ return fn(procfd) ++} +diff --git a/vendor/github.com/cyphar/filepath-securejoin/.travis.yml b/vendor/github.com/cyphar/filepath-securejoin/.travis.yml +new file mode 100644 +index 00000000..3938f383 +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/.travis.yml +@@ -0,0 +1,19 @@ ++# Copyright (C) 2017 SUSE LLC. All rights reserved. ++# Use of this source code is governed by a BSD-style ++# license that can be found in the LICENSE file. ++ ++language: go ++go: ++ - 1.7.x ++ - 1.8.x ++ - tip ++ ++os: ++ - linux ++ - osx ++ ++script: ++ - go test -cover -v ./... ++ ++notifications: ++ email: false +diff --git a/vendor/github.com/cyphar/filepath-securejoin/LICENSE b/vendor/github.com/cyphar/filepath-securejoin/LICENSE +new file mode 100644 +index 00000000..bec842f2 +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/LICENSE +@@ -0,0 +1,28 @@ ++Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. ++Copyright (C) 2017 SUSE LLC. All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are ++met: ++ ++ * Redistributions of source code must retain the above copyright ++notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above ++copyright notice, this list of conditions and the following disclaimer ++in the documentation and/or other materials provided with the ++distribution. ++ * Neither the name of Google Inc. nor the names of its ++contributors may be used to endorse or promote products derived from ++this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +diff --git a/vendor/github.com/cyphar/filepath-securejoin/README.md b/vendor/github.com/cyphar/filepath-securejoin/README.md +new file mode 100644 +index 00000000..49b2baa9 +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/README.md +@@ -0,0 +1,65 @@ ++## `filepath-securejoin` ## ++ ++[![Build Status](https://travis-ci.org/cyphar/filepath-securejoin.svg?branch=master)](https://travis-ci.org/cyphar/filepath-securejoin) ++ ++An implementation of `SecureJoin`, a [candidate for inclusion in the Go ++standard library][go#20126]. The purpose of this function is to be a "secure" ++alternative to `filepath.Join`, and in particular it provides certain ++guarantees that are not provided by `filepath.Join`. ++ ++This is the function prototype: ++ ++```go ++func SecureJoin(root, unsafePath string) (string, error) ++``` ++ ++This library **guarantees** the following: ++ ++* If no error is set, the resulting string **must** be a child path of ++ `SecureJoin` and will not contain any symlink path components (they will all ++ be expanded). ++ ++* When expanding symlinks, all symlink path components **must** be resolved ++ relative to the provided root. In particular, this can be considered a ++ userspace implementation of how `chroot(2)` operates on file paths. Note that ++ these symlinks will **not** be expanded lexically (`filepath.Clean` is not ++ called on the input before processing). ++ ++* Non-existant path components are unaffected by `SecureJoin` (similar to ++ `filepath.EvalSymlinks`'s semantics). ++ ++* The returned path will always be `filepath.Clean`ed and thus not contain any ++ `..` components. ++ ++A (trivial) implementation of this function on GNU/Linux systems could be done ++with the following (note that this requires root privileges and is far more ++opaque than the implementation in this library, and also requires that ++`readlink` is inside the `root` path): ++ ++```go ++package securejoin ++ ++import ( ++ "os/exec" ++ "path/filepath" ++) ++ ++func SecureJoin(root, unsafePath string) (string, error) { ++ unsafePath = string(filepath.Separator) + unsafePath ++ cmd := exec.Command("chroot", root, ++ "readlink", "--canonicalize-missing", "--no-newline", unsafePath) ++ output, err := cmd.CombinedOutput() ++ if err != nil { ++ return "", err ++ } ++ expanded := string(output) ++ return filepath.Join(root, expanded), nil ++} ++``` ++ ++[go#20126]: https://github.com/golang/go/issues/20126 ++ ++### License ### ++ ++The license of this project is the same as Go, which is a BSD 3-clause license ++available in the `LICENSE` file. +diff --git a/vendor/github.com/cyphar/filepath-securejoin/VERSION b/vendor/github.com/cyphar/filepath-securejoin/VERSION +new file mode 100644 +index 00000000..ee1372d3 +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/VERSION +@@ -0,0 +1 @@ ++0.2.2 +diff --git a/vendor/github.com/cyphar/filepath-securejoin/join.go b/vendor/github.com/cyphar/filepath-securejoin/join.go +new file mode 100644 +index 00000000..c4ca3d71 +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/join.go +@@ -0,0 +1,134 @@ ++// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. ++// Copyright (C) 2017 SUSE LLC. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++// Package securejoin is an implementation of the hopefully-soon-to-be-included ++// SecureJoin helper that is meant to be part of the "path/filepath" package. ++// The purpose of this project is to provide a PoC implementation to make the ++// SecureJoin proposal (https://github.com/golang/go/issues/20126) more ++// tangible. ++package securejoin ++ ++import ( ++ "bytes" ++ "os" ++ "path/filepath" ++ "strings" ++ "syscall" ++ ++ "github.com/pkg/errors" ++) ++ ++// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been ++// evaluated in attempting to securely join the two given paths. ++var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join") ++ ++// IsNotExist tells you if err is an error that implies that either the path ++// accessed does not exist (or path components don't exist). This is ++// effectively a more broad version of os.IsNotExist. ++func IsNotExist(err error) bool { ++ // If it's a bone-fide ENOENT just bail. ++ if os.IsNotExist(errors.Cause(err)) { ++ return true ++ } ++ ++ // Check that it's not actually an ENOTDIR, which in some cases is a more ++ // convoluted case of ENOENT (usually involving weird paths). ++ var errno error ++ switch err := errors.Cause(err).(type) { ++ case *os.PathError: ++ errno = err.Err ++ case *os.LinkError: ++ errno = err.Err ++ case *os.SyscallError: ++ errno = err.Err ++ } ++ return errno == syscall.ENOTDIR || errno == syscall.ENOENT ++} ++ ++// SecureJoinVFS joins the two given path components (similar to Join) except ++// that the returned path is guaranteed to be scoped inside the provided root ++// path (when evaluated). Any symbolic links in the path are evaluated with the ++// given root treated as the root of the filesystem, similar to a chroot. The ++// filesystem state is evaluated through the given VFS interface (if nil, the ++// standard os.* family of functions are used). ++// ++// Note that the guarantees provided by this function only apply if the path ++// components in the returned string are not modified (in other words are not ++// replaced with symlinks on the filesystem) after this function has returned. ++// Such a symlink race is necessarily out-of-scope of SecureJoin. ++func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) { ++ // Use the os.* VFS implementation if none was specified. ++ if vfs == nil { ++ vfs = osVFS{} ++ } ++ ++ var path bytes.Buffer ++ n := 0 ++ for unsafePath != "" { ++ if n > 255 { ++ return "", ErrSymlinkLoop ++ } ++ ++ // Next path component, p. ++ i := strings.IndexRune(unsafePath, filepath.Separator) ++ var p string ++ if i == -1 { ++ p, unsafePath = unsafePath, "" ++ } else { ++ p, unsafePath = unsafePath[:i], unsafePath[i+1:] ++ } ++ ++ // Create a cleaned path, using the lexical semantics of /../a, to ++ // create a "scoped" path component which can safely be joined to fullP ++ // for evaluation. At this point, path.String() doesn't contain any ++ // symlink components. ++ cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p) ++ if cleanP == string(filepath.Separator) { ++ path.Reset() ++ continue ++ } ++ fullP := filepath.Clean(root + cleanP) ++ ++ // Figure out whether the path is a symlink. ++ fi, err := vfs.Lstat(fullP) ++ if err != nil && !IsNotExist(err) { ++ return "", err ++ } ++ // Treat non-existent path components the same as non-symlinks (we ++ // can't do any better here). ++ if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 { ++ path.WriteString(p) ++ path.WriteRune(filepath.Separator) ++ continue ++ } ++ ++ // Only increment when we actually dereference a link. ++ n++ ++ ++ // It's a symlink, expand it by prepending it to the yet-unparsed path. ++ dest, err := vfs.Readlink(fullP) ++ if err != nil { ++ return "", err ++ } ++ // Absolute symlinks reset any work we've already done. ++ if filepath.IsAbs(dest) { ++ path.Reset() ++ } ++ unsafePath = dest + string(filepath.Separator) + unsafePath ++ } ++ ++ // We have to clean path.String() here because it may contain '..' ++ // components that are entirely lexical, but would be misleading otherwise. ++ // And finally do a final clean to ensure that root is also lexically ++ // clean. ++ fullP := filepath.Clean(string(filepath.Separator) + path.String()) ++ return filepath.Clean(root + fullP), nil ++} ++ ++// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library ++// of functions as the VFS. If in doubt, use this function over SecureJoinVFS. ++func SecureJoin(root, unsafePath string) (string, error) { ++ return SecureJoinVFS(root, unsafePath, nil) ++} +diff --git a/vendor/github.com/cyphar/filepath-securejoin/vendor.conf b/vendor/github.com/cyphar/filepath-securejoin/vendor.conf +new file mode 100644 +index 00000000..66bb574b +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/vendor.conf +@@ -0,0 +1 @@ ++github.com/pkg/errors v0.8.0 +diff --git a/vendor/github.com/cyphar/filepath-securejoin/vfs.go b/vendor/github.com/cyphar/filepath-securejoin/vfs.go +new file mode 100644 +index 00000000..a82a5eae +--- /dev/null ++++ b/vendor/github.com/cyphar/filepath-securejoin/vfs.go +@@ -0,0 +1,41 @@ ++// Copyright (C) 2017 SUSE LLC. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package securejoin ++ ++import "os" ++ ++// In future this should be moved into a separate package, because now there ++// are several projects (umoci and go-mtree) that are using this sort of ++// interface. ++ ++// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is ++// equivalent to using the standard os.* family of functions. This is mainly ++// used for the purposes of mock testing, but also can be used to otherwise use ++// SecureJoin with VFS-like system. ++type VFS interface { ++ // Lstat returns a FileInfo describing the named file. If the file is a ++ // symbolic link, the returned FileInfo describes the symbolic link. Lstat ++ // makes no attempt to follow the link. These semantics are identical to ++ // os.Lstat. ++ Lstat(name string) (os.FileInfo, error) ++ ++ // Readlink returns the destination of the named symbolic link. These ++ // semantics are identical to os.Readlink. ++ Readlink(name string) (string, error) ++} ++ ++// osVFS is the "nil" VFS, in that it just passes everything through to the os ++// module. ++type osVFS struct{} ++ ++// Lstat returns a FileInfo describing the named file. If the file is a ++// symbolic link, the returned FileInfo describes the symbolic link. Lstat ++// makes no attempt to follow the link. These semantics are identical to ++// os.Lstat. ++func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } ++ ++// Readlink returns the destination of the named symbolic link. These ++// semantics are identical to os.Readlink. ++func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) } +diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore +new file mode 100644 +index 00000000..daf913b1 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/.gitignore +@@ -0,0 +1,24 @@ ++# Compiled Object files, Static and Dynamic libs (Shared Objects) ++*.o ++*.a ++*.so ++ ++# Folders ++_obj ++_test ++ ++# Architecture specific extensions/prefixes ++*.[568vq] ++[568vq].out ++ ++*.cgo1.go ++*.cgo2.c ++_cgo_defun.c ++_cgo_gotypes.go ++_cgo_export.* ++ ++_testmain.go ++ ++*.exe ++*.test ++*.prof +diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml +new file mode 100644 +index 00000000..9159de03 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/.travis.yml +@@ -0,0 +1,10 @@ ++language: go ++go_import_path: github.com/pkg/errors ++go: ++ - 1.11.x ++ - 1.12.x ++ - 1.13.x ++ - tip ++ ++script: ++ - make check +diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE +new file mode 100644 +index 00000000..835ba3e7 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/LICENSE +@@ -0,0 +1,23 @@ ++Copyright (c) 2015, Dave Cheney ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++ ++* Redistributions of source code must retain the above copyright notice, this ++ list of conditions and the following disclaimer. ++ ++* Redistributions in binary form must reproduce the above copyright notice, ++ this list of conditions and the following disclaimer in the documentation ++ and/or other materials provided with the distribution. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE ++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +diff --git a/vendor/github.com/pkg/errors/Makefile b/vendor/github.com/pkg/errors/Makefile +new file mode 100644 +index 00000000..ce9d7cde +--- /dev/null ++++ b/vendor/github.com/pkg/errors/Makefile +@@ -0,0 +1,44 @@ ++PKGS := github.com/pkg/errors ++SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) ++GO := go ++ ++check: test vet gofmt misspell unconvert staticcheck ineffassign unparam ++ ++test: ++ $(GO) test $(PKGS) ++ ++vet: | test ++ $(GO) vet $(PKGS) ++ ++staticcheck: ++ $(GO) get honnef.co/go/tools/cmd/staticcheck ++ staticcheck -checks all $(PKGS) ++ ++misspell: ++ $(GO) get github.com/client9/misspell/cmd/misspell ++ misspell \ ++ -locale GB \ ++ -error \ ++ *.md *.go ++ ++unconvert: ++ $(GO) get github.com/mdempsky/unconvert ++ unconvert -v $(PKGS) ++ ++ineffassign: ++ $(GO) get github.com/gordonklaus/ineffassign ++ find $(SRCDIRS) -name '*.go' | xargs ineffassign ++ ++pedantic: check errcheck ++ ++unparam: ++ $(GO) get mvdan.cc/unparam ++ unparam ./... ++ ++errcheck: ++ $(GO) get github.com/kisielk/errcheck ++ errcheck $(PKGS) ++ ++gofmt: ++ @echo Checking code is gofmted ++ @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)" +diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md +new file mode 100644 +index 00000000..54dfdcb1 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/README.md +@@ -0,0 +1,59 @@ ++# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) ++ ++Package errors provides simple error handling primitives. ++ ++`go get github.com/pkg/errors` ++ ++The traditional error handling idiom in Go is roughly akin to ++```go ++if err != nil { ++ return err ++} ++``` ++which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. ++ ++## Adding context to an error ++ ++The errors.Wrap function returns a new error that adds context to the original error. For example ++```go ++_, err := ioutil.ReadAll(r) ++if err != nil { ++ return errors.Wrap(err, "read failed") ++} ++``` ++## Retrieving the cause of an error ++ ++Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. ++```go ++type causer interface { ++ Cause() error ++} ++``` ++`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: ++```go ++switch err := errors.Cause(err).(type) { ++case *MyError: ++ // handle specifically ++default: ++ // unknown error ++} ++``` ++ ++[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). ++ ++## Roadmap ++ ++With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: ++ ++- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible) ++- 1.0. Final release. ++ ++## Contributing ++ ++Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. ++ ++Before sending a PR, please discuss your change by raising an issue. ++ ++## License ++ ++BSD-2-Clause +diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml +new file mode 100644 +index 00000000..a932eade +--- /dev/null ++++ b/vendor/github.com/pkg/errors/appveyor.yml +@@ -0,0 +1,32 @@ ++version: build-{build}.{branch} ++ ++clone_folder: C:\gopath\src\github.com\pkg\errors ++shallow_clone: true # for startup speed ++ ++environment: ++ GOPATH: C:\gopath ++ ++platform: ++ - x64 ++ ++# http://www.appveyor.com/docs/installed-software ++install: ++ # some helpful output for debugging builds ++ - go version ++ - go env ++ # pre-installed MinGW at C:\MinGW is 32bit only ++ # but MSYS2 at C:\msys64 has mingw64 ++ - set PATH=C:\msys64\mingw64\bin;%PATH% ++ - gcc --version ++ - g++ --version ++ ++build_script: ++ - go install -v ./... ++ ++test_script: ++ - set PATH=C:\gopath\bin;%PATH% ++ - go test -v ./... ++ ++#artifacts: ++# - path: '%GOPATH%\bin\*.exe' ++deploy: off +diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go +new file mode 100644 +index 00000000..161aea25 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/errors.go +@@ -0,0 +1,288 @@ ++// Package errors provides simple error handling primitives. ++// ++// The traditional error handling idiom in Go is roughly akin to ++// ++// if err != nil { ++// return err ++// } ++// ++// which when applied recursively up the call stack results in error reports ++// without context or debugging information. The errors package allows ++// programmers to add context to the failure path in their code in a way ++// that does not destroy the original value of the error. ++// ++// Adding context to an error ++// ++// The errors.Wrap function returns a new error that adds context to the ++// original error by recording a stack trace at the point Wrap is called, ++// together with the supplied message. For example ++// ++// _, err := ioutil.ReadAll(r) ++// if err != nil { ++// return errors.Wrap(err, "read failed") ++// } ++// ++// If additional control is required, the errors.WithStack and ++// errors.WithMessage functions destructure errors.Wrap into its component ++// operations: annotating an error with a stack trace and with a message, ++// respectively. ++// ++// Retrieving the cause of an error ++// ++// Using errors.Wrap constructs a stack of errors, adding context to the ++// preceding error. Depending on the nature of the error it may be necessary ++// to reverse the operation of errors.Wrap to retrieve the original error ++// for inspection. Any error value which implements this interface ++// ++// type causer interface { ++// Cause() error ++// } ++// ++// can be inspected by errors.Cause. errors.Cause will recursively retrieve ++// the topmost error that does not implement causer, which is assumed to be ++// the original cause. For example: ++// ++// switch err := errors.Cause(err).(type) { ++// case *MyError: ++// // handle specifically ++// default: ++// // unknown error ++// } ++// ++// Although the causer interface is not exported by this package, it is ++// considered a part of its stable public interface. ++// ++// Formatted printing of errors ++// ++// All error values returned from this package implement fmt.Formatter and can ++// be formatted by the fmt package. The following verbs are supported: ++// ++// %s print the error. If the error has a Cause it will be ++// printed recursively. ++// %v see %s ++// %+v extended format. Each Frame of the error's StackTrace will ++// be printed in detail. ++// ++// Retrieving the stack trace of an error or wrapper ++// ++// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are ++// invoked. This information can be retrieved with the following interface: ++// ++// type stackTracer interface { ++// StackTrace() errors.StackTrace ++// } ++// ++// The returned errors.StackTrace type is defined as ++// ++// type StackTrace []Frame ++// ++// The Frame type represents a call site in the stack trace. Frame supports ++// the fmt.Formatter interface that can be used for printing information about ++// the stack trace of this error. For example: ++// ++// if err, ok := err.(stackTracer); ok { ++// for _, f := range err.StackTrace() { ++// fmt.Printf("%+s:%d\n", f, f) ++// } ++// } ++// ++// Although the stackTracer interface is not exported by this package, it is ++// considered a part of its stable public interface. ++// ++// See the documentation for Frame.Format for more details. ++package errors ++ ++import ( ++ "fmt" ++ "io" ++) ++ ++// New returns an error with the supplied message. ++// New also records the stack trace at the point it was called. ++func New(message string) error { ++ return &fundamental{ ++ msg: message, ++ stack: callers(), ++ } ++} ++ ++// Errorf formats according to a format specifier and returns the string ++// as a value that satisfies error. ++// Errorf also records the stack trace at the point it was called. ++func Errorf(format string, args ...interface{}) error { ++ return &fundamental{ ++ msg: fmt.Sprintf(format, args...), ++ stack: callers(), ++ } ++} ++ ++// fundamental is an error that has a message and a stack, but no caller. ++type fundamental struct { ++ msg string ++ *stack ++} ++ ++func (f *fundamental) Error() string { return f.msg } ++ ++func (f *fundamental) Format(s fmt.State, verb rune) { ++ switch verb { ++ case 'v': ++ if s.Flag('+') { ++ io.WriteString(s, f.msg) ++ f.stack.Format(s, verb) ++ return ++ } ++ fallthrough ++ case 's': ++ io.WriteString(s, f.msg) ++ case 'q': ++ fmt.Fprintf(s, "%q", f.msg) ++ } ++} ++ ++// WithStack annotates err with a stack trace at the point WithStack was called. ++// If err is nil, WithStack returns nil. ++func WithStack(err error) error { ++ if err == nil { ++ return nil ++ } ++ return &withStack{ ++ err, ++ callers(), ++ } ++} ++ ++type withStack struct { ++ error ++ *stack ++} ++ ++func (w *withStack) Cause() error { return w.error } ++ ++// Unwrap provides compatibility for Go 1.13 error chains. ++func (w *withStack) Unwrap() error { return w.error } ++ ++func (w *withStack) Format(s fmt.State, verb rune) { ++ switch verb { ++ case 'v': ++ if s.Flag('+') { ++ fmt.Fprintf(s, "%+v", w.Cause()) ++ w.stack.Format(s, verb) ++ return ++ } ++ fallthrough ++ case 's': ++ io.WriteString(s, w.Error()) ++ case 'q': ++ fmt.Fprintf(s, "%q", w.Error()) ++ } ++} ++ ++// Wrap returns an error annotating err with a stack trace ++// at the point Wrap is called, and the supplied message. ++// If err is nil, Wrap returns nil. ++func Wrap(err error, message string) error { ++ if err == nil { ++ return nil ++ } ++ err = &withMessage{ ++ cause: err, ++ msg: message, ++ } ++ return &withStack{ ++ err, ++ callers(), ++ } ++} ++ ++// Wrapf returns an error annotating err with a stack trace ++// at the point Wrapf is called, and the format specifier. ++// If err is nil, Wrapf returns nil. ++func Wrapf(err error, format string, args ...interface{}) error { ++ if err == nil { ++ return nil ++ } ++ err = &withMessage{ ++ cause: err, ++ msg: fmt.Sprintf(format, args...), ++ } ++ return &withStack{ ++ err, ++ callers(), ++ } ++} ++ ++// WithMessage annotates err with a new message. ++// If err is nil, WithMessage returns nil. ++func WithMessage(err error, message string) error { ++ if err == nil { ++ return nil ++ } ++ return &withMessage{ ++ cause: err, ++ msg: message, ++ } ++} ++ ++// WithMessagef annotates err with the format specifier. ++// If err is nil, WithMessagef returns nil. ++func WithMessagef(err error, format string, args ...interface{}) error { ++ if err == nil { ++ return nil ++ } ++ return &withMessage{ ++ cause: err, ++ msg: fmt.Sprintf(format, args...), ++ } ++} ++ ++type withMessage struct { ++ cause error ++ msg string ++} ++ ++func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } ++func (w *withMessage) Cause() error { return w.cause } ++ ++// Unwrap provides compatibility for Go 1.13 error chains. ++func (w *withMessage) Unwrap() error { return w.cause } ++ ++func (w *withMessage) Format(s fmt.State, verb rune) { ++ switch verb { ++ case 'v': ++ if s.Flag('+') { ++ fmt.Fprintf(s, "%+v\n", w.Cause()) ++ io.WriteString(s, w.msg) ++ return ++ } ++ fallthrough ++ case 's', 'q': ++ io.WriteString(s, w.Error()) ++ } ++} ++ ++// Cause returns the underlying cause of the error, if possible. ++// An error value has a cause if it implements the following ++// interface: ++// ++// type causer interface { ++// Cause() error ++// } ++// ++// If the error does not implement Cause, the original error will ++// be returned. If the error is nil, nil will be returned without further ++// investigation. ++func Cause(err error) error { ++ type causer interface { ++ Cause() error ++ } ++ ++ for err != nil { ++ cause, ok := err.(causer) ++ if !ok { ++ break ++ } ++ err = cause.Cause() ++ } ++ return err ++} +diff --git a/vendor/github.com/pkg/errors/go113.go b/vendor/github.com/pkg/errors/go113.go +new file mode 100644 +index 00000000..be0d10d0 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/go113.go +@@ -0,0 +1,38 @@ ++// +build go1.13 ++ ++package errors ++ ++import ( ++ stderrors "errors" ++) ++ ++// Is reports whether any error in err's chain matches target. ++// ++// The chain consists of err itself followed by the sequence of errors obtained by ++// repeatedly calling Unwrap. ++// ++// An error is considered to match a target if it is equal to that target or if ++// it implements a method Is(error) bool such that Is(target) returns true. ++func Is(err, target error) bool { return stderrors.Is(err, target) } ++ ++// As finds the first error in err's chain that matches target, and if so, sets ++// target to that error value and returns true. ++// ++// The chain consists of err itself followed by the sequence of errors obtained by ++// repeatedly calling Unwrap. ++// ++// An error matches target if the error's concrete value is assignable to the value ++// pointed to by target, or if the error has a method As(interface{}) bool such that ++// As(target) returns true. In the latter case, the As method is responsible for ++// setting target. ++// ++// As will panic if target is not a non-nil pointer to either a type that implements ++// error, or to any interface type. As returns false if err is nil. ++func As(err error, target interface{}) bool { return stderrors.As(err, target) } ++ ++// Unwrap returns the result of calling the Unwrap method on err, if err's ++// type contains an Unwrap method returning error. ++// Otherwise, Unwrap returns nil. ++func Unwrap(err error) error { ++ return stderrors.Unwrap(err) ++} +diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go +new file mode 100644 +index 00000000..779a8348 +--- /dev/null ++++ b/vendor/github.com/pkg/errors/stack.go +@@ -0,0 +1,177 @@ ++package errors ++ ++import ( ++ "fmt" ++ "io" ++ "path" ++ "runtime" ++ "strconv" ++ "strings" ++) ++ ++// Frame represents a program counter inside a stack frame. ++// For historical reasons if Frame is interpreted as a uintptr ++// its value represents the program counter + 1. ++type Frame uintptr ++ ++// pc returns the program counter for this frame; ++// multiple frames may have the same PC value. ++func (f Frame) pc() uintptr { return uintptr(f) - 1 } ++ ++// file returns the full path to the file that contains the ++// function for this Frame's pc. ++func (f Frame) file() string { ++ fn := runtime.FuncForPC(f.pc()) ++ if fn == nil { ++ return "unknown" ++ } ++ file, _ := fn.FileLine(f.pc()) ++ return file ++} ++ ++// line returns the line number of source code of the ++// function for this Frame's pc. ++func (f Frame) line() int { ++ fn := runtime.FuncForPC(f.pc()) ++ if fn == nil { ++ return 0 ++ } ++ _, line := fn.FileLine(f.pc()) ++ return line ++} ++ ++// name returns the name of this function, if known. ++func (f Frame) name() string { ++ fn := runtime.FuncForPC(f.pc()) ++ if fn == nil { ++ return "unknown" ++ } ++ return fn.Name() ++} ++ ++// Format formats the frame according to the fmt.Formatter interface. ++// ++// %s source file ++// %d source line ++// %n function name ++// %v equivalent to %s:%d ++// ++// Format accepts flags that alter the printing of some verbs, as follows: ++// ++// %+s function name and path of source file relative to the compile time ++// GOPATH separated by \n\t (\n\t) ++// %+v equivalent to %+s:%d ++func (f Frame) Format(s fmt.State, verb rune) { ++ switch verb { ++ case 's': ++ switch { ++ case s.Flag('+'): ++ io.WriteString(s, f.name()) ++ io.WriteString(s, "\n\t") ++ io.WriteString(s, f.file()) ++ default: ++ io.WriteString(s, path.Base(f.file())) ++ } ++ case 'd': ++ io.WriteString(s, strconv.Itoa(f.line())) ++ case 'n': ++ io.WriteString(s, funcname(f.name())) ++ case 'v': ++ f.Format(s, 's') ++ io.WriteString(s, ":") ++ f.Format(s, 'd') ++ } ++} ++ ++// MarshalText formats a stacktrace Frame as a text string. The output is the ++// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. ++func (f Frame) MarshalText() ([]byte, error) { ++ name := f.name() ++ if name == "unknown" { ++ return []byte(name), nil ++ } ++ return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil ++} ++ ++// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). ++type StackTrace []Frame ++ ++// Format formats the stack of Frames according to the fmt.Formatter interface. ++// ++// %s lists source files for each Frame in the stack ++// %v lists the source file and line number for each Frame in the stack ++// ++// Format accepts flags that alter the printing of some verbs, as follows: ++// ++// %+v Prints filename, function, and line number for each Frame in the stack. ++func (st StackTrace) Format(s fmt.State, verb rune) { ++ switch verb { ++ case 'v': ++ switch { ++ case s.Flag('+'): ++ for _, f := range st { ++ io.WriteString(s, "\n") ++ f.Format(s, verb) ++ } ++ case s.Flag('#'): ++ fmt.Fprintf(s, "%#v", []Frame(st)) ++ default: ++ st.formatSlice(s, verb) ++ } ++ case 's': ++ st.formatSlice(s, verb) ++ } ++} ++ ++// formatSlice will format this StackTrace into the given buffer as a slice of ++// Frame, only valid when called with '%s' or '%v'. ++func (st StackTrace) formatSlice(s fmt.State, verb rune) { ++ io.WriteString(s, "[") ++ for i, f := range st { ++ if i > 0 { ++ io.WriteString(s, " ") ++ } ++ f.Format(s, verb) ++ } ++ io.WriteString(s, "]") ++} ++ ++// stack represents a stack of program counters. ++type stack []uintptr ++ ++func (s *stack) Format(st fmt.State, verb rune) { ++ switch verb { ++ case 'v': ++ switch { ++ case st.Flag('+'): ++ for _, pc := range *s { ++ f := Frame(pc) ++ fmt.Fprintf(st, "\n%+v", f) ++ } ++ } ++ } ++} ++ ++func (s *stack) StackTrace() StackTrace { ++ f := make([]Frame, len(*s)) ++ for i := 0; i < len(f); i++ { ++ f[i] = Frame((*s)[i]) ++ } ++ return f ++} ++ ++func callers() *stack { ++ const depth = 32 ++ var pcs [depth]uintptr ++ n := runtime.Callers(3, pcs[:]) ++ var st stack = pcs[0:n] ++ return &st ++} ++ ++// funcname removes the path prefix component of a function's name reported by func.Name(). ++func funcname(name string) string { ++ i := strings.LastIndex(name, "/") ++ name = name[i+1:] ++ i = strings.Index(name, ".") ++ return name[i+1:] ++} +-- +2.27.0 + diff --git a/patch/0128-runc-fix-CVE-2022-29162.patch b/patch/0128-runc-fix-CVE-2022-29162.patch new file mode 100644 index 0000000000000000000000000000000000000000..98182cc42b1e843d84549d5c46ba54b8a01f22bd --- /dev/null +++ b/patch/0128-runc-fix-CVE-2022-29162.patch @@ -0,0 +1,45 @@ +From d940c8e738ce011bbfafdc6ef8af6516f8ee2191 Mon Sep 17 00:00:00 2001 +From: Vanient +Date: Mon, 6 Jun 2022 20:23:46 +0800 +Subject: [PATCH] runc: fix CVE-2022-29162 + +do not set inheritable capabilities +upstream:https://github.com/opencontainers/runc/commit/98fe566c527479195ce3c8167136d2a555fe6b65 + +Signed-off-by: Vanient +--- + exec.go | 1 - + libcontainer/specconv/example.go | 5 ----- + 2 files changed, 6 deletions(-) + +diff --git a/exec.go b/exec.go +index 9ed90ea..5e5ce00 100644 +--- a/exec.go ++++ b/exec.go +@@ -176,7 +176,6 @@ func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { + if caps := context.StringSlice("cap"); len(caps) > 0 { + for _, c := range caps { + p.Capabilities.Bounding = append(p.Capabilities.Bounding, c) +- p.Capabilities.Inheritable = append(p.Capabilities.Inheritable, c) + p.Capabilities.Effective = append(p.Capabilities.Effective, c) + p.Capabilities.Permitted = append(p.Capabilities.Permitted, c) + p.Capabilities.Ambient = append(p.Capabilities.Ambient, c) +diff --git a/libcontainer/specconv/example.go b/libcontainer/specconv/example.go +index 9a4460c..973ea79 100644 +--- a/libcontainer/specconv/example.go ++++ b/libcontainer/specconv/example.go +@@ -46,11 +46,6 @@ func Example() *specs.Spec { + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + }, +- Inheritable: []string{ +- "CAP_AUDIT_WRITE", +- "CAP_KILL", +- "CAP_NET_BIND_SERVICE", +- }, + Ambient: []string{ + "CAP_AUDIT_WRITE", + "CAP_KILL", +-- +2.27.0 + diff --git a/patch/0128-runc-optimize-nsexec-logging.patch b/patch/0128-runc-optimize-nsexec-logging.patch new file mode 100644 index 0000000000000000000000000000000000000000..341fa907661e47725d58d319e0368627cc6a0220 --- /dev/null +++ b/patch/0128-runc-optimize-nsexec-logging.patch @@ -0,0 +1,280 @@ +From f59d2013c5b1e3a7a500023848c5a366301bddec Mon Sep 17 00:00:00 2001 +From: xiadanni +Date: Tue, 8 Jun 2021 17:08:59 +0800 +Subject: [PATCH] runc: optimize nsexec logging + +Conflict:NA +Reference:https://github.com/opencontainers/runc/pull/2034/commits + https://github.com/opencontainers/runc/commit/64bb59f5920b15d886cb2be52aede641fd4a047b + https://github.com/opencontainers/runc/commit/201d60c51d0b78afb780841443200a25d63493a6 + +Signed-off-by: xiadanni +--- + libcontainer/container_linux.go | 21 ++++++++++ + libcontainer/logs/logs.go | 68 +++++++++++++++++++++++++++++++++ + libcontainer/nsenter/nsexec.c | 53 +++++++++++++++++++++++-- + libcontainer/process_linux.go | 3 ++ + 4 files changed, 142 insertions(+), 3 deletions(-) + create mode 100644 libcontainer/logs/logs.go + +diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go +index 73192866..7be84a63 100644 +--- a/libcontainer/container_linux.go ++++ b/libcontainer/container_linux.go +@@ -23,6 +23,7 @@ import ( + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/criurpc" ++ "github.com/opencontainers/runc/libcontainer/logs" + "github.com/opencontainers/runc/libcontainer/system" + "github.com/opencontainers/runc/libcontainer/utils" + "github.com/syndtr/gocapability/capability" +@@ -309,6 +310,17 @@ func (c *linuxContainer) start(process *Process) error { + if err != nil { + return newSystemErrorWithCause(err, "creating new parent process") + } ++ ++ if logsDone := logs.ForwardLogs(); logsDone != nil { ++ defer func() { ++ select { ++ case <-logsDone: ++ case <-time.After(3 * time.Second): ++ logrus.Warnf("wait child close logfd timeout") ++ } ++ }() ++ } ++ + if err := parent.start(); err != nil { + printCgroupInfo(c.config.Cgroups.Path) + // terminate the process to ensure that it properly is reaped. +@@ -408,6 +420,9 @@ func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) { + if err != nil { + return nil, newSystemErrorWithCause(err, "creating new init pipe") + } ++ if err := logs.InitLogPipe(); err != nil { ++ return nil, fmt.Errorf("Unable to create the log pipe: %s", err) ++ } + cmd, err := c.commandTemplate(p, childPipe) + if err != nil { + return nil, newSystemErrorWithCause(err, "creating new command template") +@@ -450,6 +465,12 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec. + cmd.Env = append(cmd.Env, + fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1), + ) ++ ++ cmd.ExtraFiles = append(cmd.ExtraFiles, logs.ChildLogPipe) ++ cmd.Env = append(cmd.Env, ++ fmt.Sprintf("_LIBCONTAINER_LOGPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1), ++ ) ++ + // NOTE: when running a container with no PID namespace and the parent process spawning the container is + // PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason + // even with the parent still running. +diff --git a/libcontainer/logs/logs.go b/libcontainer/logs/logs.go +new file mode 100644 +index 00000000..219fe382 +--- /dev/null ++++ b/libcontainer/logs/logs.go +@@ -0,0 +1,68 @@ ++package logs ++ ++import ( ++ "bufio" ++ "encoding/json" ++ "os" ++ ++ "github.com/Sirupsen/logrus" ++) ++ ++var ( ++ ParentLogPipe *os.File ++ ChildLogPipe *os.File ++) ++ ++func InitLogPipe() error { ++ var err error ++ if ParentLogPipe == nil { ++ ParentLogPipe, ChildLogPipe, err = os.Pipe() ++ } ++ return err ++} ++ ++func CloseChild() { ++ if ChildLogPipe != nil { ++ ChildLogPipe.Close() ++ ChildLogPipe = nil ++ } ++} ++ ++func ForwardLogs() chan error { ++ done := make(chan error, 1) ++ if ParentLogPipe == nil { ++ close(done) ++ return done ++ } ++ ++ s := bufio.NewScanner(ParentLogPipe) ++ go func() { ++ for s.Scan() { ++ processEntry(s.Bytes()) ++ } ++ if err := ParentLogPipe.Close(); err != nil { ++ logrus.Errorf("error closing log source: %v", err) ++ } ++ // The only error we want to return is when reading from ++ // logPipe has failed. ++ done <- s.Err() ++ close(done) ++ }() ++ ++ return done ++} ++ ++func processEntry(text []byte) { ++ if len(text) == 0 { ++ return ++ } ++ var jl struct { ++ Level string `json:"level"` ++ Msg string `json:"msg"` ++ } ++ if err := json.Unmarshal(text, &jl); err != nil { ++ logrus.Errorf("failed to decode %q to json: %v", text, err) ++ return ++ } ++ logrus.Errorf("error from child %s", jl.Msg) ++} +diff --git a/libcontainer/nsenter/nsexec.c b/libcontainer/nsenter/nsexec.c +index 4f73b1a1..8e059e09 100644 +--- a/libcontainer/nsenter/nsexec.c ++++ b/libcontainer/nsenter/nsexec.c +@@ -77,6 +77,8 @@ struct nlconfig_t { + size_t oom_score_adj_len; + }; + ++int logfd; ++ + /* + * List of netlink message types sent to us as part of bootstrapping the init. + * These constants are defined in libcontainer/message_linux.go. +@@ -111,6 +113,26 @@ int setns(int fd, int nstype) + } + #endif + ++void write_log_with_info(const char *level, const char *function, int line, const char *format, ...) ++{ ++ static char message[1024]; ++ va_list args; ++ ++ if (logfd < 0 || level == NULL) ++ return; ++ ++ va_start(args, format); ++ if (vsnprintf(message, 1024, format, args) < 0) ++ return; ++ va_end(args); ++ ++ if (dprintf(logfd, "{\"level\":\"%s\", \"msg\": \"%s:%d %s\"}\n", level, function, line, message) < 0) ++ return; ++} ++ ++#define logerr(fmt, ...) \ ++ write_log_with_info("error", __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) ++ + /* XXX: This is ugly. */ + static int syncfd = -1; + +@@ -118,13 +140,13 @@ static int syncfd = -1; + #define bail(fmt, ...) \ + do { \ + int ret = __COUNTER__ + 1; \ +- fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \ ++ logerr("nsenter: " fmt ": %m", ##__VA_ARGS__); \ + if (syncfd >= 0) { \ + enum sync_t s = SYNC_ERR; \ + if (write(syncfd, &s, sizeof(s)) != sizeof(s)) \ +- fprintf(stderr, "nsenter: failed: write(s)"); \ ++ logerr("nsenter: failed: write(s)"); \ + if (write(syncfd, &ret, sizeof(ret)) != sizeof(ret)) \ +- fprintf(stderr, "nsenter: failed: write(ret)"); \ ++ logerr("nsenter: failed: write(ret)"); \ + } \ + exit(ret); \ + } while(0) +@@ -259,6 +281,24 @@ static int initpipe(void) + return pipenum; + } + ++static void setup_logpipe(void) ++{ ++ char *logpipe, *endptr; ++ ++ logpipe = getenv("_LIBCONTAINER_LOGPIPE"); ++ if (logpipe == NULL || *logpipe == '\0') { ++ logfd = -1; ++ return; ++ } ++ ++ logfd = strtol(logpipe, &endptr, 10); ++ if (logpipe == endptr || *endptr != '\0') { ++ fprintf(stderr, "unable to parse _LIBCONTAINER_LOGPIPE, value: %s\n", logpipe); ++ /* It is too early to use bail */ ++ exit(1); ++ } ++} ++ + /* Returns the clone(2) flag for a namespace, given the name of a namespace. */ + static int nsflag(char *name) + { +@@ -442,6 +482,12 @@ void nsexec(void) + int sync_child_pipe[2], sync_grandchild_pipe[2]; + struct nlconfig_t config = {0}; + ++ /* ++ * Setup a pipe to send logs to the parent. This should happen ++ * first, because bail will use that pipe. ++ */ ++ setup_logpipe(); ++ + /* + * If we don't have an init pipe, just return to the go routine. + * We'll only get an init pipe for start or exec. +@@ -867,6 +913,7 @@ void nsexec(void) + /* Free netlink data. */ + nl_free(&config); + ++ close(logfd); + /* Finish executing, let the Go runtime take over. */ + return; + } +diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go +index 25fe30b5..0c5cd47c 100644 +--- a/libcontainer/process_linux.go ++++ b/libcontainer/process_linux.go +@@ -18,6 +18,7 @@ import ( + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/system" + "github.com/opencontainers/runc/libcontainer/utils" ++ "github.com/opencontainers/runc/libcontainer/logs" + "golang.org/x/sys/unix" + ) + +@@ -73,6 +74,7 @@ func (p *setnsProcess) start() (err error) { + defer p.parentPipe.Close() + err = p.cmd.Start() + p.childPipe.Close() ++ logs.CloseChild() + if err != nil { + return newSystemErrorWithCause(err, "starting setns process") + } +@@ -256,6 +258,7 @@ func (p *initProcess) start() error { + p.process.ops = p + p.childPipe.Close() + p.rootDir.Close() ++ logs.CloseChild() + if err != nil { + p.process.ops = nil + return newSystemErrorWithCause(err, "starting init process command") +-- +2.27.0 + diff --git a/patch/0129-runc-improve-log-for-debugging.patch b/patch/0129-runc-improve-log-for-debugging.patch new file mode 100644 index 0000000000000000000000000000000000000000..11bb7275616d51df80e989ef29b40d83bab4e4e1 --- /dev/null +++ b/patch/0129-runc-improve-log-for-debugging.patch @@ -0,0 +1,297 @@ +From da07a376d48d2d589f8ce5669f93450da4f01521 Mon Sep 17 00:00:00 2001 +From: xiadanni +Date: Mon, 25 Oct 2021 15:57:42 +0800 +Subject: [PATCH] runc: improve log for debugging + +add following logs for debugging +1. print pid and memory cgroup information when container init process + start fail. +2. improve error return in execSetns() +3. using logpipe to support for logging from child process +4. add log when init() finished in child process for debug + +Signed-off-by: xiadanni +--- + libcontainer/factory_linux.go | 13 +++++++++---- + libcontainer/init_linux.go | 4 +++- + libcontainer/logs/logs.go | 2 +- + libcontainer/nsenter/nsexec.c | 1 - + libcontainer/process_linux.go | 10 +++++----- + libcontainer/setns_init_linux.go | 5 +++++ + libcontainer/standard_init_linux.go | 18 ++++++++++++++---- + main.go | 6 +++++- + main_unix.go | 10 ++++++++++ + 9 files changed, 52 insertions(+), 17 deletions(-) + +diff --git a/libcontainer/factory_linux.go b/libcontainer/factory_linux.go +index fe9ce242..e4ef5184 100644 +--- a/libcontainer/factory_linux.go ++++ b/libcontainer/factory_linux.go +@@ -4,15 +4,15 @@ package libcontainer + + import ( + "encoding/json" ++ "errors" + "fmt" ++ "io/ioutil" + "os" + "path/filepath" + "regexp" + "runtime/debug" + "strconv" + "syscall" +- "io/ioutil" +- "errors" + + "github.com/docker/docker/pkg/mount" + "github.com/opencontainers/runc/libcontainer/cgroups" +@@ -281,6 +281,11 @@ func (l *LinuxFactory) StartInitialization() (err error) { + defer consoleSocket.Close() + } + ++ logPipeFd, err2 := strconv.Atoi(os.Getenv("_LIBCONTAINER_LOGPIPE")) ++ if err2 != nil { ++ logPipeFd = 0 ++ } ++ + // clear the current process's environment to clean any libcontainer + // specific env vars. + os.Clearenv() +@@ -303,7 +308,7 @@ func (l *LinuxFactory) StartInitialization() (err error) { + } + }() + +- i, err := newContainerInit(it, pipe, consoleSocket, rootfd) ++ i, err := newContainerInit(it, pipe, consoleSocket, rootfd, logPipeFd) + if err != nil { + return err + } +@@ -346,7 +351,7 @@ func (l *LinuxFactory) updateStateCapabilites(compatState *CompatState, configPa + var memSize int64 = int64(memorySwappiness) + if memSize < 0 { + memSize = 0 +- var memUSize uint64 = uint64(memSize-1) ++ var memUSize uint64 = uint64(memSize - 1) + compatState.Config.Cgroups.MemorySwappiness = &memUSize + needUpdate = true + } +diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go +index ee632b4c..e9a83e9b 100644 +--- a/libcontainer/init_linux.go ++++ b/libcontainer/init_linux.go +@@ -66,7 +66,7 @@ type initer interface { + Init() error + } + +-func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDirFD int) (initer, error) { ++func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDirFD, logFd int) (initer, error) { + var config *initConfig + if err := json.NewDecoder(pipe).Decode(&config); err != nil { + return nil, err +@@ -81,6 +81,7 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDi + pipe: pipe, + consoleSocket: consoleSocket, + config: config, ++ logFd: logFd, + }, nil + case initStandard: + return &linuxStandardInit{ +@@ -89,6 +90,7 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, stateDi + parentPid: syscall.Getppid(), + config: config, + stateDirFD: stateDirFD, ++ logFd: logFd, + }, nil + } + return nil, fmt.Errorf("unknown init type %q", t) +diff --git a/libcontainer/logs/logs.go b/libcontainer/logs/logs.go +index 219fe382..408a1480 100644 +--- a/libcontainer/logs/logs.go ++++ b/libcontainer/logs/logs.go +@@ -64,5 +64,5 @@ func processEntry(text []byte) { + logrus.Errorf("failed to decode %q to json: %v", text, err) + return + } +- logrus.Errorf("error from child %s", jl.Msg) ++ logrus.Infof("log from child: %s", jl.Msg) + } +diff --git a/libcontainer/nsenter/nsexec.c b/libcontainer/nsenter/nsexec.c +index 8e059e09..0075b6e4 100644 +--- a/libcontainer/nsenter/nsexec.c ++++ b/libcontainer/nsenter/nsexec.c +@@ -913,7 +913,6 @@ void nsexec(void) + /* Free netlink data. */ + nl_free(&config); + +- close(logfd); + /* Finish executing, let the Go runtime take over. */ + return; + } +diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go +index 0c5cd47c..5cdc30c4 100644 +--- a/libcontainer/process_linux.go ++++ b/libcontainer/process_linux.go +@@ -16,9 +16,9 @@ import ( + "github.com/Sirupsen/logrus" + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/configs" ++ "github.com/opencontainers/runc/libcontainer/logs" + "github.com/opencontainers/runc/libcontainer/system" + "github.com/opencontainers/runc/libcontainer/utils" +- "github.com/opencontainers/runc/libcontainer/logs" + "golang.org/x/sys/unix" + ) + +@@ -137,7 +137,7 @@ func (p *setnsProcess) execSetns() error { + } + if !status.Success() { + p.cmd.Wait() +- return newSystemError(&exec.ExitError{ProcessState: status}) ++ return newSystemErrorWithCause(&exec.ExitError{ProcessState: status}, "getting setns process status") + } + var pid *pid + if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil { +@@ -224,16 +224,16 @@ func (p *initProcess) execSetns() error { + status, err := p.cmd.Process.Wait() + if err != nil { + p.cmd.Wait() +- return err ++ return newSystemErrorWithCause(err, "waiting on setns process to finish") + } + if !status.Success() { + p.cmd.Wait() +- return &exec.ExitError{ProcessState: status} ++ return newSystemErrorWithCause(&exec.ExitError{ProcessState: status}, "getting setns process status") + } + var pid *pid + if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil { + p.cmd.Wait() +- return err ++ return newSystemErrorWithCause(err, "reading pid from init pipe") + } + + // Clean up the zombie parent process +diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go +index b3fab219..1f7ec98b 100644 +--- a/libcontainer/setns_init_linux.go ++++ b/libcontainer/setns_init_linux.go +@@ -21,6 +21,7 @@ type linuxSetnsInit struct { + pipe *os.File + consoleSocket *os.File + config *initConfig ++ logFd int + } + + func (l *linuxSetnsInit) getSessionRingName() string { +@@ -68,5 +69,9 @@ func (l *linuxSetnsInit) Init() error { + if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil { + return err + } ++ if l.logFd != 0 { ++ syscall.Close(l.logFd) ++ } ++ + return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) + } +diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go +index b985180a..53c81e9f 100644 +--- a/libcontainer/standard_init_linux.go ++++ b/libcontainer/standard_init_linux.go +@@ -27,6 +27,7 @@ type linuxStandardInit struct { + parentPid int + stateDirFD int + config *initConfig ++ logFd int + } + + func (l *linuxStandardInit) getSessionRingParams() (string, uint32, uint32) { +@@ -181,6 +182,10 @@ func (l *linuxStandardInit) Init() error { + // close the pipe to signal that we have completed our init. + l.pipe.Close() + ++ if l.logFd != 0 { ++ syscall.Close(l.logFd) ++ } ++ + // wait for the fifo to be opened on the other side before + // exec'ing the users process. + ch := make(chan Error, 1) +@@ -222,13 +227,18 @@ func (l *linuxStandardInit) Init() error { + } + + func printCgroupInfo(path string) { ++ cgroupRoot := "/sys/fs/cgroup" + infoFileList := []string{ + "/proc/meminfo", + "/sys/fs/cgroup/memory/memory.stat", +- filepath.Join("/sys/fs/cgroup/files", path, "/files.limit"), +- filepath.Join("/sys/fs/cgroup/files", path, "/files.usage"), +- filepath.Join("/sys/fs/cgroup/memory", path, "/memory.stat"), +- filepath.Join("/sys/fs/cgroup/cpu", path, "/cpu.stat"), ++ filepath.Join(cgroupRoot, "files", path, "files.limit"), ++ filepath.Join(cgroupRoot, "files", path, "files.usage"), ++ filepath.Join(cgroupRoot, "pids", path, "pids.max"), ++ filepath.Join(cgroupRoot, "pids", path, "pids.current"), ++ filepath.Join(cgroupRoot, "memory", path, "memory.usage_in_bytes"), ++ filepath.Join(cgroupRoot, "memory", path, "memory.limit_in_bytes"), ++ filepath.Join(cgroupRoot, "memory", path, "memory.stat"), ++ filepath.Join(cgroupRoot, "cpu", path, "cpu.stat"), + } + for _, file := range infoFileList { + printFileContent(file) +diff --git a/main.go b/main.go +index 04762424..4141ec56 100644 +--- a/main.go ++++ b/main.go +@@ -10,7 +10,7 @@ import ( + "time" + + "github.com/Sirupsen/logrus" +- "github.com/Sirupsen/logrus/hooks/syslog" ++ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" + ) +@@ -118,6 +118,10 @@ func main() { + updateCommand, + } + app.Before = func(context *cli.Context) error { ++ if logrus.StandardLogger().Out != logrus.New().Out { ++ return nil ++ } ++ + if path := context.GlobalString("log"); path != "" { + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) + if err != nil { +diff --git a/main_unix.go b/main_unix.go +index 56904e08..45e6df61 100644 +--- a/main_unix.go ++++ b/main_unix.go +@@ -6,7 +6,9 @@ import ( + "fmt" + "os" + "runtime" ++ "strconv" + ++ "github.com/Sirupsen/logrus" + "github.com/opencontainers/runc/libcontainer" + _ "github.com/opencontainers/runc/libcontainer/nsenter" + "github.com/urfave/cli" +@@ -16,6 +18,14 @@ func init() { + if len(os.Args) > 1 && os.Args[1] == "init" { + runtime.GOMAXPROCS(1) + runtime.LockOSThread() ++ ++ logPipeFd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_LOGPIPE")) ++ if err != nil { ++ return ++ } ++ logrus.SetOutput(os.NewFile(uintptr(logPipeFd), "logpipe")) ++ logrus.SetFormatter(new(logrus.JSONFormatter)) ++ logrus.Info("child process init-function finished") + } + } + +-- +2.27.0 + diff --git a/patch/0130-runc-fix-cgroup-info-print-error.patch b/patch/0130-runc-fix-cgroup-info-print-error.patch new file mode 100644 index 0000000000000000000000000000000000000000..f0353744cda6c2da88e6ed8e6b468d444432caf8 --- /dev/null +++ b/patch/0130-runc-fix-cgroup-info-print-error.patch @@ -0,0 +1,50 @@ +From 107de8857b41b5ac3c2d1230383e3855fac872de Mon Sep 17 00:00:00 2001 +From: xiadanni +Date: Tue, 7 Dec 2021 20:40:52 +0800 +Subject: [PATCH] runc: fix cgroup info print error + +reason: still using syslog hook to print logrus in create-init, +as logPipe will be closed before printCgroupInfo() called, cgroup info +could not be printed by logPipe. + +Signed-off-by: xiadanni +--- + main_unix.go | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/main_unix.go b/main_unix.go +index 45e6df61..0355b276 100644 +--- a/main_unix.go ++++ b/main_unix.go +@@ -19,13 +19,14 @@ func init() { + runtime.GOMAXPROCS(1) + runtime.LockOSThread() + +- logPipeFd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_LOGPIPE")) +- if err != nil { +- return ++ if initType := os.Getenv("_LIBCONTAINER_INITTYPE"); initType == "setns" { ++ logPipeFd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_LOGPIPE")) ++ if err != nil { ++ return ++ } ++ logrus.SetOutput(os.NewFile(uintptr(logPipeFd), "logpipe")) ++ logrus.SetFormatter(new(logrus.JSONFormatter)) + } +- logrus.SetOutput(os.NewFile(uintptr(logPipeFd), "logpipe")) +- logrus.SetFormatter(new(logrus.JSONFormatter)) +- logrus.Info("child process init-function finished") + } + } + +@@ -33,6 +34,7 @@ var initCommand = cli.Command{ + Name: "init", + Usage: `initialize the namespaces and launch the process (do not call it outside of runc)`, + Action: func(context *cli.Context) error { ++ logrus.Info("child process init-command start") + factory, _ := libcontainer.New("") + if err := factory.StartInitialization(); err != nil { + fmt.Fprintf(os.Stderr, "libcontainer: container start initialization failed: %s", err) +-- +2.27.0 + diff --git a/runc-openeuler.spec b/runc-openeuler.spec index 9c99806bb340580f85e2e09d338a77295f76726e..6e1d701bf845152fa2a4157506f13ea0d04a90a0 100644 --- a/runc-openeuler.spec +++ b/runc-openeuler.spec @@ -4,7 +4,7 @@ Name: docker-runc Version: 1.0.0.rc3 -Release: 114 +Release: 301 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -53,6 +53,19 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Tue Aug 09 2022 zhongjiawei - 1.0.0.rc3-301 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:sync bugfix, include + 1. add check in spec + 2. add mount destination validation(fix CVE-2021-30465) + 3. fix backport patch apply ignored + 4. optimize nsexec logging + 5. improve log for debugging + 6. fix cgroup info print error + 7. fix CVE-2022-29162 + * Thu Dec 23 2021 xiadanni - 1.0.0.rc3-114 - Type:bugfix - CVE:NA diff --git a/series.conf b/series.conf index 7c53b831dbf42f553eb6a207c434614711b91b01..b5d1f52501356d19218110641b7ed97b21081993 100644 --- a/series.conf +++ b/series.conf @@ -1,126 +1,132 @@ -0001-.travis.yml-Don-t-require-FETCH_HEAD.patch -0002-Don-t-try-to-read-freezer.state-from-the-cu.patch -0003-Use-opencontainers-selinux-package.patch -0004-handle-unprivileged-operations-and-dumpable.patch -0005-runc-add-support-for-rootless-containers.patch -0006-rootless-add-rootless-cgroup-manager.patch -0007-libcontainer-configs-add-proper-HostUID-and.patch -0008-libcontainer-init-fix-unmapped-console-fcho.patch -0009-rootless-add-autogenerated-rootless-config-.patch -0010-integration-added-root-requires.patch -0011-tests-add-rootless-integration-tests.patch -0012-vendor-add-golang.org-x-sys-unix-9a7256cb28.patch -0013-libcontainer-rewrite-cmsg-to-use-sys-unix.patch -0014-Set-container-state-only-once-during-start.patch -0015-checkpoint-check-if-system-supports-pre-dum.patch -0016-Fix-console-syscalls.patch -0017-restore-apply-resource-limits.patch -0018-could-load-a-stopped-container.patch -0019-Revert-back-to-using-sbin.patch -0020-add-testcase-in-generic_error_test.go.patch -0021-Fix-misspelling-of-properties-in-various-pl.patch -0022-Add-a-rootless-containers-section-on-README.patch -0023-vendor-clean-up-to-be-better-written.patch -0024-Optimizing-looping-over-namespaces.patch -0025-Add-a-rootless-section-to-spec-man-page-and.patch -0026-Allow-updating-container-pids-limit.patch -0027-Remove-redundant-declaraion-of-namespace-sl.patch -0028-Revert-saneTerminal.patch -0029-vendor-runtime-spec-fork-docker-runtime-spe.patch -0030-Update-memory-specs-to-use-int64-not-uint64.patch -0031-Add-spec-for-euleros.patch -0032-runc-17-Always-save-own-namespace-paths.patch -0033-runc-change-runc-default-umask-to-027.patch -0034-runc-17-Add-some-compatibility-code-to-surpor.patch -0035-runc-17-Add-root-to-HookState-for-compatibili.patch -0036-runc-17-add-compatibility-for-docker-1.11.2.patch -0037-docker-Don-t-enalbe-kmem-accounting-by-defa.patch -0039-Fix-unittest-and-integration-test-error-cause.patch -0041-Add-timeout-for-syscall.Openat.patch -0042-update-state-earlier-to-avoid-cgroup-leak-whe.patch -0043-runc-Use-rslave-instead-of-rprivate-in-chro.patch -0044-runc-default-mount-propagation-correctly.patch -0045-runc-add-hook-specific-info-when-error-occurr.patch -0046-runc-print-cgroup-info-if-cpuset-missing-occu.patch -0047-runc-add-more-specific-log-for-hooks.patch -0048-runc-Only-configure-networking.patch -0049-cgroups-fs-fix-NPE-on-Destroy-than-no-cgrou.patch -0050-runc-Avoid-race-when-opening-exec-fifo.patch -0051-runc-Return-from-goroutine-when-it-should-t.patch -0052-runc-reduce-max-number-of-retries-to-10.patch -0053-runc-print-error-message-during-start-into-co.patch -0054-runc-ignore-exec.fifo-removing-not-exist-erro.patch -0055-Add-file-fds-limit.patch -0056-runc-Modify-max-files.limit-to-max-because-of.patch -0057-runc-change-read-value-of-cgroup-files.limit-.patch -0058-runc-fix-panic-when-Linux-is-nil.patch -0059-Fix-setup-cgroup-before-prestart-hook.patch -0060-runc-runc-logs-forwarding-to-syslog.patch -0061-runc-17-change-golang-build-version-to-make-o.patch -0062-runc-Check-the-hook-timeout-in-case-overflow.patch -0063-docker-close-openchan-immediately-to-avoid-er.patch -0064-runc-bump-to-v1.0.0.rc3.4-after-normalization.patch -0065-runc-support-namespaced-kernel-params-can-be-.patch -0066-runc-bump-to-v1.0.0.rc3.6.patch -0067-runc-make-the-runc-log-more-useful.patch -0068-runc-reduced-the-same-log-when-the-hook-exect.patch -0069-runc-Change-Files-to-LinuxFiles-for-file-limi.patch -0070-runc-not-print-no-such-file-when-cli-err.patch -0071-runc-revert-Change-Files-to-LinuxFiles-for-fi.patch -0072-Revert-runc-not-print-no-such-file-when-cli-e.patch -0073-runc-fix-state.json-no-such-file-or-directory.patch -0074-runc-fix-check-sysctl-in-host-network-mode.patch -0075-runc-Fix-systemd-journald-service-dependency.patch -0076-runc-Fix-syslog-hook-bug.patch -0077-runc-Require-libseccomp-static-lib-for-upgrade-f.patch -0078-runc-Fix-race-in-runc-exec.patch -0079-runc-modify-spec-file-for-upgrade.patch -0080-runc-support-specify-umask.patch -0081-runc-fix-oom-killer-disable-unhandled-due-t.patch -0082-runc-make-runc-spec-and-docker-18.9-compati.patch -0083-log-fix-runc-log-decode-failed.patch -0084-oci-fix-runc-panic-and-support-oom-score.patch -0085-runc-do-not-setup-sysctl-in-runc-when-userns-.patch -0086-runc-support-set-seccomp-priority.patch -0087-runc-fix-spec-LinuxSyscall-struct.patch -0088-nsenter-clone-proc-self-exe-to-avoid-exposi.patch -0089-Revert-nsenter-clone-proc-self-exe-to-avoid.patch -0090-nsenter-clone-proc-self-exe-to-avoid-exposi.patch -0091-runc-cve-2019-5736-workaround-if-memfd_create.patch -0092-runc-cve-2019-5736-fix-build-failure.patch -0093-runc-fix-error-when-check-the-init-process.patch -0094-runc-If-tmp-is-mounted-by-option-noexec-docke.patch -0095-runc-just-warning-when-poststart-and-poststop.patch -0096-runc-do-not-kill-container-if-poststart-hooks.patch -0097-runc-Fix-mountpoint-leak-and-pivot_root-error.patch -0098-runc-fix-read-only-containers-under-userns-.patch -0099-runc-enable-bep-ldflags.patch -0100-runc-set-makefile-buildid.patch -0101-runc-print-memory-info-when-syscall.Exec-fail.patch -0102-runc-add-sysctl-kernel.pid_max-to-whitelist.patch -0104-runc-Retry-adding-pids-to-cgroups-when-EINV.patch -0105-runc-disable-core-dump-during-pipe-io.patch -0106-runc-do-not-override-devices.allow-file-when-.patch -0107-runc-fix-exec-problem-caused-by-libseccomp-up.patch -0108-runc-print-files-limit-and-usage-when-exec-fa.patch -0109-runc-add-copyright.patch -0110-runc-add-lisence.patch -0111-runc-add-log-message-for-cgroup-file-check.patch -0112-runc-add-log-message-for-cgroup-file-check.patch -0113-runc-modify-files-cgroup-info-reading-path.patch -0112-runc-Fixes-1585-config.Namespaces-is-empty-.patch -0113-runc-Write-freezer-state-after-every-state-.patch -0114-runc-may-kill-other-process-when-container-.patch -0115-runc-Fix-cgroup-hugetlb-size-prefix-for-kB.patch -0116-runc-check-nil-pointers-in-cgroup-manager.patch -0117-runc-Pass-back-the-pid-of-runc-1-CHILD-so-w.patch -0118-runc-don-t-deny-all-devices-when-update-cgroup-resou.patch -0118-runc-rootfs-do-not-permit-proc-mounts-to-no.patch -0119-runc-use-git-commit-to-store-commit-ID.patch -0120-runc-fix-permission-denied.patch -0121-runc-add-sys-symbol-to-support-riscv.patch -0122-runc-add-riscv-on-existing-files.patch -0121-runc-add-cpu-and-memory-info-when-print-cgroup-info.patch -0124-runc-fix-freezing-race.patch -0125-runc-compile-option-compliance.patch +patch/0001-.travis.yml-Don-t-require-FETCH_HEAD.patch +patch/0002-Don-t-try-to-read-freezer.state-from-the-cu.patch +patch/0003-Use-opencontainers-selinux-package.patch +patch/0004-handle-unprivileged-operations-and-dumpable.patch +patch/0005-runc-add-support-for-rootless-containers.patch +patch/0006-rootless-add-rootless-cgroup-manager.patch +patch/0007-libcontainer-configs-add-proper-HostUID-and.patch +patch/0008-libcontainer-init-fix-unmapped-console-fcho.patch +patch/0009-rootless-add-autogenerated-rootless-config-.patch +patch/0010-integration-added-root-requires.patch +patch/0011-tests-add-rootless-integration-tests.patch +patch/0012-vendor-add-golang.org-x-sys-unix-9a7256cb28.patch +patch/0013-libcontainer-rewrite-cmsg-to-use-sys-unix.patch +patch/0014-Set-container-state-only-once-during-start.patch +patch/0015-checkpoint-check-if-system-supports-pre-dum.patch +patch/0016-Fix-console-syscalls.patch +patch/0017-restore-apply-resource-limits.patch +patch/0018-could-load-a-stopped-container.patch +patch/0019-Revert-back-to-using-sbin.patch +patch/0020-add-testcase-in-generic_error_test.go.patch +patch/0021-Fix-misspelling-of-properties-in-various-pl.patch +patch/0022-Add-a-rootless-containers-section-on-README.patch +patch/0023-vendor-clean-up-to-be-better-written.patch +patch/0024-Optimizing-looping-over-namespaces.patch +patch/0025-Add-a-rootless-section-to-spec-man-page-and.patch +patch/0026-Allow-updating-container-pids-limit.patch +patch/0027-Remove-redundant-declaraion-of-namespace-sl.patch +patch/0028-Revert-saneTerminal.patch +patch/0029-vendor-runtime-spec-fork-docker-runtime-spe.patch +patch/0030-Update-memory-specs-to-use-int64-not-uint64.patch +patch/0031-Add-spec-for-euleros.patch +patch/0032-runc-17-Always-save-own-namespace-paths.patch +patch/0033-runc-change-runc-default-umask-to-027.patch +patch/0034-runc-17-Add-some-compatibility-code-to-surpor.patch +patch/0035-runc-17-Add-root-to-HookState-for-compatibili.patch +patch/0036-runc-17-add-compatibility-for-docker-1.11.2.patch +patch/0037-docker-Don-t-enalbe-kmem-accounting-by-defa.patch +patch/0039-Fix-unittest-and-integration-test-error-cause.patch +patch/0041-Add-timeout-for-syscall.Openat.patch +patch/0042-update-state-earlier-to-avoid-cgroup-leak-whe.patch +patch/0043-runc-Use-rslave-instead-of-rprivate-in-chro.patch +patch/0044-runc-default-mount-propagation-correctly.patch +patch/0045-runc-add-hook-specific-info-when-error-occurr.patch +patch/0046-runc-print-cgroup-info-if-cpuset-missing-occu.patch +patch/0047-runc-add-more-specific-log-for-hooks.patch +patch/0048-runc-Only-configure-networking.patch +patch/0049-cgroups-fs-fix-NPE-on-Destroy-than-no-cgrou.patch +patch/0050-runc-Avoid-race-when-opening-exec-fifo.patch +patch/0051-runc-Return-from-goroutine-when-it-should-t.patch +patch/0052-runc-reduce-max-number-of-retries-to-10.patch +patch/0053-runc-print-error-message-during-start-into-co.patch +patch/0054-runc-ignore-exec.fifo-removing-not-exist-erro.patch +patch/0055-Add-file-fds-limit.patch +patch/0056-runc-Modify-max-files.limit-to-max-because-of.patch +patch/0057-runc-change-read-value-of-cgroup-files.limit-.patch +patch/0058-runc-fix-panic-when-Linux-is-nil.patch +patch/0059-Fix-setup-cgroup-before-prestart-hook.patch +patch/0060-runc-runc-logs-forwarding-to-syslog.patch +patch/0061-runc-17-change-golang-build-version-to-make-o.patch +patch/0062-runc-Check-the-hook-timeout-in-case-overflow.patch +patch/0063-docker-close-openchan-immediately-to-avoid-er.patch +patch/0064-runc-bump-to-v1.0.0.rc3.4-after-normalization.patch +patch/0065-runc-support-namespaced-kernel-params-can-be-.patch +patch/0066-runc-bump-to-v1.0.0.rc3.6.patch +patch/0067-runc-make-the-runc-log-more-useful.patch +patch/0068-runc-reduced-the-same-log-when-the-hook-exect.patch +patch/0069-runc-Change-Files-to-LinuxFiles-for-file-limi.patch +patch/0070-runc-not-print-no-such-file-when-cli-err.patch +patch/0071-runc-revert-Change-Files-to-LinuxFiles-for-fi.patch +patch/0072-Revert-runc-not-print-no-such-file-when-cli-e.patch +patch/0073-runc-fix-state.json-no-such-file-or-directory.patch +patch/0074-runc-fix-check-sysctl-in-host-network-mode.patch +patch/0075-runc-Fix-systemd-journald-service-dependency.patch +patch/0076-runc-Fix-syslog-hook-bug.patch +patch/0077-runc-Require-libseccomp-static-lib-for-upgrade-f.patch +patch/0078-runc-Fix-race-in-runc-exec.patch +patch/0079-runc-modify-spec-file-for-upgrade.patch +patch/0080-runc-support-specify-umask.patch +patch/0081-runc-fix-oom-killer-disable-unhandled-due-t.patch +patch/0082-runc-make-runc-spec-and-docker-18.9-compati.patch +patch/0083-log-fix-runc-log-decode-failed.patch +patch/0084-oci-fix-runc-panic-and-support-oom-score.patch +patch/0085-runc-do-not-setup-sysctl-in-runc-when-userns-.patch +patch/0086-runc-support-set-seccomp-priority.patch +patch/0087-runc-fix-spec-LinuxSyscall-struct.patch +patch/0088-nsenter-clone-proc-self-exe-to-avoid-exposi.patch +patch/0089-Revert-nsenter-clone-proc-self-exe-to-avoid.patch +patch/0090-nsenter-clone-proc-self-exe-to-avoid-exposi.patch +patch/0091-runc-cve-2019-5736-workaround-if-memfd_create.patch +patch/0092-runc-cve-2019-5736-fix-build-failure.patch +patch/0093-runc-fix-error-when-check-the-init-process.patch +patch/0094-runc-If-tmp-is-mounted-by-option-noexec-docke.patch +patch/0095-runc-just-warning-when-poststart-and-poststop.patch +patch/0096-runc-do-not-kill-container-if-poststart-hooks.patch +patch/0097-runc-Fix-mountpoint-leak-and-pivot_root-error.patch +patch/0098-runc-fix-read-only-containers-under-userns-.patch +patch/0099-runc-enable-bep-ldflags.patch +patch/0100-runc-set-makefile-buildid.patch +patch/0101-runc-print-memory-info-when-syscall.Exec-fail.patch +patch/0102-runc-add-sysctl-kernel.pid_max-to-whitelist.patch +patch/0104-runc-Retry-adding-pids-to-cgroups-when-EINV.patch +patch/0105-runc-disable-core-dump-during-pipe-io.patch +patch/0106-runc-do-not-override-devices.allow-file-when-.patch +patch/0107-runc-fix-exec-problem-caused-by-libseccomp-up.patch +patch/0108-runc-print-files-limit-and-usage-when-exec-fa.patch +patch/0109-runc-add-copyright.patch +patch/0110-runc-add-lisence.patch +patch/0111-runc-add-log-message-for-cgroup-file-check.patch +patch/0112-runc-add-log-message-for-cgroup-file-check.patch +patch/0113-runc-modify-files-cgroup-info-reading-path.patch +patch/0112-runc-Fixes-1585-config.Namespaces-is-empty-.patch +patch/0113-runc-Write-freezer-state-after-every-state-.patch +patch/0114-runc-may-kill-other-process-when-container-.patch +patch/0115-runc-Fix-cgroup-hugetlb-size-prefix-for-kB.patch +patch/0116-runc-check-nil-pointers-in-cgroup-manager.patch +patch/0117-runc-Pass-back-the-pid-of-runc-1-CHILD-so-w.patch +patch/0118-runc-don-t-deny-all-devices-when-update-cgroup-resou.patch +patch/0118-runc-rootfs-do-not-permit-proc-mounts-to-no.patch +patch/0119-runc-use-git-commit-to-store-commit-ID.patch +patch/0120-runc-fix-permission-denied.patch +patch/0121-runc-add-sys-symbol-to-support-riscv.patch +patch/0122-runc-add-riscv-on-existing-files.patch +patch/0121-runc-add-cpu-and-memory-info-when-print-cgroup-info.patch +patch/0124-runc-fix-freezing-race.patch +patch/0125-runc-compile-option-compliance.patch +patch/0126-runc-add-check-in-spec.patch +patch/0127-runc-add-mount-destination-validation-fix-CVE-2021.patch +patch/0128-runc-optimize-nsexec-logging.patch +patch/0129-runc-improve-log-for-debugging.patch +patch/0130-runc-fix-cgroup-info-print-error.patch +patch/0128-runc-fix-CVE-2022-29162.patch #end