From 431b8bb4d8dde8b6c96a280c950829481d617de2 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 25 Mar 2025 14:36:42 -0700 Subject: [PATCH] int/linux: add/use Getwd Signed-off-by: Kir Kolyshkin --- internal/linux/eintr.go | 10 ++++++++++ internal/linux/linux.go | 6 ++++++ libcontainer/init_linux.go | 5 +++-- libcontainer/specconv/spec_linux.go | 20 +++++--------------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/internal/linux/eintr.go b/internal/linux/eintr.go index 4b3dd5197..36a6e3e29 100644 --- a/internal/linux/eintr.go +++ b/internal/linux/eintr.go @@ -16,3 +16,13 @@ func retryOnEINTR(fn func() error) error { } } } + +// retryOnEINTR2 is like retryOnEINTR, but it returns 2 values. +func retryOnEINTR2[T any](fn func() (T, error)) (T, error) { + for { + val, err := fn() + if !errors.Is(err, unix.EINTR) { + return val, err + } + } +} diff --git a/internal/linux/linux.go b/internal/linux/linux.go index d5ce74d2a..efcd985d4 100644 --- a/internal/linux/linux.go +++ b/internal/linux/linux.go @@ -6,6 +6,12 @@ import ( "golang.org/x/sys/unix" ) +// Getwd wraps [unix.Getwd]. +func Getwd() (wd string, err error) { + wd, err = retryOnEINTR2(unix.Getwd) + return wd, os.NewSyscallError("getwd", err) +} + // Sendmsg wraps [unix.Sendmsg]. func Sendmsg(fd int, p, oob []byte, to unix.Sockaddr, flags int) error { err := retryOnEINTR(func() error { diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index ef7b0cf13..4546af9da 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -20,6 +20,7 @@ import ( "golang.org/x/sys/unix" "github.com/opencontainers/cgroups" + "github.com/opencontainers/runc/internal/linux" "github.com/opencontainers/runc/libcontainer/capabilities" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/system" @@ -280,10 +281,10 @@ func verifyCwd() error { // details, and CVE-2024-21626 for the security issue that motivated this // check. // - // We have to use unix.Getwd() here because os.Getwd() has a workaround for + // We do not use os.Getwd() here because it has a workaround for // $PWD which involves doing stat(.), which can fail if the current // directory is inaccessible to the container process. - if wd, err := unix.Getwd(); errors.Is(err, unix.ENOENT) { + if wd, err := linux.Getwd(); errors.Is(err, unix.ENOENT) { return errors.New("current working directory is outside of container mount namespace root -- possible container breakout detected") } else if err != nil { return fmt.Errorf("failed to verify if current working directory is safe: %w", err) diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index be1e3d8ea..37068da7f 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -16,6 +16,7 @@ import ( dbus "github.com/godbus/dbus/v5" "github.com/opencontainers/cgroups" devices "github.com/opencontainers/cgroups/devices/config" + "github.com/opencontainers/runc/internal/linux" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/internal/userns" "github.com/opencontainers/runc/libcontainer/seccomp" @@ -344,24 +345,13 @@ type CreateOpts struct { RootlessCgroups bool } -// getwd is a wrapper similar to os.Getwd, except it always gets -// the value from the kernel, which guarantees the returned value -// to be absolute and clean. -func getwd() (wd string, err error) { - for { - wd, err = unix.Getwd() - if err != unix.EINTR { - break - } - } - return wd, os.NewSyscallError("getwd", err) -} - // CreateLibcontainerConfig creates a new libcontainer configuration from a // given specification and a cgroup name func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) { - // runc's cwd will always be the bundle path - cwd, err := getwd() + // Runc's cwd will always be the bundle path. + // Use the value from the kernel, which guarantees the returned value + // to be absolute and clean. + cwd, err := linux.Getwd() if err != nil { return nil, err }