mirror of
https://github.com/opencontainers/runc.git
synced 2026-02-06 03:45:41 +01:00
Fix error from runc run on noexec fs
When starting a new container, and the very last step of executing of a user process fails (last lines of (*linuxStandardInit).Init), it is too late to print a proper error since both the log pipe and the init pipe are closed. This is partially mitigated by using exec.LookPath() which is supposed to say whether we will be able to execute or not. Alas, it fails to do so when the binary to be executed resides on a filesystem mounted with noexec flag. A workaround would be to use access(2) with X_OK flag. Alas, it is not working when runc itself is a setuid (or setgid) binary. In this case, faccessat2(2) with AT_EACCESS can be used, but it is only available since Linux v5.8. So, use faccessat2(2) with AT_EACCESS if available. If not, fall back to access(2) for non-setuid runc, and do nothing for setuid runc (as there is nothing we can do). Note that this check if in addition to whatever exec.LookPath does. Fixes https://github.com/opencontainers/runc/issues/3520 Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
@@ -198,6 +198,13 @@ func (l *linuxStandardInit) Init() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// exec.LookPath might return no error for an executable residing on a
|
||||
// file system mounted with noexec flag, so perform this extra check
|
||||
// now while we can still return a proper error.
|
||||
if err := system.Eaccess(name); err != nil {
|
||||
return &os.PathError{Op: "exec", Path: name, Err: err}
|
||||
}
|
||||
|
||||
// Set seccomp as close to execve as possible, so as few syscalls take
|
||||
// place afterward (reducing the amount of syscalls that users need to
|
||||
// enable in their seccomp profiles). However, this needs to be done
|
||||
|
||||
@@ -31,6 +31,25 @@ func (p ParentDeathSignal) Set() error {
|
||||
return SetParentDeathSignal(uintptr(p))
|
||||
}
|
||||
|
||||
// Eaccess is similar to unix.Access except for setuid/setgid binaries
|
||||
// it checks against the effective (rather than real) uid and gid.
|
||||
func Eaccess(path string) error {
|
||||
err := unix.Faccessat2(unix.AT_FDCWD, path, unix.X_OK, unix.AT_EACCESS)
|
||||
if err != unix.ENOSYS && err != unix.EPERM { //nolint:errorlint // unix errors are bare
|
||||
return err
|
||||
}
|
||||
|
||||
// Faccessat2() not available; check if we are a set[ug]id binary.
|
||||
if os.Getuid() == os.Geteuid() && os.Getgid() == os.Getegid() {
|
||||
// For a non-set[ug]id binary, use access(2).
|
||||
return unix.Access(path, unix.X_OK)
|
||||
}
|
||||
|
||||
// For a setuid/setgid binary, there is no fallback way
|
||||
// so assume we can execute the binary.
|
||||
return nil
|
||||
}
|
||||
|
||||
func Execv(cmd string, args []string, env []string) error {
|
||||
name, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user