mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
dmz: add fallbacks to handle noexec for O_TMPFILE and mktemp()
Previously, if /var/run was mounted noexec, our cloned binary logic would not work if memfd_create(2) was not available because we would try to exec a binary that is on a noexec filesystem. We cannot guarantee there will be an executable filesystem on the system (other than mounting one ourselves, which would cause a bunch of other headaches) but we can at least try the obvious options (/tmp, /bin, and /). If none of these work, we will have to fail. Reported-by: lifubang <lifubang@acmcoder.com> Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -19,6 +20,23 @@ var (
|
||||
_ SealFunc = sealFile
|
||||
)
|
||||
|
||||
func isExecutable(f *os.File) bool {
|
||||
if err := unix.Faccessat(int(f.Fd()), "", unix.X_OK, unix.AT_EACCESS|unix.AT_EMPTY_PATH); err == nil {
|
||||
return true
|
||||
} else if err == unix.EACCES {
|
||||
return false
|
||||
}
|
||||
path := "/proc/self/fd/" + strconv.Itoa(int(f.Fd()))
|
||||
if err := unix.Access(path, unix.X_OK); err == nil {
|
||||
return true
|
||||
} else if err == unix.EACCES {
|
||||
return false
|
||||
}
|
||||
// Cannot check -- assume it's executable (if not, exec will fail).
|
||||
logrus.Debugf("cannot do X_OK check on binary %s -- assuming it's executable", f.Name())
|
||||
return true
|
||||
}
|
||||
|
||||
const baseMemfdSeals = unix.F_SEAL_SEAL | unix.F_SEAL_SHRINK | unix.F_SEAL_GROW | unix.F_SEAL_WRITE
|
||||
|
||||
func sealMemfd(f **os.File) error {
|
||||
@@ -106,15 +124,46 @@ func getSealableFile(comment, tmpDir string) (file *os.File, sealFn SealFunc, er
|
||||
return
|
||||
}
|
||||
logrus.Debugf("memfd cloned binary failed, falling back to O_TMPFILE: %v", err)
|
||||
|
||||
// The tmpDir here (c.root) might be mounted noexec, so we need a couple of
|
||||
// fallbacks to try. It's possible that none of these are writable and
|
||||
// executable, in which case there's nothing we can practically do (other
|
||||
// than mounting our own executable tmpfs, which would have its own
|
||||
// issues).
|
||||
tmpDirs := []string{
|
||||
tmpDir,
|
||||
os.TempDir(),
|
||||
"/tmp",
|
||||
".",
|
||||
"/bin",
|
||||
"/",
|
||||
}
|
||||
|
||||
// Try to fallback to O_TMPFILE (supported since Linux 3.11).
|
||||
file, sealFn, err = otmpfile(tmpDir)
|
||||
if err == nil {
|
||||
for _, dir := range tmpDirs {
|
||||
file, sealFn, err = otmpfile(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !isExecutable(file) {
|
||||
logrus.Debugf("tmpdir %s is noexec -- trying a different tmpdir", dir)
|
||||
file.Close()
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
logrus.Debugf("O_TMPFILE cloned binary failed, falling back to mktemp(): %v", err)
|
||||
// Finally, try a classic unlinked temporary file.
|
||||
file, sealFn, err = mktemp(tmpDir)
|
||||
if err == nil {
|
||||
for _, dir := range tmpDirs {
|
||||
file, sealFn, err = mktemp(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !isExecutable(file) {
|
||||
logrus.Debugf("tmpdir %s is noexec -- trying a different tmpdir", dir)
|
||||
file.Close()
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
return nil, nil, fmt.Errorf("could not create sealable file for cloned binary: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user