Merge pull request #27459 from TomSweeneyRedHat/dev/tsweeney/cve-2025-53881-main

Bump to runc v1.3.3 - CVE-2025-52881
This commit is contained in:
openshift-merge-bot[bot]
2025-11-07 18:09:09 +00:00
committed by GitHub
12 changed files with 434 additions and 123 deletions

2
go.mod
View File

@@ -144,7 +144,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/runc v1.3.2 // indirect
github.com/opencontainers/runc v1.3.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.9 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect

4
go.sum
View File

@@ -306,8 +306,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runc v1.3.2 h1:GUwgo0Fx9M/pl2utaSYlJfdBcXAB/CZXDxe322lvJ3Y=
github.com/opencontainers/runc v1.3.2/go.mod h1:F7UQQEsxcjUNnFpT1qPLHZBKYP7yWwk6hq8suLy9cl0=
github.com/opencontainers/runc v1.3.3 h1:qlmBbbhu+yY0QM7jqfuat7M1H3/iXjju3VkP9lkFQr4=
github.com/opencontainers/runc v1.3.3/go.mod h1:D7rL72gfWxVs9cJ2/AayxB0Hlvn9g0gaF1R7uunumSI=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20250523060157-0ea5ed0382a2 h1:2xZEHOdeQBV6PW8ZtimN863bIOl7OCW/X10K0cnxKeA=

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2024-2025 SUSE LLC
*
* 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 pathrs provides wrappers around filepath-securejoin to add the
// minimum set of features needed from libpathrs that are not provided by
// filepath-securejoin, with the eventual goal being that these can be used to
// ease the transition by converting them stubs when enabling libpathrs builds.
package pathrs

View File

@@ -0,0 +1,99 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2024-2025 SUSE LLC
*
* 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 pathrs
import (
"fmt"
"os"
"path/filepath"
"github.com/cyphar/filepath-securejoin/pathrs-lite"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
// MkdirAllInRootOpen attempts to make
//
// path, _ := securejoin.SecureJoin(root, unsafePath)
// os.MkdirAll(path, mode)
// os.Open(path)
//
// safer against attacks where components in the path are changed between
// SecureJoin returning and MkdirAll (or Open) being called. In particular, we
// try to detect any symlink components in the path while we are doing the
// MkdirAll.
//
// NOTE: If unsafePath is a subpath of root, we assume that you have already
// called SecureJoin and so we use the provided path verbatim without resolving
// any symlinks (this is done in a way that avoids symlink-exchange races).
// This means that the path also must not contain ".." elements, otherwise an
// error will occur.
//
// This uses (pathrs-lite).MkdirAllHandle under the hood, but it has special
// handling if unsafePath has already been scoped within the rootfs (this is
// needed for a lot of runc callers and fixing this would require reworking a
// lot of path logic).
func MkdirAllInRootOpen(root, unsafePath string, mode os.FileMode) (*os.File, error) {
// If the path is already "within" the root, get the path relative to the
// root and use that as the unsafe path. This is necessary because a lot of
// MkdirAllInRootOpen callers have already done SecureJoin, and refactoring
// all of them to stop using these SecureJoin'd paths would require a fair
// amount of work.
// TODO(cyphar): Do the refactor to libpathrs once it's ready.
if IsLexicallyInRoot(root, unsafePath) {
subPath, err := filepath.Rel(root, unsafePath)
if err != nil {
return nil, err
}
unsafePath = subPath
}
// Check for any silly mode bits.
if mode&^0o7777 != 0 {
return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode)
}
// Linux (and thus os.MkdirAll) silently ignores the suid and sgid bits if
// passed. While it would make sense to return an error in that case (since
// the user has asked for a mode that won't be applied), for compatibility
// reasons we have to ignore these bits.
if ignoredBits := mode &^ 0o1777; ignoredBits != 0 {
logrus.Warnf("MkdirAll called with no-op mode bits that are ignored by Linux: 0o%.3o", ignoredBits)
mode &= 0o1777
}
rootDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
if err != nil {
return nil, fmt.Errorf("open root handle: %w", err)
}
defer rootDir.Close()
return retryEAGAIN(func() (*os.File, error) {
return pathrs.MkdirAllHandle(rootDir, unsafePath, mode)
})
}
// MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the
// returned handle, for callers that don't need to use it.
func MkdirAllInRoot(root, unsafePath string, mode os.FileMode) error {
f, err := MkdirAllInRootOpen(root, unsafePath, mode)
if err == nil {
_ = f.Close()
}
return err
}

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2024-2025 SUSE LLC
*
* 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 pathrs
import (
"strings"
)
// IsLexicallyInRoot is shorthand for strings.HasPrefix(path+"/", root+"/"),
// but properly handling the case where path or root have a "/" suffix.
//
// NOTE: The return value only make sense if the path is already mostly cleaned
// (i.e., doesn't contain "..", ".", nor unneeded "/"s).
func IsLexicallyInRoot(root, path string) bool {
root = strings.TrimRight(root, "/")
path = strings.TrimRight(path, "/")
return strings.HasPrefix(path+"/", root+"/")
}

View File

@@ -0,0 +1,108 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2025 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2025 SUSE LLC
*
* 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 pathrs
import (
"fmt"
"os"
"github.com/cyphar/filepath-securejoin/pathrs-lite"
"github.com/cyphar/filepath-securejoin/pathrs-lite/procfs"
)
func procOpenReopen(openFn func(subpath string) (*os.File, error), subpath string, flags int) (*os.File, error) {
handle, err := retryEAGAIN(func() (*os.File, error) {
return openFn(subpath)
})
if err != nil {
return nil, err
}
defer handle.Close()
f, err := Reopen(handle, flags)
if err != nil {
return nil, fmt.Errorf("reopen %s: %w", handle.Name(), err)
}
return f, nil
}
// ProcSelfOpen is a wrapper around [procfs.Handle.OpenSelf] and
// [pathrs.Reopen], to let you one-shot open a procfs file with the given
// flags.
func ProcSelfOpen(subpath string, flags int) (*os.File, error) {
proc, err := retryEAGAIN(procfs.OpenProcRoot)
if err != nil {
return nil, err
}
defer proc.Close()
return procOpenReopen(proc.OpenSelf, subpath, flags)
}
// ProcPidOpen is a wrapper around [procfs.Handle.OpenPid] and [pathrs.Reopen],
// to let you one-shot open a procfs file with the given flags.
func ProcPidOpen(pid int, subpath string, flags int) (*os.File, error) {
proc, err := retryEAGAIN(procfs.OpenProcRoot)
if err != nil {
return nil, err
}
defer proc.Close()
return procOpenReopen(func(subpath string) (*os.File, error) {
return proc.OpenPid(pid, subpath)
}, subpath, flags)
}
// ProcThreadSelfOpen is a wrapper around [procfs.Handle.OpenThreadSelf] and
// [pathrs.Reopen], to let you one-shot open a procfs file with the given
// flags. The returned [procfs.ProcThreadSelfCloser] needs the same handling as
// when using pathrs-lite.
func ProcThreadSelfOpen(subpath string, flags int) (_ *os.File, _ procfs.ProcThreadSelfCloser, Err error) {
proc, err := retryEAGAIN(procfs.OpenProcRoot)
if err != nil {
return nil, nil, err
}
defer proc.Close()
handle, closer, err := retryEAGAIN2(func() (*os.File, procfs.ProcThreadSelfCloser, error) {
return proc.OpenThreadSelf(subpath)
})
if err != nil {
return nil, nil, err
}
if closer != nil {
defer func() {
if Err != nil {
closer()
}
}()
}
defer handle.Close()
f, err := Reopen(handle, flags)
if err != nil {
return nil, nil, fmt.Errorf("reopen %s: %w", handle.Name(), err)
}
return f, closer, nil
}
// Reopen is a wrapper around pathrs.Reopen.
func Reopen(file *os.File, flags int) (*os.File, error) {
return retryEAGAIN(func() (*os.File, error) {
return pathrs.Reopen(file, flags)
})
}

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2024-2025 SUSE LLC
*
* 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 pathrs
import (
"errors"
"fmt"
"time"
"golang.org/x/sys/unix"
)
// Based on >50k tests running "runc run" on a 16-core system with very heavy
// rename(2) load, the single longest latency caused by -EAGAIN retries was
// ~800us (with the vast majority being closer to 400us). So, a 2ms limit
// should give more than enough headroom for any real system in practice.
const retryDeadline = 2 * time.Millisecond
// retryEAGAIN is a top-level retry loop for pathrs to try to returning
// spurious errors in most normal user cases when using openat2 (libpathrs
// itself does up to 128 retries already, but this method takes a
// wallclock-deadline approach to simply retry until a timer elapses).
func retryEAGAIN[T any](fn func() (T, error)) (T, error) {
deadline := time.After(retryDeadline)
for {
v, err := fn()
if !errors.Is(err, unix.EAGAIN) {
return v, err
}
select {
case <-deadline:
return *new(T), fmt.Errorf("%v retry deadline exceeded: %w", retryDeadline, err)
default:
// retry
}
}
}
// retryEAGAIN2 is like retryEAGAIN except it returns two values.
func retryEAGAIN2[T1, T2 any](fn func() (T1, T2, error)) (T1, T2, error) {
type ret struct {
v1 T1
v2 T2
}
v, err := retryEAGAIN(func() (ret, error) {
v1, v2, err := fn()
return ret{v1: v1, v2: v2}, err
})
return v.v1, v.v2, err
}

View File

@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2024-2025 SUSE LLC
*
* 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 pathrs
import (
"fmt"
"os"
"path/filepath"
"github.com/cyphar/filepath-securejoin/pathrs-lite"
"golang.org/x/sys/unix"
)
// OpenInRoot opens the given path inside the root with the provided flags. It
// is effectively shorthand for [securejoin.OpenInRoot] followed by
// [securejoin.Reopen].
func OpenInRoot(root, subpath string, flags int) (*os.File, error) {
handle, err := retryEAGAIN(func() (*os.File, error) {
return pathrs.OpenInRoot(root, subpath)
})
if err != nil {
return nil, err
}
defer handle.Close()
return Reopen(handle, flags)
}
// CreateInRoot creates a new file inside a root (as well as any missing parent
// directories) and returns a handle to said file. This effectively has
// open(O_CREAT|O_NOFOLLOW) semantics. If you want the creation to use O_EXCL,
// include it in the passed flags. The fileMode argument uses unix.* mode bits,
// *not* os.FileMode.
func CreateInRoot(root, subpath string, flags int, fileMode uint32) (*os.File, error) {
dir, filename := filepath.Split(subpath)
if filepath.Join("/", filename) == "/" {
return nil, fmt.Errorf("create in root subpath %q has bad trailing component %q", subpath, filename)
}
dirFd, err := MkdirAllInRootOpen(root, dir, 0o755)
if err != nil {
return nil, err
}
defer dirFd.Close()
// We know that the filename does not have any "/" components, and that
// dirFd is inside the root. O_NOFOLLOW will stop us from following
// trailing symlinks, so this is safe to do. libpathrs's Root::create_file
// works the same way.
flags |= unix.O_CREAT | unix.O_NOFOLLOW
fd, err := unix.Openat(int(dirFd.Fd()), filename, flags, fileMode)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), root+"/"+subpath), nil
}

View File

@@ -6,6 +6,9 @@ import (
"os"
"sync"
"golang.org/x/sys/unix"
"github.com/opencontainers/runc/internal/pathrs"
"github.com/opencontainers/runc/libcontainer/utils"
)
@@ -36,19 +39,13 @@ func setProcAttr(attr, value string) error {
// Under AppArmor you can only change your own attr, so there's no reason
// to not use /proc/thread-self/ (instead of /proc/<tid>/, like libapparmor
// does).
attrPath, closer := utils.ProcThreadSelf(attrSubPath)
defer closer()
f, err := os.OpenFile(attrPath, os.O_WRONLY, 0)
f, closer, err := pathrs.ProcThreadSelfOpen(attrSubPath, unix.O_WRONLY|unix.O_CLOEXEC)
if err != nil {
return err
}
defer closer()
defer f.Close()
if err := utils.EnsureProcHandle(f); err != nil {
return err
}
_, err = f.WriteString(value)
return err
}

View File

@@ -65,11 +65,11 @@ func CleanPath(path string) string {
return path
}
// stripRoot returns the passed path, stripping the root path if it was
// 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 {
func StripRoot(root, path string) string {
// Make the paths clean and absolute.
root, path = CleanPath("/"+root), CleanPath("/"+path)
switch {
@@ -111,5 +111,5 @@ func Annotations(labels []string) (bundle string, userAnnotations map[string]str
userAnnotations[name] = value
}
}
return
return bundle, userAnnotations
}

View File

@@ -9,27 +9,15 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
_ "unsafe" // for go:linkname
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/opencontainers/runc/internal/pathrs"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
// EnsureProcHandle returns whether or not the given file handle is on procfs.
func EnsureProcHandle(fh *os.File) error {
var buf unix.Statfs_t
if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil {
return fmt.Errorf("ensure %s is on procfs: %w", fh.Name(), err)
}
if buf.Type != unix.PROC_SUPER_MAGIC {
return fmt.Errorf("%s is not on procfs", fh.Name())
}
return nil
}
var (
haveCloseRangeCloexecBool bool
haveCloseRangeCloexecOnce sync.Once
@@ -59,19 +47,13 @@ type fdFunc func(fd int)
// fdRangeFrom calls the passed fdFunc for each file descriptor that is open in
// the current process.
func fdRangeFrom(minFd int, fn fdFunc) error {
procSelfFd, closer := ProcThreadSelf("fd")
defer closer()
fdDir, err := os.Open(procSelfFd)
fdDir, closer, err := pathrs.ProcThreadSelfOpen("fd/", unix.O_DIRECTORY|unix.O_CLOEXEC)
if err != nil {
return err
return fmt.Errorf("get handle to /proc/thread-self/fd: %w", err)
}
defer closer()
defer fdDir.Close()
if err := EnsureProcHandle(fdDir); err != nil {
return err
}
fdList, err := fdDir.Readdirnames(-1)
if err != nil {
return err
@@ -170,8 +152,8 @@ func NewSockPair(name string) (parent, child *os.File, err error) {
// 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)
unsafePath = StripRoot(root, unsafePath)
fullPath, err := securejoin.SecureJoin(root, unsafePath)
if err != nil {
return fmt.Errorf("resolving path inside rootfs failed: %w", err)
}
@@ -180,7 +162,7 @@ func WithProcfd(root, unsafePath string, fn func(procfd string) error) error {
defer closer()
// Open the target path.
fh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0)
fh, err := os.OpenFile(fullPath, unix.O_PATH|unix.O_CLOEXEC, 0)
if err != nil {
return fmt.Errorf("open o_path procfd: %w", err)
}
@@ -190,13 +172,24 @@ func WithProcfd(root, unsafePath string, fn func(procfd string) error) error {
// Double-check the path is the one we expected.
if realpath, err := os.Readlink(procfd); err != nil {
return fmt.Errorf("procfd verification failed: %w", err)
} else if realpath != path {
} else if realpath != fullPath {
return fmt.Errorf("possibly malicious path detected -- refusing to operate on %s", realpath)
}
return fn(procfd)
}
// WithProcfdFile is a very minimal wrapper around [ProcThreadSelfFd], intended
// to make migrating from [WithProcfd] and [WithProcfdPath] usage easier. The
// caller is responsible for making sure that the provided file handle is
// actually safe to operate on.
func WithProcfdFile(file *os.File, fn func(procfd string) error) error {
fdpath, closer := ProcThreadSelfFd(file.Fd())
defer closer()
return fn(fdpath)
}
type ProcThreadSelfCloser func()
var (
@@ -268,88 +261,6 @@ func ProcThreadSelfFd(fd uintptr) (string, ProcThreadSelfCloser) {
return ProcThreadSelf("fd/" + strconv.FormatUint(uint64(fd), 10))
}
// IsLexicallyInRoot is shorthand for strings.HasPrefix(path+"/", root+"/"),
// but properly handling the case where path or root are "/".
//
// NOTE: The return value only make sense if the path doesn't contain "..".
func IsLexicallyInRoot(root, path string) bool {
if root != "/" {
root += "/"
}
if path != "/" {
path += "/"
}
return strings.HasPrefix(path, root)
}
// MkdirAllInRootOpen attempts to make
//
// path, _ := securejoin.SecureJoin(root, unsafePath)
// os.MkdirAll(path, mode)
// os.Open(path)
//
// safer against attacks where components in the path are changed between
// SecureJoin returning and MkdirAll (or Open) being called. In particular, we
// try to detect any symlink components in the path while we are doing the
// MkdirAll.
//
// NOTE: If unsafePath is a subpath of root, we assume that you have already
// called SecureJoin and so we use the provided path verbatim without resolving
// any symlinks (this is done in a way that avoids symlink-exchange races).
// This means that the path also must not contain ".." elements, otherwise an
// error will occur.
//
// This uses securejoin.MkdirAllHandle under the hood, but it has special
// handling if unsafePath has already been scoped within the rootfs (this is
// needed for a lot of runc callers and fixing this would require reworking a
// lot of path logic).
func MkdirAllInRootOpen(root, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
// If the path is already "within" the root, get the path relative to the
// root and use that as the unsafe path. This is necessary because a lot of
// MkdirAllInRootOpen callers have already done SecureJoin, and refactoring
// all of them to stop using these SecureJoin'd paths would require a fair
// amount of work.
// TODO(cyphar): Do the refactor to libpathrs once it's ready.
if IsLexicallyInRoot(root, unsafePath) {
subPath, err := filepath.Rel(root, unsafePath)
if err != nil {
return nil, err
}
unsafePath = subPath
}
// Check for any silly mode bits.
if mode&^0o7777 != 0 {
return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode)
}
// Linux (and thus os.MkdirAll) silently ignores the suid and sgid bits if
// passed. While it would make sense to return an error in that case (since
// the user has asked for a mode that won't be applied), for compatibility
// reasons we have to ignore these bits.
if ignoredBits := mode &^ 0o1777; ignoredBits != 0 {
logrus.Warnf("MkdirAll called with no-op mode bits that are ignored by Linux: 0o%.3o", ignoredBits)
mode &= 0o1777
}
rootDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
if err != nil {
return nil, fmt.Errorf("open root handle: %w", err)
}
defer rootDir.Close()
return securejoin.MkdirAllHandle(rootDir, unsafePath, mode)
}
// MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the
// returned handle, for callers that don't need to use it.
func MkdirAllInRoot(root, unsafePath string, mode os.FileMode) error {
f, err := MkdirAllInRootOpen(root, unsafePath, mode)
if err == nil {
_ = f.Close()
}
return err
}
// Openat is a Go-friendly openat(2) wrapper.
func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) {
dirFd := unix.AT_FDCWD

3
vendor/modules.txt vendored
View File

@@ -544,8 +544,9 @@ github.com/opencontainers/go-digest
## explicit; go 1.18
github.com/opencontainers/image-spec/specs-go
github.com/opencontainers/image-spec/specs-go/v1
# github.com/opencontainers/runc v1.3.2
# github.com/opencontainers/runc v1.3.3
## explicit; go 1.23.0
github.com/opencontainers/runc/internal/pathrs
github.com/opencontainers/runc/libcontainer/apparmor
github.com/opencontainers/runc/libcontainer/devices
github.com/opencontainers/runc/libcontainer/utils