userns: Free user namespaces in process context
authorEric W. Biederman <ebiederm@xmission.com>
Sat, 30 Jul 2016 18:53:37 +0000 (13:53 -0500)
committerEric W. Biederman <ebiederm@xmission.com>
Mon, 8 Aug 2016 14:17:18 +0000 (09:17 -0500)
Add the necessary boiler plate to move freeing of user namespaces into
work queue and thus into process context where things can sleep.

This is a necessary precursor to per user namespace sysctls.

Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
include/linux/user_namespace.h
kernel/user_namespace.c

index 9217169..4e79b3c 100644 (file)
@@ -39,6 +39,7 @@ struct user_namespace {
        struct key              *persistent_keyring_register;
        struct rw_semaphore     persistent_keyring_register_sem;
 #endif
+       struct work_struct      work;
 };
 
 extern struct user_namespace init_user_ns;
@@ -54,12 +55,12 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
 
 extern int create_user_ns(struct cred *new);
 extern int unshare_userns(unsigned long unshare_flags, struct cred **new_cred);
-extern void free_user_ns(struct user_namespace *ns);
+extern void __put_user_ns(struct user_namespace *ns);
 
 static inline void put_user_ns(struct user_namespace *ns)
 {
        if (ns && atomic_dec_and_test(&ns->count))
-               free_user_ns(ns);
+               __put_user_ns(ns);
 }
 
 struct seq_operations;
index 68f5942..5247cdb 100644 (file)
@@ -29,6 +29,7 @@ static DEFINE_MUTEX(userns_state_mutex);
 static bool new_idmap_permitted(const struct file *file,
                                struct user_namespace *ns, int cap_setid,
                                struct uid_gid_map *map);
+static void free_user_ns(struct work_struct *work);
 
 static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 {
@@ -101,6 +102,7 @@ int create_user_ns(struct cred *new)
        ns->level = parent_ns->level + 1;
        ns->owner = owner;
        ns->group = group;
+       INIT_WORK(&ns->work, free_user_ns);
 
        /* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
        mutex_lock(&userns_state_mutex);
@@ -135,9 +137,10 @@ int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
        return err;
 }
 
-void free_user_ns(struct user_namespace *ns)
+static void free_user_ns(struct work_struct *work)
 {
-       struct user_namespace *parent;
+       struct user_namespace *parent, *ns =
+               container_of(work, struct user_namespace, work);
 
        do {
                parent = ns->parent;
@@ -149,7 +152,12 @@ void free_user_ns(struct user_namespace *ns)
                ns = parent;
        } while (atomic_dec_and_test(&parent->count));
 }
-EXPORT_SYMBOL(free_user_ns);
+
+void __put_user_ns(struct user_namespace *ns)
+{
+       schedule_work(&ns->work);
+}
+EXPORT_SYMBOL(__put_user_ns);
 
 static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count)
 {