kernel/user.c: fix a memory leak when freeing up non-init usernamespaces users
[sfrench/cifs-2.6.git] / kernel / user.c
index 477b6660f447e639a8b181d456c7c4e64e30b008..fbb300e6191f09d376ed26abb330265fe51bf0a7 100644 (file)
@@ -72,6 +72,7 @@ static void uid_hash_insert(struct user_struct *up, struct hlist_head *hashent)
 static void uid_hash_remove(struct user_struct *up)
 {
        hlist_del_init(&up->uidhash_node);
+       put_user_ns(up->user_ns);
 }
 
 static struct user_struct *uid_hash_find(uid_t uid, struct hlist_head *hashent)
@@ -285,14 +286,12 @@ int __init uids_sysfs_init(void)
 /* work function to remove sysfs directory for a user and free up
  * corresponding structures.
  */
-static void remove_user_sysfs_dir(struct work_struct *w)
+static void cleanup_user_struct(struct work_struct *w)
 {
        struct user_struct *up = container_of(w, struct user_struct, work);
        unsigned long flags;
        int remove_user = 0;
 
-       if (up->user_ns != &init_user_ns)
-               return;
        /* Make uid_hash_remove() + sysfs_remove_file() + kobject_del()
         * atomic.
         */
@@ -311,9 +310,11 @@ static void remove_user_sysfs_dir(struct work_struct *w)
        if (!remove_user)
                goto done;
 
-       kobject_uevent(&up->kobj, KOBJ_REMOVE);
-       kobject_del(&up->kobj);
-       kobject_put(&up->kobj);
+       if (up->user_ns == &init_user_ns) {
+               kobject_uevent(&up->kobj, KOBJ_REMOVE);
+               kobject_del(&up->kobj);
+               kobject_put(&up->kobj);
+       }
 
        sched_destroy_user(up);
        key_put(up->uid_keyring);
@@ -334,8 +335,7 @@ static void free_user(struct user_struct *up, unsigned long flags)
        atomic_inc(&up->__count);
        spin_unlock_irqrestore(&uidhash_lock, flags);
 
-       put_user_ns(up->user_ns);
-       INIT_WORK(&up->work, remove_user_sysfs_dir);
+       INIT_WORK(&up->work, cleanup_user_struct);
        schedule_work(&up->work);
 }
 
@@ -357,12 +357,29 @@ static void free_user(struct user_struct *up, unsigned long flags)
        sched_destroy_user(up);
        key_put(up->uid_keyring);
        key_put(up->session_keyring);
-       put_user_ns(up->user_ns);
        kmem_cache_free(uid_cachep, up);
 }
 
 #endif
 
+#if defined(CONFIG_RT_GROUP_SCHED) && defined(CONFIG_USER_SCHED)
+/*
+ * We need to check if a setuid can take place. This function should be called
+ * before successfully completing the setuid.
+ */
+int task_can_switch_user(struct user_struct *up, struct task_struct *tsk)
+{
+
+       return sched_rt_can_attach(up->tg, tsk);
+
+}
+#else
+int task_can_switch_user(struct user_struct *up, struct task_struct *tsk)
+{
+       return 1;
+}
+#endif
+
 /*
  * Locate the user_struct for the passed UID.  If found, take a ref on it.  The
  * caller must undo that ref with free_uid().