From 7563a8f06de34cbfda74e9b01a5a1c5af6ec14e1 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 10 Nov 2021 17:36:48 -0800 Subject: [PATCH] libct: wrap more unix errors When I tried to start a rootless container under a different/wrong user, I got: $ ../runc/runc --systemd-cgroup --root /tmp/runc.$$ run 445 ERRO[0000] runc run failed: operation not permitted This is obviously not good enough. With this commit, the error is: ERRO[0000] runc run failed: fchown fd 9: operation not permitted Alas, there are still some code that returns unwrapped errnos from various unix calls. This is a followup to commit d8ba4128b299a8 which wrapped many, but not all, bare unix errors. Do wrap some more, using either os.PathError or os.SyscallError. While at it, - use os.SyscallError instead of os.NewSyscallError; - use errors.Is(err, os.ErrXxx) instead of os.IsXxx(err). Signed-off-by: Kir Kolyshkin --- libcontainer/init_linux.go | 11 ++++++----- libcontainer/process_linux.go | 2 +- libcontainer/rootfs_linux.go | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index 935b7356c..e6e1fd6b5 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -8,6 +8,7 @@ import ( "io" "net" "os" + "strconv" "strings" "unsafe" @@ -377,7 +378,7 @@ func setupUser(config *initConfig) error { if allowSupGroups { suppGroups := append(execUser.Sgids, addGroups...) if err := unix.Setgroups(suppGroups); err != nil { - return err + return &os.SyscallError{Syscall: "setgroups", Err: err} } } @@ -403,7 +404,7 @@ func setupUser(config *initConfig) error { func fixStdioPermissions(config *initConfig, u *user.ExecUser) error { var null unix.Stat_t if err := unix.Stat("/dev/null", &null); err != nil { - return err + return &os.PathError{Op: "stat", Path: "/dev/null", Err: err} } for _, fd := range []uintptr{ os.Stdin.Fd(), @@ -412,7 +413,7 @@ func fixStdioPermissions(config *initConfig, u *user.ExecUser) error { } { var s unix.Stat_t if err := unix.Fstat(int(fd), &s); err != nil { - return err + return &os.PathError{Op: "fstat", Path: "fd " + strconv.Itoa(int(fd)), Err: err} } // Skip chown of /dev/null if it was used as one of the STDIO fds. @@ -438,7 +439,7 @@ func fixStdioPermissions(config *initConfig, u *user.ExecUser) error { if err == unix.EINVAL || err == unix.EPERM { continue } - return err + return &os.PathError{Op: "fchown", Path: "fd " + strconv.Itoa(int(fd)), Err: err} } } return nil @@ -518,7 +519,7 @@ func isWaitable(pid int) (bool, error) { si := &siginfo{} _, _, e := unix.Syscall6(unix.SYS_WAITID, _P_PID, uintptr(pid), uintptr(unsafe.Pointer(si)), unix.WEXITED|unix.WNOWAIT|unix.WNOHANG, 0, 0) if e != 0 { - return false, os.NewSyscallError("waitid", e) + return false, &os.SyscallError{Syscall: "waitid", Err: e} } return si.si_pid != 0, nil diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index ed361028a..3913e3855 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -793,7 +793,7 @@ func (p *Process) InitializeIO(rootuid, rootgid int) (i *IO, err error) { // change ownership of the pipes in case we are in a user namespace for _, fd := range fds { if err := unix.Fchown(int(fd), rootuid, rootgid); err != nil { - return nil, err + return nil, &os.PathError{Op: "fchown", Path: "fd " + strconv.Itoa(int(fd)), Err: err} } } return i, nil diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index 9578e4428..63322d01d 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -641,7 +641,7 @@ func reOpenDevNull() error { } for fd := 0; fd < 3; fd++ { if err := unix.Fstat(fd, &stat); err != nil { - return err + return &os.PathError{Op: "fstat", Path: "fd " + strconv.Itoa(fd), Err: err} } if stat.Rdev == devNullStat.Rdev { // Close and re-open the fd. @@ -709,9 +709,9 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error { return bindMountDeviceNode(rootfs, dest, node) } if err := mknodDevice(dest, node); err != nil { - if os.IsExist(err) { + if errors.Is(err, os.ErrExist) { return nil - } else if os.IsPermission(err) { + } else if errors.Is(err, os.ErrPermission) { return bindMountDeviceNode(rootfs, dest, node) } return err @@ -736,9 +736,9 @@ func mknodDevice(dest string, node *devices.Device) error { return err } if err := unix.Mknod(dest, uint32(fileMode), int(dev)); err != nil { - return err + return &os.PathError{Op: "mknod", Path: dest, Err: err} } - return unix.Chown(dest, int(node.Uid), int(node.Gid)) + return os.Chown(dest, int(node.Uid), int(node.Gid)) } // Get the parent mount point of directory passed in as argument. Also return