SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
return do_execve(getname(filename), argv, envp);
}
SYSCALL_DEFINE5(execveat, int, fd, const char __user *, filename, const char __user *const __user *, argv, const char __user *const __user *, envp, int, flags) { int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
return do_execveat(fd, getname_flags(filename, lookup_flags, NULL), argv, envp, flags); }
#ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename, const compat_uptr_t __user *, argv, const compat_uptr_t __user *, envp) { return compat_do_execve(getname(filename), argv, envp); }
COMPAT_SYSCALL_DEFINE5(execveat, int, fd, const char __user *, filename, const compat_uptr_t __user *, argv, const compat_uptr_t __user *, envp, int, flags) { int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
return compat_do_execveat(fd, getname_flags(filename, lookup_flags, NULL), argv, envp, flags); } #endif
/* * sys_execve() executes a new program. */ static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr argv, struct user_arg_ptr envp, int flags) { char *pathbuf = NULL; struct linux_binprm *bprm; struct file *file; struct files_struct *displaced; int retval;
if (IS_ERR(filename)) return PTR_ERR(filename);
/* * We move the actual failure in case of RLIMIT_NPROC excess from * set*uid() to execve() because too many poorly written programs * don't check setuid() return code. Here we additionally recheck * whether NPROC limit is still exceeded. */ if ((current->flags & PF_NPROC_EXCEEDED) && atomic_read(¤t_user()->processes) > rlimit(RLIMIT_NPROC)) { retval = -EAGAIN; goto out_ret; }
/* We're below the limit (still or again), so we don't want to make * further execve() calls fail. */ current->flags &= ~PF_NPROC_EXCEEDED;
retval = unshare_files(&displaced); if (retval) goto out_ret;
retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) goto out_files;
retval = prepare_bprm_creds(bprm); if (retval) goto out_free;
check_unsafe_exec(bprm); current->in_execve = 1;
file = do_open_execat(fd, filename, flags); retval = PTR_ERR(file); if (IS_ERR(file)) goto out_unmark;
sched_exec();
bprm->file = file; if (fd == AT_FDCWD || filename->name[0] == '/') { bprm->filename = filename->name; } else { if (filename->name[0] == '\0') pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d", fd); else pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d/%s", fd, filename->name); if (!pathbuf) { retval = -ENOMEM; goto out_unmark; } /* * Record that a name derived from an O_CLOEXEC fd will be * inaccessible after exec. Relies on having exclusive access to * current->files (due to unshare_files above). */ if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt))) bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE; bprm->filename = pathbuf; } bprm->interp = bprm->filename;
retval = bprm_mm_init(bprm); if (retval) goto out_unmark;
bprm->argc = count(argv, MAX_ARG_STRINGS); if ((retval = bprm->argc) < 0) goto out;
bprm->envc = count(envp, MAX_ARG_STRINGS); if ((retval = bprm->envc) < 0) goto out;
retval = prepare_binprm(bprm); if (retval < 0) goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm); if (retval < 0) goto out;
bprm->exec = bprm->p; retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) goto out;
retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out;
retval = exec_binprm(bprm); if (retval < 0) goto out;
/* execve succeeded */ current->fs->in_exec = 0; current->in_execve = 0; acct_update_integrals(current); task_numa_free(current); free_bprm(bprm); kfree(pathbuf); putname(filename); if (displaced) put_files_struct(displaced); return retval;
out: if (bprm->mm) { acct_arg_size(bprm, 0); mmput(bprm->mm); }
out_unmark: current->fs->in_exec = 0; current->in_execve = 0;
out_free: free_bprm(bprm); kfree(pathbuf);
out_files: if (displaced) reset_files_struct(displaced); out_ret: putname(filename); return retval; }