exec:check_unsafe_exec: kill the dead -EAGAIN and clear_in_exec logic
authorOleg Nesterov <oleg@redhat.com>
Thu, 23 Jan 2014 23:55:50 +0000 (15:55 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 24 Jan 2014 00:37:02 +0000 (16:37 -0800)
fs_struct->in_exec == T means that this ->fs is used by a single process
(thread group), and one of the treads does do_execve().

To avoid the mt-exec races this code has the following complications:

1. check_unsafe_exec() returns -EBUSY if ->in_exec was
   already set by another thread.

2. do_execve_common() records "clear_in_exec" to ensure
   that the error path can only clear ->in_exec if it was
   set by current.

However, after 9b1bf12d5d51 "signals: move cred_guard_mutex from
task_struct to signal_struct" we do not need these complications:

1. We can't race with our sub-thread, this is called under
   per-process ->cred_guard_mutex. And we can't race with
   another CLONE_FS task, we already checked that this fs
   is not shared.

   We can remove the  dead -EAGAIN logic.

2. "out_unmark:" in do_execve_common() is either called
   under ->cred_guard_mutex, or after de_thread() which
   kills other threads, so we can't race with sub-thread
   which could set ->in_exec. And if ->fs is shared with
   another process ->in_exec should be false anyway.

   We can clear in_exec unconditionally.

This also means that check_unsafe_exec() can be void.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Kees Cook <keescook@chromium.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/exec.c

index 81ae6212187aa8fc4b87bc3c6efe6527d30978ca..389fe7b0ba148059dcbc0d5e34f84dd5820b82a9 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1223,11 +1223,10 @@ EXPORT_SYMBOL(install_exec_creds);
  * - the caller must hold ->cred_guard_mutex to protect against
  *   PTRACE_ATTACH
  */
-static int check_unsafe_exec(struct linux_binprm *bprm)
+static void check_unsafe_exec(struct linux_binprm *bprm)
 {
        struct task_struct *p = current, *t;
        unsigned n_fs;
-       int res = 0;
 
        if (p->ptrace) {
                if (p->ptrace & PT_PTRACE_CAP)
@@ -1253,22 +1252,15 @@ static int check_unsafe_exec(struct linux_binprm *bprm)
        }
        rcu_read_unlock();
 
-       if (p->fs->users > n_fs) {
+       if (p->fs->users > n_fs)
                bprm->unsafe |= LSM_UNSAFE_SHARE;
-       } else {
-               res = -EAGAIN;
-               if (!p->fs->in_exec) {
-                       p->fs->in_exec = 1;
-                       res = 1;
-               }
-       }
+       else
+               p->fs->in_exec = 1;
        spin_unlock(&p->fs->lock);
-
-       return res;
 }
 
-/* 
- * Fill the binprm structure from the inode. 
+/*
+ * Fill the binprm structure from the inode.
  * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
  *
  * This may be called multiple times for binary chains (scripts for example).
@@ -1453,7 +1445,6 @@ static int do_execve_common(const char *filename,
        struct linux_binprm *bprm;
        struct file *file;
        struct files_struct *displaced;
-       bool clear_in_exec;
        int retval;
 
        /*
@@ -1485,10 +1476,7 @@ static int do_execve_common(const char *filename,
        if (retval)
                goto out_free;
 
-       retval = check_unsafe_exec(bprm);
-       if (retval < 0)
-               goto out_free;
-       clear_in_exec = retval;
+       check_unsafe_exec(bprm);
        current->in_execve = 1;
 
        file = open_exec(filename);
@@ -1558,8 +1546,7 @@ out_file:
        }
 
 out_unmark:
-       if (clear_in_exec)
-               current->fs->in_exec = 0;
+       current->fs->in_exec = 0;
        current->in_execve = 0;
 
 out_free: