github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/loader/loader.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package loader loads an executable file into a MemoryManager. 16 package loader 17 18 import ( 19 "bytes" 20 "fmt" 21 "io" 22 "path" 23 24 "github.com/SagerNet/gvisor/pkg/abi" 25 "github.com/SagerNet/gvisor/pkg/abi/linux" 26 "github.com/SagerNet/gvisor/pkg/abi/linux/errno" 27 "github.com/SagerNet/gvisor/pkg/context" 28 "github.com/SagerNet/gvisor/pkg/cpuid" 29 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 30 "github.com/SagerNet/gvisor/pkg/hostarch" 31 "github.com/SagerNet/gvisor/pkg/rand" 32 "github.com/SagerNet/gvisor/pkg/sentry/arch" 33 "github.com/SagerNet/gvisor/pkg/sentry/fsbridge" 34 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 35 "github.com/SagerNet/gvisor/pkg/sentry/mm" 36 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 37 "github.com/SagerNet/gvisor/pkg/syserr" 38 "github.com/SagerNet/gvisor/pkg/syserror" 39 "github.com/SagerNet/gvisor/pkg/usermem" 40 ) 41 42 // LoadArgs holds specifications for an executable file to be loaded. 43 type LoadArgs struct { 44 // MemoryManager is the memory manager to load the executable into. 45 MemoryManager *mm.MemoryManager 46 47 // RemainingTraversals is the maximum number of symlinks to follow to 48 // resolve Filename. This counter is passed by reference to keep it 49 // updated throughout the call stack. 50 RemainingTraversals *uint 51 52 // ResolveFinal indicates whether the final link of Filename should be 53 // resolved, if it is a symlink. 54 ResolveFinal bool 55 56 // Filename is the path for the executable. 57 Filename string 58 59 // File is an open fs.File object of the executable. If File is not 60 // nil, then File will be loaded and Filename will be ignored. 61 // 62 // The caller is responsible for checking that the user can execute this file. 63 File fsbridge.File 64 65 // Opener is used to open the executable file when 'File' is nil. 66 Opener fsbridge.Lookup 67 68 // CloseOnExec indicates that the executable (or one of its parent 69 // directories) was opened with O_CLOEXEC. If the executable is an 70 // interpreter script, then cause an ENOENT error to occur, since the 71 // script would otherwise be inaccessible to the interpreter. 72 CloseOnExec bool 73 74 // Argv is the vector of arguments to pass to the executable. 75 Argv []string 76 77 // Envv is the vector of environment variables to pass to the 78 // executable. 79 Envv []string 80 81 // Features specifies the CPU feature set for the executable. 82 Features *cpuid.FeatureSet 83 } 84 85 // openPath opens args.Filename and checks that it is valid for loading. 86 // 87 // openPath returns an *fs.Dirent and *fs.File for args.Filename, which is not 88 // installed in the Task FDTable. The caller takes ownership of both. 89 // 90 // args.Filename must be a readable, executable, regular file. 91 func openPath(ctx context.Context, args LoadArgs) (fsbridge.File, error) { 92 if args.Filename == "" { 93 ctx.Infof("cannot open empty name") 94 return nil, syserror.ENOENT 95 } 96 97 // TODO(github.com/SagerNet/issue/160): Linux requires only execute permission, 98 // not read. However, our backing filesystems may prevent us from reading 99 // the file without read permission. Additionally, a task with a 100 // non-readable executable has additional constraints on access via 101 // ptrace and procfs. 102 opts := vfs.OpenOptions{ 103 Flags: linux.O_RDONLY, 104 FileExec: true, 105 } 106 return args.Opener.OpenPath(ctx, args.Filename, opts, args.RemainingTraversals, args.ResolveFinal) 107 } 108 109 // checkIsRegularFile prevents us from trying to execute a directory, pipe, etc. 110 func checkIsRegularFile(ctx context.Context, file fsbridge.File, filename string) error { 111 t, err := file.Type(ctx) 112 if err != nil { 113 return err 114 } 115 if t != linux.ModeRegular { 116 ctx.Infof("%q is not a regular file: %v", filename, t) 117 return linuxerr.EACCES 118 } 119 return nil 120 } 121 122 // allocStack allocates and maps a stack in to any available part of the address space. 123 func allocStack(ctx context.Context, m *mm.MemoryManager, a arch.Context) (*arch.Stack, error) { 124 ar, err := m.MapStack(ctx) 125 if err != nil { 126 return nil, err 127 } 128 return &arch.Stack{Arch: a, IO: m, Bottom: ar.End}, nil 129 } 130 131 const ( 132 // maxLoaderAttempts is the maximum number of attempts to try to load 133 // an interpreter scripts, to prevent loops. 6 (initial + 5 changes) is 134 // what the Linux kernel allows (fs/exec.c:search_binary_handler). 135 maxLoaderAttempts = 6 136 ) 137 138 // loadExecutable loads an executable that is pointed to by args.File. The 139 // caller is responsible for checking that the user can execute this file. 140 // If nil, the path args.Filename is resolved and loaded (check that the user 141 // can execute this file is done here in this case). If the executable is an 142 // interpreter script rather than an ELF, the binary of the corresponding 143 // interpreter will be loaded. 144 // 145 // It returns: 146 // * loadedELF, description of the loaded binary 147 // * arch.Context matching the binary arch 148 // * fs.Dirent of the binary file 149 // * Possibly updated args.Argv 150 func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, arch.Context, fsbridge.File, []string, error) { 151 for i := 0; i < maxLoaderAttempts; i++ { 152 if args.File == nil { 153 var err error 154 args.File, err = openPath(ctx, args) 155 if err != nil { 156 ctx.Infof("Error opening %s: %v", args.Filename, err) 157 return loadedELF{}, nil, nil, nil, err 158 } 159 // Ensure file is release in case the code loops or errors out. 160 defer args.File.DecRef(ctx) 161 } else { 162 if err := checkIsRegularFile(ctx, args.File, args.Filename); err != nil { 163 return loadedELF{}, nil, nil, nil, err 164 } 165 } 166 167 // Check the header. Is this an ELF or interpreter script? 168 var hdr [4]uint8 169 // N.B. We assume that reading from a regular file cannot block. 170 _, err := args.File.ReadFull(ctx, usermem.BytesIOSequence(hdr[:]), 0) 171 // Allow unexpected EOF, as a valid executable could be only three bytes 172 // (e.g., #!a). 173 if err != nil && err != io.ErrUnexpectedEOF { 174 if err == io.EOF { 175 err = syserror.ENOEXEC 176 } 177 return loadedELF{}, nil, nil, nil, err 178 } 179 180 switch { 181 case bytes.Equal(hdr[:], []byte(elfMagic)): 182 loaded, ac, err := loadELF(ctx, args) 183 if err != nil { 184 ctx.Infof("Error loading ELF: %v", err) 185 return loadedELF{}, nil, nil, nil, err 186 } 187 // An ELF is always terminal. Hold on to file. 188 args.File.IncRef() 189 return loaded, ac, args.File, args.Argv, err 190 191 case bytes.Equal(hdr[:2], []byte(interpreterScriptMagic)): 192 if args.CloseOnExec { 193 return loadedELF{}, nil, nil, nil, syserror.ENOENT 194 } 195 args.Filename, args.Argv, err = parseInterpreterScript(ctx, args.Filename, args.File, args.Argv) 196 if err != nil { 197 ctx.Infof("Error loading interpreter script: %v", err) 198 return loadedELF{}, nil, nil, nil, err 199 } 200 // Refresh the traversal limit for the interpreter. 201 *args.RemainingTraversals = linux.MaxSymlinkTraversals 202 203 default: 204 ctx.Infof("Unknown magic: %v", hdr) 205 return loadedELF{}, nil, nil, nil, syserror.ENOEXEC 206 } 207 // Set to nil in case we loop on a Interpreter Script. 208 args.File = nil 209 } 210 211 return loadedELF{}, nil, nil, nil, linuxerr.ELOOP 212 } 213 214 // Load loads args.File into a MemoryManager. If args.File is nil, the path 215 // args.Filename is resolved and loaded instead. 216 // 217 // If Load returns ErrSwitchFile it should be called again with the returned 218 // path and argv. 219 // 220 // Preconditions: 221 // * The Task MemoryManager is empty. 222 // * Load is called on the Task goroutine. 223 func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *VDSO) (abi.OS, arch.Context, string, *syserr.Error) { 224 // Load the executable itself. 225 loaded, ac, file, newArgv, err := loadExecutable(ctx, args) 226 if err != nil { 227 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("failed to load %s: %v", args.Filename, err), syserr.FromError(err).ToLinux()) 228 } 229 defer file.DecRef(ctx) 230 231 // Load the VDSO. 232 vdsoAddr, err := loadVDSO(ctx, args.MemoryManager, vdso, loaded) 233 if err != nil { 234 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("error loading VDSO: %v", err), syserr.FromError(err).ToLinux()) 235 } 236 237 // Setup the heap. brk starts at the next page after the end of the 238 // executable. Userspace can assume that the remainer of the page after 239 // loaded.end is available for its use. 240 e, ok := loaded.end.RoundUp() 241 if !ok { 242 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("brk overflows: %#x", loaded.end), errno.ENOEXEC) 243 } 244 args.MemoryManager.BrkSetup(ctx, e) 245 246 // Allocate our stack. 247 stack, err := allocStack(ctx, args.MemoryManager, ac) 248 if err != nil { 249 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to allocate stack: %v", err), syserr.FromError(err).ToLinux()) 250 } 251 252 // Push the original filename to the stack, for AT_EXECFN. 253 if _, err := stack.PushNullTerminatedByteSlice([]byte(args.Filename)); err != nil { 254 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push exec filename: %v", err), syserr.FromError(err).ToLinux()) 255 } 256 execfn := stack.Bottom 257 258 // Push 16 random bytes on the stack which AT_RANDOM will point to. 259 var b [16]byte 260 if _, err := rand.Read(b[:]); err != nil { 261 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to read random bytes: %v", err), syserr.FromError(err).ToLinux()) 262 } 263 if _, err = stack.PushNullTerminatedByteSlice(b[:]); err != nil { 264 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push random bytes: %v", err), syserr.FromError(err).ToLinux()) 265 } 266 random := stack.Bottom 267 268 c := auth.CredentialsFromContext(ctx) 269 270 // Add generic auxv entries. 271 auxv := append(loaded.auxv, arch.Auxv{ 272 arch.AuxEntry{linux.AT_UID, hostarch.Addr(c.RealKUID.In(c.UserNamespace).OrOverflow())}, 273 arch.AuxEntry{linux.AT_EUID, hostarch.Addr(c.EffectiveKUID.In(c.UserNamespace).OrOverflow())}, 274 arch.AuxEntry{linux.AT_GID, hostarch.Addr(c.RealKGID.In(c.UserNamespace).OrOverflow())}, 275 arch.AuxEntry{linux.AT_EGID, hostarch.Addr(c.EffectiveKGID.In(c.UserNamespace).OrOverflow())}, 276 // The conditions that require AT_SECURE = 1 never arise. See 277 // kernel.Task.updateCredsForExecLocked. 278 arch.AuxEntry{linux.AT_SECURE, 0}, 279 arch.AuxEntry{linux.AT_CLKTCK, linux.CLOCKS_PER_SEC}, 280 arch.AuxEntry{linux.AT_EXECFN, execfn}, 281 arch.AuxEntry{linux.AT_RANDOM, random}, 282 arch.AuxEntry{linux.AT_PAGESZ, hostarch.PageSize}, 283 arch.AuxEntry{linux.AT_SYSINFO_EHDR, vdsoAddr}, 284 }...) 285 auxv = append(auxv, extraAuxv...) 286 287 sl, err := stack.Load(newArgv, args.Envv, auxv) 288 if err != nil { 289 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to load stack: %v", err), syserr.FromError(err).ToLinux()) 290 } 291 292 m := args.MemoryManager 293 m.SetArgvStart(sl.ArgvStart) 294 m.SetArgvEnd(sl.ArgvEnd) 295 m.SetEnvvStart(sl.EnvvStart) 296 m.SetEnvvEnd(sl.EnvvEnd) 297 m.SetAuxv(auxv) 298 m.SetExecutable(ctx, file) 299 300 symbolValue, err := getSymbolValueFromVDSO("rt_sigreturn") 301 if err != nil { 302 return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to find rt_sigreturn in vdso: %v", err), syserr.FromError(err).ToLinux()) 303 } 304 305 // Found rt_sigretrun. 306 addr := uint64(vdsoAddr) + symbolValue - vdsoPrelink 307 m.SetVDSOSigReturn(addr) 308 309 ac.SetIP(uintptr(loaded.entry)) 310 ac.SetStack(uintptr(stack.Bottom)) 311 312 name := path.Base(args.Filename) 313 if len(name) > linux.TASK_COMM_LEN-1 { 314 name = name[:linux.TASK_COMM_LEN-1] 315 } 316 317 return loaded.os, ac, name, nil 318 }