[RHEL8,COMMIT] userns: associate user_struct with the user_namespace

Submitted by Konstantin Khorenko on Oct. 29, 2020, 12:13 p.m.

Details

Message ID 202010291213.09TCDGK41546137@finist-co8.sw.ru
State New
Series "userns: associate user_struct with the user_namespace"
Headers show

Commit Message

Konstantin Khorenko Oct. 29, 2020, 12:13 p.m.
The commit is pushed to "branch-rh8-4.18.0-193.6.3.vz8.4.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-193.6.3.vz8.4.14
------>
commit c8bab1a0ef8d333f51d674fe70edb281b7484832
Author: Andrey Ryabinin <aryabinin@virtuozzo.com>
Date:   Thu Oct 29 15:13:15 2020 +0300

    userns: associate user_struct with the user_namespace
    
    user_struct contains per-user counters like processes, files,
    sigpending etc which we wouldn't like to share across different
    namespaces.
    Make per-userns uid hastable instead of global.
    This is partial revert of the 7b44ab978b77a
     ("userns: Disassociate user_struct from the user_namespace.")
    
    Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
---
 include/linux/sched/user.h     |  1 +
 include/linux/user_namespace.h |  4 ++++
 kernel/user.c                  | 22 +++++++++++++---------
 kernel/user_namespace.c        | 13 +++++++++++++
 4 files changed, 31 insertions(+), 9 deletions(-)

Patch hide | download patch | download mbox

diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h
index 9a9536fd4fe3..4bf5a723f138 100644
--- a/include/linux/sched/user.h
+++ b/include/linux/sched/user.h
@@ -60,6 +60,7 @@  extern struct user_struct *find_user(kuid_t);
 extern struct user_struct root_user;
 #define INIT_USER (&root_user)
 
+extern struct user_struct * alloc_uid_ns(struct user_namespace *ns, kuid_t);
 
 /* per-UID process charging. */
 extern struct user_struct * alloc_uid(kuid_t);
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index b004d5aeba1f..30493179b756 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -15,6 +15,9 @@ 
 #define UID_GID_MAP_MAX_BASE_EXTENTS 5
 #define UID_GID_MAP_MAX_EXTENTS 340
 
+#define UIDHASH_BITS   (CONFIG_BASE_SMALL ? 3 : 7)
+#define UIDHASH_SZ     (1 << UIDHASH_BITS)
+
 struct uid_gid_extent {
 	u32 first;
 	u32 lower_first;
@@ -73,6 +76,7 @@  struct user_namespace {
 	struct uid_gid_map	gid_map;
 	struct uid_gid_map	projid_map;
 	atomic_t		count;
+	struct hlist_head       uidhash_table[UIDHASH_SZ];
 	struct user_namespace	*parent;
 	int			level;
 	kuid_t			owner;
diff --git a/kernel/user.c b/kernel/user.c
index 0df9b1640b2a..f9f540484499 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -8,6 +8,7 @@ 
  * able to have per-user limits for system resources. 
  */
 
+#include <linux/cred.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -74,14 +75,11 @@  EXPORT_SYMBOL_GPL(init_user_ns);
  * when changing user ID's (ie setuid() and friends).
  */
 
-#define UIDHASH_BITS	(CONFIG_BASE_SMALL ? 3 : 7)
-#define UIDHASH_SZ	(1 << UIDHASH_BITS)
 #define UIDHASH_MASK		(UIDHASH_SZ - 1)
 #define __uidhashfn(uid)	(((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK)
-#define uidhashentry(uid)	(uidhash_table + __uidhashfn((__kuid_val(uid))))
+#define uidhashentry(ns, uid)  ((ns)->uidhash_table + __uidhashfn((__kuid_val(uid))))
 
 static struct kmem_cache *uid_cachep;
-struct hlist_head uidhash_table[UIDHASH_SZ];
 
 /*
  * The uidhash_lock is mostly taken from process context, but it is
@@ -155,9 +153,10 @@  struct user_struct *find_user(kuid_t uid)
 {
 	struct user_struct *ret;
 	unsigned long flags;
+	struct user_namespace *ns = current_user_ns();
 
 	spin_lock_irqsave(&uidhash_lock, flags);
-	ret = uid_hash_find(uid, uidhashentry(uid));
+	ret = uid_hash_find(uid, uidhashentry(ns, uid));
 	spin_unlock_irqrestore(&uidhash_lock, flags);
 	return ret;
 }
@@ -173,9 +172,9 @@  void free_uid(struct user_struct *up)
 		free_user(up, flags);
 }
 
-struct user_struct *alloc_uid(kuid_t uid)
+struct user_struct *alloc_uid_ns(struct user_namespace *ns, kuid_t uid)
 {
-	struct hlist_head *hashent = uidhashentry(uid);
+	struct hlist_head *hashent = uidhashentry(ns, uid);
 	struct user_struct *up, *new;
 
 	spin_lock_irq(&uidhash_lock);
@@ -215,6 +214,11 @@  struct user_struct *alloc_uid(kuid_t uid)
 	return NULL;
 }
 
+struct user_struct *alloc_uid(kuid_t uid)
+{
+	return alloc_uid_ns(current_user_ns(), uid);
+}
+
 static int __init uid_cache_init(void)
 {
 	int n;
@@ -223,11 +227,11 @@  static int __init uid_cache_init(void)
 			0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
 
 	for(n = 0; n < UIDHASH_SZ; ++n)
-		INIT_HLIST_HEAD(uidhash_table + n);
+		INIT_HLIST_HEAD(init_user_ns.uidhash_table + n);
 
 	/* Insert the root user immediately (init already runs as root) */
 	spin_lock_irq(&uidhash_lock);
-	uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID));
+	uid_hash_insert(&root_user, uidhashentry(&init_user_ns, GLOBAL_ROOT_UID));
 	spin_unlock_irq(&uidhash_lock);
 
 	return 0;
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 243fb390d744..459b88044c62 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -74,6 +74,7 @@  static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 int create_user_ns(struct cred *new)
 {
 	struct user_namespace *ns, *parent_ns = new->user_ns;
+	struct user_struct *new_user;
 	kuid_t owner = new->euid;
 	kgid_t group = new->egid;
 	struct ucounts *ucounts;
@@ -116,6 +117,17 @@  int create_user_ns(struct cred *new)
 		goto fail_free;
 	ns->ns.ops = &userns_operations;
 
+	for (i = 0; i < UIDHASH_SZ; ++i)
+		INIT_HLIST_HEAD(ns->uidhash_table + i);
+
+	ret = -ENOMEM;
+	new_user = alloc_uid_ns(ns, owner);
+	if (!new_user)
+		goto fail_uid;
+
+	free_uid(new->user);
+	new->user = new_user;
+
 	atomic_set(&ns->count, 1);
 	/* Leave the new->user_ns reference with the new user namespace. */
 	ns->parent = parent_ns;
@@ -146,6 +158,7 @@  int create_user_ns(struct cred *new)
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 	key_put(ns->persistent_keyring_register);
 #endif
+fail_uid:
 	ns_free_inum(&ns->ns);
 fail_free:
 	kmem_cache_free(user_ns_cachep, ns);