2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
// Package loader loads an executable file into a MemoryManager.
|
2018-04-27 17:37:02 +00:00
|
|
|
package loader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-01-08 20:56:59 +00:00
|
|
|
"fmt"
|
2018-04-27 17:37:02 +00:00
|
|
|
"io"
|
|
|
|
"path"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/abi"
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
2020-01-27 23:17:58 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/context"
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/cpuid"
|
|
|
|
"gvisor.dev/gvisor/pkg/rand"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/arch"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs"
|
2020-02-14 19:11:55 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/mm"
|
2020-02-14 19:11:55 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/syserr"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
2020-01-27 23:17:58 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/usermem"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
// LoadArgs holds specifications for an executable file to be loaded.
|
|
|
|
type LoadArgs struct {
|
|
|
|
// MemoryManager is the memory manager to load the executable into.
|
|
|
|
MemoryManager *mm.MemoryManager
|
|
|
|
|
|
|
|
// RemainingTraversals is the maximum number of symlinks to follow to
|
|
|
|
// resolve Filename. This counter is passed by reference to keep it
|
|
|
|
// updated throughout the call stack.
|
|
|
|
RemainingTraversals *uint
|
|
|
|
|
|
|
|
// ResolveFinal indicates whether the final link of Filename should be
|
|
|
|
// resolved, if it is a symlink.
|
|
|
|
ResolveFinal bool
|
|
|
|
|
|
|
|
// Filename is the path for the executable.
|
|
|
|
Filename string
|
|
|
|
|
|
|
|
// File is an open fs.File object of the executable. If File is not
|
|
|
|
// nil, then File will be loaded and Filename will be ignored.
|
2020-02-14 19:11:55 +00:00
|
|
|
//
|
|
|
|
// The caller is responsible for checking that the user can execute this file.
|
|
|
|
File fsbridge.File
|
|
|
|
|
|
|
|
// Opener is used to open the executable file when 'File' is nil.
|
|
|
|
Opener fsbridge.Lookup
|
2019-10-26 05:31:35 +00:00
|
|
|
|
2019-10-29 17:03:18 +00:00
|
|
|
// CloseOnExec indicates that the executable (or one of its parent
|
|
|
|
// directories) was opened with O_CLOEXEC. If the executable is an
|
|
|
|
// interpreter script, then cause an ENOENT error to occur, since the
|
|
|
|
// script would otherwise be inaccessible to the interpreter.
|
|
|
|
CloseOnExec bool
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
// Argv is the vector of arguments to pass to the executable.
|
|
|
|
Argv []string
|
|
|
|
|
|
|
|
// Envv is the vector of environment variables to pass to the
|
|
|
|
// executable.
|
|
|
|
Envv []string
|
|
|
|
|
|
|
|
// Features specifies the CPU feature set for the executable.
|
|
|
|
Features *cpuid.FeatureSet
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// readFull behaves like io.ReadFull for an *fs.File.
|
|
|
|
func readFull(ctx context.Context, f *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
|
|
|
|
var total int64
|
|
|
|
for dst.NumBytes() > 0 {
|
|
|
|
n, err := f.Preadv(ctx, dst, offset+total)
|
|
|
|
total += n
|
|
|
|
if err == io.EOF && total != 0 {
|
|
|
|
return total, io.ErrUnexpectedEOF
|
|
|
|
} else if err != nil {
|
|
|
|
return total, err
|
|
|
|
}
|
|
|
|
dst = dst.DropFirst64(n)
|
|
|
|
}
|
|
|
|
return total, nil
|
|
|
|
}
|
|
|
|
|
2019-10-29 19:50:03 +00:00
|
|
|
// openPath opens args.Filename and checks that it is valid for loading.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
2019-10-29 19:50:03 +00:00
|
|
|
// openPath returns an *fs.Dirent and *fs.File for args.Filename, which is not
|
|
|
|
// installed in the Task FDTable. The caller takes ownership of both.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
2019-10-26 05:31:35 +00:00
|
|
|
// args.Filename must be a readable, executable, regular file.
|
2020-02-14 19:11:55 +00:00
|
|
|
func openPath(ctx context.Context, args LoadArgs) (fsbridge.File, error) {
|
2019-10-26 05:31:35 +00:00
|
|
|
if args.Filename == "" {
|
2018-07-03 18:27:29 +00:00
|
|
|
ctx.Infof("cannot open empty name")
|
2020-02-14 19:11:55 +00:00
|
|
|
return nil, syserror.ENOENT
|
2019-07-30 18:19:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 19:11:55 +00:00
|
|
|
// TODO(gvisor.dev/issue/160): Linux requires only execute permission,
|
|
|
|
// not read. However, our backing filesystems may prevent us from reading
|
|
|
|
// the file without read permission. Additionally, a task with a
|
|
|
|
// non-readable executable has additional constraints on access via
|
|
|
|
// ptrace and procfs.
|
|
|
|
opts := vfs.OpenOptions{
|
|
|
|
Flags: linux.O_RDONLY,
|
|
|
|
FileExec: true,
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2020-02-14 19:11:55 +00:00
|
|
|
return args.Opener.OpenPath(ctx, args.Filename, opts, args.RemainingTraversals, args.ResolveFinal)
|
2019-10-29 19:50:03 +00:00
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-10-29 19:50:03 +00:00
|
|
|
// checkIsRegularFile prevents us from trying to execute a directory, pipe, etc.
|
2020-02-14 19:11:55 +00:00
|
|
|
func checkIsRegularFile(ctx context.Context, file fsbridge.File, filename string) error {
|
|
|
|
t, err := file.Type(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2020-02-14 19:11:55 +00:00
|
|
|
if t != linux.ModeRegular {
|
|
|
|
ctx.Infof("%q is not a regular file: %v", filename, t)
|
2019-07-30 18:19:18 +00:00
|
|
|
return syserror.EACCES
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-07-30 18:19:18 +00:00
|
|
|
return nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// allocStack allocates and maps a stack in to any available part of the address space.
|
|
|
|
func allocStack(ctx context.Context, m *mm.MemoryManager, a arch.Context) (*arch.Stack, error) {
|
|
|
|
ar, err := m.MapStack(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &arch.Stack{a, m, ar.End}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// maxLoaderAttempts is the maximum number of attempts to try to load
|
2018-05-07 23:37:08 +00:00
|
|
|
// an interpreter scripts, to prevent loops. 6 (initial + 5 changes) is
|
2018-04-27 17:37:02 +00:00
|
|
|
// what the Linux kernel allows (fs/exec.c:search_binary_handler).
|
|
|
|
maxLoaderAttempts = 6
|
|
|
|
)
|
|
|
|
|
2020-02-14 19:11:55 +00:00
|
|
|
// loadExecutable loads an executable that is pointed to by args.File. The
|
|
|
|
// caller is responsible for checking that the user can execute this file.
|
|
|
|
// If nil, the path args.Filename is resolved and loaded (check that the user
|
|
|
|
// can execute this file is done here in this case). If the executable is an
|
2019-10-26 05:31:35 +00:00
|
|
|
// interpreter script rather than an ELF, the binary of the corresponding
|
|
|
|
// interpreter will be loaded.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// It returns:
|
|
|
|
// * loadedELF, description of the loaded binary
|
|
|
|
// * arch.Context matching the binary arch
|
|
|
|
// * fs.Dirent of the binary file
|
2019-10-26 05:31:35 +00:00
|
|
|
// * Possibly updated args.Argv
|
2020-02-14 19:11:55 +00:00
|
|
|
func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, arch.Context, fsbridge.File, []string, error) {
|
2018-04-27 17:37:02 +00:00
|
|
|
for i := 0; i < maxLoaderAttempts; i++ {
|
2019-10-26 05:31:35 +00:00
|
|
|
if args.File == nil {
|
2020-02-14 19:11:55 +00:00
|
|
|
var err error
|
|
|
|
args.File, err = openPath(ctx, args)
|
|
|
|
if err != nil {
|
|
|
|
ctx.Infof("Error opening %s: %v", args.Filename, err)
|
|
|
|
return loadedELF{}, nil, nil, nil, err
|
2019-10-29 19:50:03 +00:00
|
|
|
}
|
2020-02-14 19:11:55 +00:00
|
|
|
// Ensure file is release in case the code loops or errors out.
|
|
|
|
defer args.File.DecRef()
|
2019-07-30 18:19:18 +00:00
|
|
|
} else {
|
2020-02-14 19:11:55 +00:00
|
|
|
if err := checkIsRegularFile(ctx, args.File, args.Filename); err != nil {
|
|
|
|
return loadedELF{}, nil, nil, nil, err
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check the header. Is this an ELF or interpreter script?
|
|
|
|
var hdr [4]uint8
|
|
|
|
// N.B. We assume that reading from a regular file cannot block.
|
2020-02-14 19:11:55 +00:00
|
|
|
_, err := args.File.ReadFull(ctx, usermem.BytesIOSequence(hdr[:]), 0)
|
2019-10-29 19:50:03 +00:00
|
|
|
// Allow unexpected EOF, as a valid executable could be only three bytes
|
|
|
|
// (e.g., #!a).
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil && err != io.ErrUnexpectedEOF {
|
|
|
|
if err == io.EOF {
|
|
|
|
err = syserror.ENOEXEC
|
|
|
|
}
|
|
|
|
return loadedELF{}, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case bytes.Equal(hdr[:], []byte(elfMagic)):
|
2019-10-26 05:31:35 +00:00
|
|
|
loaded, ac, err := loadELF(ctx, args)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
|
|
|
ctx.Infof("Error loading ELF: %v", err)
|
|
|
|
return loadedELF{}, nil, nil, nil, err
|
|
|
|
}
|
2020-02-14 19:11:55 +00:00
|
|
|
// An ELF is always terminal. Hold on to file.
|
|
|
|
args.File.IncRef()
|
|
|
|
return loaded, ac, args.File, args.Argv, err
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
case bytes.Equal(hdr[:2], []byte(interpreterScriptMagic)):
|
2019-10-29 17:03:18 +00:00
|
|
|
if args.CloseOnExec {
|
|
|
|
return loadedELF{}, nil, nil, nil, syserror.ENOENT
|
|
|
|
}
|
2019-10-26 05:31:35 +00:00
|
|
|
args.Filename, args.Argv, err = parseInterpreterScript(ctx, args.Filename, args.File, args.Argv)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
|
|
|
ctx.Infof("Error loading interpreter script: %v", err)
|
|
|
|
return loadedELF{}, nil, nil, nil, err
|
|
|
|
}
|
2019-10-29 20:58:09 +00:00
|
|
|
// Refresh the traversal limit for the interpreter.
|
|
|
|
*args.RemainingTraversals = linux.MaxSymlinkTraversals
|
2020-02-14 19:11:55 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
default:
|
|
|
|
ctx.Infof("Unknown magic: %v", hdr)
|
|
|
|
return loadedELF{}, nil, nil, nil, syserror.ENOEXEC
|
|
|
|
}
|
2019-10-26 05:31:35 +00:00
|
|
|
// Set to nil in case we loop on a Interpreter Script.
|
|
|
|
args.File = nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return loadedELF{}, nil, nil, nil, syserror.ELOOP
|
|
|
|
}
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
// Load loads args.File into a MemoryManager. If args.File is nil, the path
|
|
|
|
// args.Filename is resolved and loaded instead.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// If Load returns ErrSwitchFile it should be called again with the returned
|
|
|
|
// path and argv.
|
|
|
|
//
|
|
|
|
// Preconditions:
|
|
|
|
// * The Task MemoryManager is empty.
|
|
|
|
// * Load is called on the Task goroutine.
|
2019-10-26 05:31:35 +00:00
|
|
|
func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *VDSO) (abi.OS, arch.Context, string, *syserr.Error) {
|
|
|
|
// Load the executable itself.
|
2020-02-14 19:11:55 +00:00
|
|
|
loaded, ac, file, newArgv, err := loadExecutable(ctx, args)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
2019-10-26 05:31:35 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to load %s: %v", args.Filename, err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2020-02-14 19:11:55 +00:00
|
|
|
defer file.DecRef()
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// Load the VDSO.
|
2019-10-26 05:31:35 +00:00
|
|
|
vdsoAddr, err := loadVDSO(ctx, args.MemoryManager, vdso, loaded)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Error loading VDSO: %v", err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the heap. brk starts at the next page after the end of the
|
2019-10-26 05:31:35 +00:00
|
|
|
// executable. Userspace can assume that the remainer of the page after
|
2018-04-27 17:37:02 +00:00
|
|
|
// loaded.end is available for its use.
|
|
|
|
e, ok := loaded.end.RoundUp()
|
|
|
|
if !ok {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("brk overflows: %#x", loaded.end), linux.ENOEXEC)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-10-26 05:31:35 +00:00
|
|
|
args.MemoryManager.BrkSetup(ctx, e)
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// Allocate our stack.
|
2019-10-26 05:31:35 +00:00
|
|
|
stack, err := allocStack(ctx, args.MemoryManager, ac)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to allocate stack: %v", err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Push the original filename to the stack, for AT_EXECFN.
|
2019-10-26 05:31:35 +00:00
|
|
|
execfn, err := stack.Push(args.Filename)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push exec filename: %v", err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Push 16 random bytes on the stack which AT_RANDOM will point to.
|
|
|
|
var b [16]byte
|
|
|
|
if _, err := rand.Read(b[:]); err != nil {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to read random bytes: %v", err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
random, err := stack.Push(b)
|
|
|
|
if err != nil {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push random bytes: %v", err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-07-19 19:41:00 +00:00
|
|
|
c := auth.CredentialsFromContext(ctx)
|
|
|
|
|
|
|
|
// Add generic auxv entries.
|
2018-04-27 17:37:02 +00:00
|
|
|
auxv := append(loaded.auxv, arch.Auxv{
|
2018-07-19 19:41:00 +00:00
|
|
|
arch.AuxEntry{linux.AT_UID, usermem.Addr(c.RealKUID.In(c.UserNamespace).OrOverflow())},
|
|
|
|
arch.AuxEntry{linux.AT_EUID, usermem.Addr(c.EffectiveKUID.In(c.UserNamespace).OrOverflow())},
|
|
|
|
arch.AuxEntry{linux.AT_GID, usermem.Addr(c.RealKGID.In(c.UserNamespace).OrOverflow())},
|
|
|
|
arch.AuxEntry{linux.AT_EGID, usermem.Addr(c.EffectiveKGID.In(c.UserNamespace).OrOverflow())},
|
2019-10-01 22:41:32 +00:00
|
|
|
// The conditions that require AT_SECURE = 1 never arise. See
|
|
|
|
// kernel.Task.updateCredsForExecLocked.
|
|
|
|
arch.AuxEntry{linux.AT_SECURE, 0},
|
2018-04-27 17:37:02 +00:00
|
|
|
arch.AuxEntry{linux.AT_CLKTCK, linux.CLOCKS_PER_SEC},
|
|
|
|
arch.AuxEntry{linux.AT_EXECFN, execfn},
|
|
|
|
arch.AuxEntry{linux.AT_RANDOM, random},
|
|
|
|
arch.AuxEntry{linux.AT_PAGESZ, usermem.PageSize},
|
|
|
|
arch.AuxEntry{linux.AT_SYSINFO_EHDR, vdsoAddr},
|
|
|
|
}...)
|
|
|
|
auxv = append(auxv, extraAuxv...)
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
sl, err := stack.Load(newArgv, args.Envv, auxv)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
2019-01-08 20:56:59 +00:00
|
|
|
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to load stack: %v", err), syserr.FromError(err).ToLinux())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
m := args.MemoryManager
|
2018-04-27 17:37:02 +00:00
|
|
|
m.SetArgvStart(sl.ArgvStart)
|
|
|
|
m.SetArgvEnd(sl.ArgvEnd)
|
|
|
|
m.SetEnvvStart(sl.EnvvStart)
|
|
|
|
m.SetEnvvEnd(sl.EnvvEnd)
|
|
|
|
m.SetAuxv(auxv)
|
2020-02-14 19:11:55 +00:00
|
|
|
m.SetExecutable(file)
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
ac.SetIP(uintptr(loaded.entry))
|
|
|
|
ac.SetStack(uintptr(stack.Bottom))
|
|
|
|
|
2019-10-26 05:31:35 +00:00
|
|
|
name := path.Base(args.Filename)
|
2018-04-27 17:37:02 +00:00
|
|
|
if len(name) > linux.TASK_COMM_LEN-1 {
|
|
|
|
name = name[:linux.TASK_COMM_LEN-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
return loaded.os, ac, name, nil
|
|
|
|
}
|