[PATCH] Disable CLONE_CHILD_CLEARTID for abnormal exit
[sfrench/cifs-2.6.git] / kernel / fork.c
index bca6ce6d3ded7f2ef5c4048296ce13aa5d554bcc..f37980df1d58d5cb22f619625bde651759ae1673 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/binfmts.h>
 #include <linux/mman.h>
 #include <linux/fs.h>
+#include <linux/nsproxy.h>
 #include <linux/capability.h>
 #include <linux/cpu.h>
 #include <linux/cpuset.h>
@@ -42,6 +43,7 @@
 #include <linux/profile.h>
 #include <linux/rmap.h>
 #include <linux/acct.h>
+#include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
 #include <linux/delayacct.h>
 #include <linux/taskstats_kern.h>
@@ -80,26 +82,26 @@ int nr_processes(void)
 #ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
 # define alloc_task_struct()   kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
 # define free_task_struct(tsk) kmem_cache_free(task_struct_cachep, (tsk))
-static kmem_cache_t *task_struct_cachep;
+static struct kmem_cache *task_struct_cachep;
 #endif
 
 /* SLAB cache for signal_struct structures (tsk->signal) */
-static kmem_cache_t *signal_cachep;
+static struct kmem_cache *signal_cachep;
 
 /* SLAB cache for sighand_struct structures (tsk->sighand) */
-kmem_cache_t *sighand_cachep;
+struct kmem_cache *sighand_cachep;
 
 /* SLAB cache for files_struct structures (tsk->files) */
-kmem_cache_t *files_cachep;
+struct kmem_cache *files_cachep;
 
 /* SLAB cache for fs_struct structures (tsk->fs) */
-kmem_cache_t *fs_cachep;
+struct kmem_cache *fs_cachep;
 
 /* SLAB cache for vm_area_struct structures */
-kmem_cache_t *vm_area_cachep;
+struct kmem_cache *vm_area_cachep;
 
 /* SLAB cache for mm_struct structures (tsk->mm) */
-static kmem_cache_t *mm_cachep;
+static struct kmem_cache *mm_cachep;
 
 void free_task(struct task_struct *tsk)
 {
@@ -235,7 +237,7 @@ static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
                                goto fail_nomem;
                        charge = len;
                }
-               tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+               tmp = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
                if (!tmp)
                        goto fail_nomem;
                *tmp = *mpnt;
@@ -317,7 +319,7 @@ static inline void mm_free_pgd(struct mm_struct * mm)
 
  __cacheline_aligned_in_smp DEFINE_SPINLOCK(mmlist_lock);
 
-#define allocate_mm()  (kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
+#define allocate_mm()  (kmem_cache_alloc(mm_cachep, GFP_KERNEL))
 #define free_mm(mm)    (kmem_cache_free(mm_cachep, (mm)))
 
 #include <linux/init_task.h>
@@ -446,7 +448,16 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
                tsk->vfork_done = NULL;
                complete(vfork_done);
        }
-       if (tsk->clear_child_tid && atomic_read(&mm->mm_users) > 1) {
+
+       /*
+        * If we're exiting normally, clear a user-space tid field if
+        * requested.  We leave this alone when dying by signal, to leave
+        * the value intact in a core dump, and to save the unnecessary
+        * trouble otherwise.  Userland only wants this done for a sys_exit.
+        */
+       if (tsk->clear_child_tid
+           && !(tsk->flags & PF_SIGNALED)
+           && atomic_read(&mm->mm_users) > 1) {
                u32 __user * tidptr = tsk->clear_child_tid;
                tsk->clear_child_tid = NULL;
 
@@ -477,6 +488,10 @@ static struct mm_struct *dup_mm(struct task_struct *tsk)
 
        memcpy(mm, oldmm, sizeof(*mm));
 
+       /* Initializing for Swap token stuff */
+       mm->token_priority = 0;
+       mm->last_interval = 0;
+
        if (!mm_init(mm))
                goto fail_nomem;
 
@@ -540,6 +555,10 @@ static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
                goto fail_nomem;
 
 good_mm:
+       /* Initializing for Swap token stuff */
+       mm->token_priority = 0;
+       mm->last_interval = 0;
+
        tsk->mm = mm;
        tsk->active_mm = mm;
        return 0;
@@ -611,7 +630,7 @@ static struct files_struct *alloc_files(void)
        struct files_struct *newf;
        struct fdtable *fdt;
 
-       newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
+       newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
        if (!newf)
                goto out;
 
@@ -828,7 +847,7 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts
        if (clone_flags & CLONE_THREAD) {
                atomic_inc(&current->signal->count);
                atomic_inc(&current->signal->live);
-               taskstats_tgid_alloc(current->signal);
+               taskstats_tgid_alloc(current);
                return 0;
        }
        sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);
@@ -895,7 +914,6 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts
 void __cleanup_signal(struct signal_struct *sig)
 {
        exit_thread_group_keys(sig);
-       taskstats_tgid_free(sig);
        kmem_cache_free(signal_cachep, sig);
 }
 
@@ -982,6 +1000,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        if (!p)
                goto fork_out;
 
+       rt_mutex_init_task(p);
+
 #ifdef CONFIG_TRACE_IRQFLAGS
        DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
        DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
@@ -1086,8 +1106,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        p->lockdep_recursion = 0;
 #endif
 
-       rt_mutex_init_task(p);
-
 #ifdef CONFIG_DEBUG_MUTEXES
        p->blocked_on = NULL; /* not blocked yet */
 #endif
@@ -1115,11 +1133,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                goto bad_fork_cleanup_signal;
        if ((retval = copy_keys(clone_flags, p)))
                goto bad_fork_cleanup_mm;
-       if ((retval = copy_namespace(clone_flags, p)))
+       if ((retval = copy_namespaces(clone_flags, p)))
                goto bad_fork_cleanup_keys;
        retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
        if (retval)
-               goto bad_fork_cleanup_namespace;
+               goto bad_fork_cleanup_namespaces;
 
        p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
        /*
@@ -1150,7 +1168,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 
        /* Our parent execution domain becomes current domain
           These must match for thread signalling to apply */
-          
        p->parent_exec_id = p->self_exec_id;
 
        /* ok, now we should be set up.. */
@@ -1173,6 +1190,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        /* Need tasklist lock for parent etc handling! */
        write_lock_irq(&tasklist_lock);
 
+       /* for sys_ioprio_set(IOPRIO_WHO_PGRP) */
+       p->ioprio = current->ioprio;
+
        /*
         * The task hasn't been attached yet, so its cpus_allowed mask will
         * not be changed, nor will its assigned CPU.
@@ -1209,7 +1229,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                spin_unlock(&current->sighand->siglock);
                write_unlock_irq(&tasklist_lock);
                retval = -ERESTARTNOINTR;
-               goto bad_fork_cleanup_namespace;
+               goto bad_fork_cleanup_namespaces;
        }
 
        if (clone_flags & CLONE_THREAD) {
@@ -1232,11 +1252,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                }
        }
 
-       /*
-        * inherit ioprio
-        */
-       p->ioprio = current->ioprio;
-
        if (likely(p->pid)) {
                add_parent(p);
                if (unlikely(p->ptrace & PT_PTRACED))
@@ -1262,8 +1277,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        proc_fork_connector(p);
        return p;
 
-bad_fork_cleanup_namespace:
-       exit_namespace(p);
+bad_fork_cleanup_namespaces:
+       exit_task_namespaces(p);
 bad_fork_cleanup_keys:
        exit_keys(p);
 bad_fork_cleanup_mm:
@@ -1317,9 +1332,8 @@ struct task_struct * __devinit fork_idle(int cpu)
        struct pt_regs regs;
 
        task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0);
-       if (!task)
-               return ERR_PTR(-ENOMEM);
-       init_idle(task, cpu);
+       if (!IS_ERR(task))
+               init_idle(task, cpu);
 
        return task;
 }
@@ -1416,7 +1430,7 @@ long do_fork(unsigned long clone_flags,
 #define ARCH_MIN_MMSTRUCT_ALIGN 0
 #endif
 
-static void sighand_ctor(void *data, kmem_cache_t *cachep, unsigned long flags)
+static void sighand_ctor(void *data, struct kmem_cache *cachep, unsigned long flags)
 {
        struct sighand_struct *sighand = data;
 
@@ -1516,10 +1530,9 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
  */
 static int unshare_namespace(unsigned long unshare_flags, struct namespace **new_nsp, struct fs_struct *new_fs)
 {
-       struct namespace *ns = current->namespace;
+       struct namespace *ns = current->nsproxy->namespace;
 
-       if ((unshare_flags & CLONE_NEWNS) &&
-           (ns && atomic_read(&ns->count) > 1)) {
+       if ((unshare_flags & CLONE_NEWNS) && ns) {
                if (!capable(CAP_SYS_ADMIN))
                        return -EPERM;
 
@@ -1591,6 +1604,16 @@ static int unshare_semundo(unsigned long unshare_flags, struct sem_undo_list **n
        return 0;
 }
 
+#ifndef CONFIG_IPC_NS
+static inline int unshare_ipcs(unsigned long flags, struct ipc_namespace **ns)
+{
+       if (flags & CLONE_NEWIPC)
+               return -EINVAL;
+
+       return 0;
+}
+#endif
+
 /*
  * unshare allows a process to 'unshare' part of the process
  * context which was originally shared using clone.  copy_*
@@ -1608,13 +1631,17 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
        struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL;
        struct files_struct *fd, *new_fd = NULL;
        struct sem_undo_list *new_ulist = NULL;
+       struct nsproxy *new_nsproxy = NULL, *old_nsproxy = NULL;
+       struct uts_namespace *uts, *new_uts = NULL;
+       struct ipc_namespace *ipc, *new_ipc = NULL;
 
        check_unshare_flags(&unshare_flags);
 
        /* Return -EINVAL for all unsupported flags */
        err = -EINVAL;
        if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
-                               CLONE_VM|CLONE_FILES|CLONE_SYSVSEM))
+                               CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
+                               CLONE_NEWUTS|CLONE_NEWIPC))
                goto bad_unshare_out;
 
        if ((err = unshare_thread(unshare_flags)))
@@ -1631,11 +1658,30 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
                goto bad_unshare_cleanup_vm;
        if ((err = unshare_semundo(unshare_flags, &new_ulist)))
                goto bad_unshare_cleanup_fd;
+       if ((err = unshare_utsname(unshare_flags, &new_uts)))
+               goto bad_unshare_cleanup_semundo;
+       if ((err = unshare_ipcs(unshare_flags, &new_ipc)))
+               goto bad_unshare_cleanup_uts;
+
+       if (new_ns || new_uts || new_ipc) {
+               old_nsproxy = current->nsproxy;
+               new_nsproxy = dup_namespaces(old_nsproxy);
+               if (!new_nsproxy) {
+                       err = -ENOMEM;
+                       goto bad_unshare_cleanup_ipc;
+               }
+       }
 
-       if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist) {
+       if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist ||
+                               new_uts || new_ipc) {
 
                task_lock(current);
 
+               if (new_nsproxy) {
+                       current->nsproxy = new_nsproxy;
+                       new_nsproxy = old_nsproxy;
+               }
+
                if (new_fs) {
                        fs = current->fs;
                        current->fs = new_fs;
@@ -1643,8 +1689,8 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
                }
 
                if (new_ns) {
-                       ns = current->namespace;
-                       current->namespace = new_ns;
+                       ns = current->nsproxy->namespace;
+                       current->nsproxy->namespace = new_ns;
                        new_ns = ns;
                }
 
@@ -1669,9 +1715,33 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
                        new_fd = fd;
                }
 
+               if (new_uts) {
+                       uts = current->nsproxy->uts_ns;
+                       current->nsproxy->uts_ns = new_uts;
+                       new_uts = uts;
+               }
+
+               if (new_ipc) {
+                       ipc = current->nsproxy->ipc_ns;
+                       current->nsproxy->ipc_ns = new_ipc;
+                       new_ipc = ipc;
+               }
+
                task_unlock(current);
        }
 
+       if (new_nsproxy)
+               put_nsproxy(new_nsproxy);
+
+bad_unshare_cleanup_ipc:
+       if (new_ipc)
+               put_ipc_ns(new_ipc);
+
+bad_unshare_cleanup_uts:
+       if (new_uts)
+               put_uts_ns(new_uts);
+
+bad_unshare_cleanup_semundo:
 bad_unshare_cleanup_fd:
        if (new_fd)
                put_files_struct(new_fd);