prctl: avoid using mmap_sem for exe_file serialization
[linux-2.6-block.git] / kernel / fork.c
index cf65139615a0d38e3fef37ee3a6e2e270c01ccaf..0d23e76a0c6155540009bfa3fe072973d6ca7530 100644 (file)
@@ -74,6 +74,7 @@
 #include <linux/uprobes.h>
 #include <linux/aio.h>
 #include <linux/compiler.h>
+#include <linux/sysctl.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/task.h>
 
+/*
+ * Minimum number of threads to boot the kernel
+ */
+#define MIN_THREADS 20
+
+/*
+ * Maximum number of threads
+ */
+#define MAX_THREADS FUTEX_TID_MASK
+
 /*
  * Protected counters by write_lock_irq(&tasklist_lock)
  */
@@ -253,7 +264,30 @@ EXPORT_SYMBOL_GPL(__put_task_struct);
 
 void __init __weak arch_task_cache_init(void) { }
 
-void __init fork_init(unsigned long mempages)
+/*
+ * set_max_threads
+ */
+static void set_max_threads(unsigned int max_threads_suggested)
+{
+       u64 threads;
+
+       /*
+        * The number of threads shall be limited such that the thread
+        * structures may only consume a small part of the available memory.
+        */
+       if (fls64(totalram_pages) + fls64(PAGE_SIZE) > 64)
+               threads = MAX_THREADS;
+       else
+               threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE,
+                                   (u64) THREAD_SIZE * 8UL);
+
+       if (threads > max_threads_suggested)
+               threads = max_threads_suggested;
+
+       max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS);
+}
+
+void __init fork_init(void)
 {
 #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
 #ifndef ARCH_MIN_TASKALIGN
@@ -268,18 +302,7 @@ void __init fork_init(unsigned long mempages)
        /* do the arch specific task caches init */
        arch_task_cache_init();
 
-       /*
-        * The default maximum number of threads is set to a safe
-        * value: the thread structures can take up at most half
-        * of memory.
-        */
-       max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);
-
-       /*
-        * we need to allow at least 20 threads to boot a system
-        */
-       if (max_threads < 20)
-               max_threads = 20;
+       set_max_threads(MAX_THREADS);
 
        init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
        init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
@@ -380,6 +403,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
         */
        down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING);
 
+       /* No ordering required: file already has been exposed. */
+       RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm));
+
        mm->total_vm = oldmm->total_vm;
        mm->shared_vm = oldmm->shared_vm;
        mm->exec_vm = oldmm->exec_vm;
@@ -505,7 +531,13 @@ static inline void mm_free_pgd(struct mm_struct *mm)
        pgd_free(mm, mm->pgd);
 }
 #else
-#define dup_mmap(mm, oldmm)    (0)
+static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
+{
+       down_write(&oldmm->mmap_sem);
+       RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm));
+       up_write(&oldmm->mmap_sem);
+       return 0;
+}
 #define mm_alloc_pgd(mm)       (0)
 #define mm_free_pgd(mm)
 #endif /* CONFIG_MMU */
@@ -674,35 +706,53 @@ void mmput(struct mm_struct *mm)
 }
 EXPORT_SYMBOL_GPL(mmput);
 
+/**
+ * set_mm_exe_file - change a reference to the mm's executable file
+ *
+ * This changes mm's executable file (shown as symlink /proc/[pid]/exe).
+ *
+ * Main users are mmput() and sys_execve(). Callers prevent concurrent
+ * invocations: in mmput() nobody alive left, in execve task is single
+ * threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the
+ * mm->exe_file, but does so without using set_mm_exe_file() in order
+ * to do avoid the need for any locks.
+ */
 void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
 {
+       struct file *old_exe_file;
+
+       /*
+        * It is safe to dereference the exe_file without RCU as
+        * this function is only called if nobody else can access
+        * this mm -- see comment above for justification.
+        */
+       old_exe_file = rcu_dereference_raw(mm->exe_file);
+
        if (new_exe_file)
                get_file(new_exe_file);
-       if (mm->exe_file)
-               fput(mm->exe_file);
-       mm->exe_file = new_exe_file;
+       rcu_assign_pointer(mm->exe_file, new_exe_file);
+       if (old_exe_file)
+               fput(old_exe_file);
 }
 
+/**
+ * get_mm_exe_file - acquire a reference to the mm's executable file
+ *
+ * Returns %NULL if mm has no associated executable file.
+ * User must release file via fput().
+ */
 struct file *get_mm_exe_file(struct mm_struct *mm)
 {
        struct file *exe_file;
 
-       /* We need mmap_sem to protect against races with removal of exe_file */
-       down_read(&mm->mmap_sem);
-       exe_file = mm->exe_file;
-       if (exe_file)
-               get_file(exe_file);
-       up_read(&mm->mmap_sem);
+       rcu_read_lock();
+       exe_file = rcu_dereference(mm->exe_file);
+       if (exe_file && !get_file_rcu(exe_file))
+               exe_file = NULL;
+       rcu_read_unlock();
        return exe_file;
 }
 
-static void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm)
-{
-       /* It's safe to write the exe_file pointer without exe_file_lock because
-        * this is called during fork when the task is not yet in /proc */
-       newmm->exe_file = get_mm_exe_file(oldmm);
-}
-
 /**
  * get_task_mm - acquire a reference to the task's mm
  *
@@ -864,8 +914,6 @@ static struct mm_struct *dup_mm(struct task_struct *tsk)
        if (!mm_init(mm, tsk))
                goto fail_nomem;
 
-       dup_mm_exe_file(oldmm, mm);
-
        err = dup_mmap(mm, oldmm);
        if (err)
                goto free_pt;
@@ -1279,9 +1327,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        if (nr_threads >= max_threads)
                goto bad_fork_cleanup_count;
 
-       if (!try_module_get(task_thread_info(p)->exec_domain->module))
-               goto bad_fork_cleanup_count;
-
        delayacct_tsk_init(p);  /* Must remain after dup_task_struct() */
        p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER);
        p->flags |= PF_FORKNOEXEC;
@@ -1406,10 +1451,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                goto bad_fork_cleanup_io;
 
        if (pid != &init_struct_pid) {
-               retval = -ENOMEM;
                pid = alloc_pid(p->nsproxy->pid_ns_for_children);
-               if (!pid)
+               if (IS_ERR(pid)) {
+                       retval = PTR_ERR(pid);
                        goto bad_fork_cleanup_io;
+               }
        }
 
        p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
@@ -1590,7 +1636,6 @@ bad_fork_cleanup_threadgroup_lock:
        if (clone_flags & CLONE_THREAD)
                threadgroup_change_end(current);
        delayacct_tsk_free(p);
-       module_put(task_thread_info(p)->exec_domain->module);
 bad_fork_cleanup_count:
        atomic_dec(&p->cred->user->processes);
        exit_creds(p);
@@ -2004,3 +2049,26 @@ int unshare_files(struct files_struct **displaced)
        task_unlock(task);
        return 0;
 }
+
+int sysctl_max_threads(struct ctl_table *table, int write,
+                      void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       struct ctl_table t;
+       int ret;
+       int threads = max_threads;
+       int min = MIN_THREADS;
+       int max = MAX_THREADS;
+
+       t = *table;
+       t.data = &threads;
+       t.extra1 = &min;
+       t.extra2 = &max;
+
+       ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos);
+       if (ret || !write)
+               return ret;
+
+       set_max_threads(threads);
+
+       return 0;
+}