summaryrefslogtreecommitdiffstats
path: root/fs/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/exec.c')
-rw-r--r--fs/exec.c196
1 files changed, 99 insertions, 97 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 2c465119affc..14b786158aa9 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1051,13 +1051,14 @@ static int exec_mmap(struct mm_struct *mm)
tsk = current;
old_mm = current->mm;
exec_mm_release(tsk, old_mm);
+ if (old_mm)
+ sync_mm_rss(old_mm);
ret = mutex_lock_killable(&tsk->signal->exec_update_mutex);
if (ret)
return ret;
if (old_mm) {
- sync_mm_rss(old_mm);
/*
* Make sure that if there is a core dump in progress
* for the old mm, we get out and die instead of going
@@ -1093,12 +1094,6 @@ static int exec_mmap(struct mm_struct *mm)
return 0;
}
-/*
- * This function makes sure the current process has its own signal table,
- * so that flush_signal_handlers can later reset the handlers without
- * disturbing other processes. (Other processes might share the signal
- * table via the CLONE_SIGHAND option to clone().)
- */
static int de_thread(struct task_struct *tsk)
{
struct signal_struct *sig = tsk->signal;
@@ -1240,6 +1235,12 @@ killed:
}
+/*
+ * This function makes sure the current process has its own signal table,
+ * so that flush_signal_handlers can later reset the handlers without
+ * disturbing other processes. (Other processes might share the signal
+ * table via the CLONE_SIGHAND option to clone().)
+ */
static int unshare_sighand(struct task_struct *me)
{
struct sighand_struct *oldsighand = me->sighand;
@@ -1296,14 +1297,19 @@ void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
* Calling this is the point of no return. None of the failures will be
* seen by userspace since either the process is already taking a fatal
* signal (via de_thread() or coredump), or will have SEGV raised
- * (after exec_mmap()) by search_binary_handlers (see below).
+ * (after exec_mmap()) by search_binary_handler (see below).
*/
-int flush_old_exec(struct linux_binprm * bprm)
+int begin_new_exec(struct linux_binprm * bprm)
{
struct task_struct *me = current;
int retval;
/*
+ * Ensure all future errors are fatal.
+ */
+ bprm->point_of_no_return = true;
+
+ /*
* Make this the only thread in the thread group.
*/
retval = de_thread(me);
@@ -1327,13 +1333,6 @@ int flush_old_exec(struct linux_binprm * bprm)
if (retval)
goto out;
- /*
- * After setting bprm->called_exec_mmap (to mark that current is
- * using the prepared mm now), we have nothing left of the original
- * process. If anything from here on returns an error, the check
- * in search_binary_handler() will SEGV current.
- */
- bprm->called_exec_mmap = 1;
bprm->mm = NULL;
#ifdef CONFIG_POSIX_TIMERS
@@ -1346,7 +1345,7 @@ int flush_old_exec(struct linux_binprm * bprm)
*/
retval = unshare_sighand(me);
if (retval)
- goto out;
+ goto out_unlock;
set_fs(USER_DS);
me->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
@@ -1361,36 +1360,7 @@ int flush_old_exec(struct linux_binprm * bprm)
* undergoing exec(2).
*/
do_close_on_exec(me->files);
- return 0;
-
-out:
- return retval;
-}
-EXPORT_SYMBOL(flush_old_exec);
-
-void would_dump(struct linux_binprm *bprm, struct file *file)
-{
- struct inode *inode = file_inode(file);
- if (inode_permission(inode, MAY_READ) < 0) {
- struct user_namespace *old, *user_ns;
- bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
-
- /* Ensure mm->user_ns contains the executable */
- user_ns = old = bprm->mm->user_ns;
- while ((user_ns != &init_user_ns) &&
- !privileged_wrt_inode_uidgid(user_ns, inode))
- user_ns = user_ns->parent;
-
- if (old != user_ns) {
- bprm->mm->user_ns = get_user_ns(user_ns);
- put_user_ns(old);
- }
- }
-}
-EXPORT_SYMBOL(would_dump);
-void setup_new_exec(struct linux_binprm * bprm)
-{
/*
* Once here, prepare_binrpm() will not be called any more, so
* the final state of setuid/setgid/fscaps can be merged into the
@@ -1400,7 +1370,7 @@ void setup_new_exec(struct linux_binprm * bprm)
if (bprm->secureexec) {
/* Make sure parent cannot signal privileged process. */
- current->pdeath_signal = 0;
+ me->pdeath_signal = 0;
/*
* For secureexec, reset the stack limit to sane default to
@@ -1413,9 +1383,7 @@ void setup_new_exec(struct linux_binprm * bprm)
bprm->rlim_stack.rlim_cur = _STK_LIM;
}
- arch_pick_mmap_layout(current->mm, &bprm->rlim_stack);
-
- current->sas_ss_sp = current->sas_ss_size = 0;
+ me->sas_ss_sp = me->sas_ss_size = 0;
/*
* Figure out dumpability. Note that this checking only of current
@@ -1429,20 +1397,82 @@ void setup_new_exec(struct linux_binprm * bprm)
else
set_dumpable(current->mm, SUID_DUMP_USER);
- arch_setup_new_exec();
perf_event_exec();
- __set_task_comm(current, kbasename(bprm->filename), true);
+ __set_task_comm(me, kbasename(bprm->filename), true);
+
+ /* An exec changes our domain. We are no longer part of the thread
+ group */
+ WRITE_ONCE(me->self_exec_id, me->self_exec_id + 1);
+ flush_signal_handlers(me, 0);
+
+ /*
+ * install the new credentials for this executable
+ */
+ security_bprm_committing_creds(bprm);
+
+ commit_creds(bprm->cred);
+ bprm->cred = NULL;
+
+ /*
+ * Disable monitoring for regular users
+ * when executing setuid binaries. Must
+ * wait until new credentials are committed
+ * by commit_creds() above
+ */
+ if (get_dumpable(me->mm) != SUID_DUMP_USER)
+ perf_event_exit_task(me);
+ /*
+ * cred_guard_mutex must be held at least to this point to prevent
+ * ptrace_attach() from altering our determination of the task's
+ * credentials; any time after this it may be unlocked.
+ */
+ security_bprm_committed_creds(bprm);
+ return 0;
+
+out_unlock:
+ mutex_unlock(&me->signal->exec_update_mutex);
+out:
+ return retval;
+}
+EXPORT_SYMBOL(begin_new_exec);
+
+void would_dump(struct linux_binprm *bprm, struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ if (inode_permission(inode, MAY_READ) < 0) {
+ struct user_namespace *old, *user_ns;
+ bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
+
+ /* Ensure mm->user_ns contains the executable */
+ user_ns = old = bprm->mm->user_ns;
+ while ((user_ns != &init_user_ns) &&
+ !privileged_wrt_inode_uidgid(user_ns, inode))
+ user_ns = user_ns->parent;
+
+ if (old != user_ns) {
+ bprm->mm->user_ns = get_user_ns(user_ns);
+ put_user_ns(old);
+ }
+ }
+}
+EXPORT_SYMBOL(would_dump);
+
+void setup_new_exec(struct linux_binprm * bprm)
+{
+ /* Setup things that can depend upon the personality */
+ struct task_struct *me = current;
+
+ arch_pick_mmap_layout(me->mm, &bprm->rlim_stack);
+
+ arch_setup_new_exec();
/* Set the new mm task size. We have to do that late because it may
* depend on TIF_32BIT which is only updated in flush_thread() on
* some architectures like powerpc
*/
- current->mm->task_size = TASK_SIZE;
-
- /* An exec changes our domain. We are no longer part of the thread
- group */
- WRITE_ONCE(current->self_exec_id, current->self_exec_id + 1);
- flush_signal_handlers(current, 0);
+ me->mm->task_size = TASK_SIZE;
+ mutex_unlock(&me->signal->exec_update_mutex);
+ mutex_unlock(&me->signal->cred_guard_mutex);
}
EXPORT_SYMBOL(setup_new_exec);
@@ -1458,7 +1488,7 @@ EXPORT_SYMBOL(finalize_exec);
/*
* Prepare credentials and lock ->cred_guard_mutex.
- * install_exec_creds() commits the new creds and drops the lock.
+ * setup_new_exec() commits the new creds and drops the lock.
* Or, if exec fails before, free_bprm() should release ->cred and
* and unlock.
*/
@@ -1479,8 +1509,6 @@ static void free_bprm(struct linux_binprm *bprm)
{
free_arg_pages(bprm);
if (bprm->cred) {
- if (bprm->called_exec_mmap)
- mutex_unlock(&current->signal->exec_update_mutex);
mutex_unlock(&current->signal->cred_guard_mutex);
abort_creds(bprm->cred);
}
@@ -1507,35 +1535,6 @@ int bprm_change_interp(const char *interp, struct linux_binprm *bprm)
EXPORT_SYMBOL(bprm_change_interp);
/*
- * install the new credentials for this executable
- */
-void install_exec_creds(struct linux_binprm *bprm)
-{
- security_bprm_committing_creds(bprm);
-
- commit_creds(bprm->cred);
- bprm->cred = NULL;
-
- /*
- * Disable monitoring for regular users
- * when executing setuid binaries. Must
- * wait until new credentials are committed
- * by commit_creds() above
- */
- if (get_dumpable(current->mm) != SUID_DUMP_USER)
- perf_event_exit_task(current);
- /*
- * cred_guard_mutex must be held at least to this point to prevent
- * ptrace_attach() from altering our determination of the task's
- * credentials; any time after this it may be unlocked.
- */
- security_bprm_committed_creds(bprm);
- mutex_unlock(&current->signal->exec_update_mutex);
- mutex_unlock(&current->signal->cred_guard_mutex);
-}
-EXPORT_SYMBOL(install_exec_creds);
-
-/*
* determine how safe it is to execute the proposed program
* - the caller must hold ->cred_guard_mutex to protect against
* PTRACE_ATTACH or seccomp thread-sync
@@ -1722,13 +1721,8 @@ int search_binary_handler(struct linux_binprm *bprm)
read_lock(&binfmt_lock);
put_binfmt(fmt);
- if (retval < 0 && bprm->called_exec_mmap) {
- /* we got to flush_old_exec() and failed after it */
- read_unlock(&binfmt_lock);
- force_sigsegv(SIGSEGV);
- return retval;
- }
- if (retval != -ENOEXEC || !bprm->file) {
+ if (bprm->point_of_no_return || !bprm->file ||
+ (retval != -ENOEXEC)) {
read_unlock(&binfmt_lock);
return retval;
}
@@ -1897,6 +1891,14 @@ static int __do_execve_file(int fd, struct filename *filename,
return retval;
out:
+ /*
+ * If past the point of no return ensure the the code never
+ * returns to the userspace process. Use an existing fatal
+ * signal if present otherwise terminate the process with
+ * SIGSEGV.
+ */
+ if (bprm->point_of_no_return && !fatal_signal_pending(current))
+ force_sigsegv(SIGSEGV);
if (bprm->mm) {
acct_arg_size(bprm, 0);
mmput(bprm->mm);