xref: /linux-6.15/kernel/user.c (revision 2077006d)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * The "user cache".
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * (C) Copyright 1991-2000 Linus Torvalds
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * We have a per-user structure to keep track of how many
81da177e4SLinus Torvalds  * processes, files etc the user has claimed, in order to be
91da177e4SLinus Torvalds  * able to have per-user limits for system resources.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/init.h>
131da177e4SLinus Torvalds #include <linux/sched.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/bitops.h>
161da177e4SLinus Torvalds #include <linux/key.h>
178703e8a4SIngo Molnar #include <linux/sched/user.h>
184021cb27SIngo Molnar #include <linux/interrupt.h>
199984de1aSPaul Gortmaker #include <linux/export.h>
20acce292cSCedric Le Goater #include <linux/user_namespace.h>
2121ca59b3SChristian Brauner #include <linux/binfmts.h>
220bb80f24SDavid Howells #include <linux/proc_ns.h>
231da177e4SLinus Torvalds 
2421ca59b3SChristian Brauner #if IS_ENABLED(CONFIG_BINFMT_MISC)
2521ca59b3SChristian Brauner struct binfmt_misc init_binfmt_misc = {
2621ca59b3SChristian Brauner 	.entries = LIST_HEAD_INIT(init_binfmt_misc.entries),
2721ca59b3SChristian Brauner 	.enabled = true,
2821ca59b3SChristian Brauner 	.entries_lock = __RW_LOCK_UNLOCKED(init_binfmt_misc.entries_lock),
2921ca59b3SChristian Brauner };
3021ca59b3SChristian Brauner EXPORT_SYMBOL_GPL(init_binfmt_misc);
3121ca59b3SChristian Brauner #endif
3221ca59b3SChristian Brauner 
3359607db3SSerge E. Hallyn /*
3459607db3SSerge E. Hallyn  * userns count is 1 for root user, 1 for init_uts_ns,
3559607db3SSerge E. Hallyn  * and 1 for... ?
3659607db3SSerge E. Hallyn  */
37aee16ce7SPavel Emelyanov struct user_namespace init_user_ns = {
3822d917d8SEric W. Biederman 	.uid_map = {
39aa4bf44dSChristian Brauner 		{
4022d917d8SEric W. Biederman 			.extent[0] = {
4122d917d8SEric W. Biederman 				.first = 0,
4222d917d8SEric W. Biederman 				.lower_first = 0,
434b06a81fSEric W. Biederman 				.count = 4294967295U,
4422d917d8SEric W. Biederman 			},
45*2077006dSChristian Brauner 			.nr_extents = 1,
4622d917d8SEric W. Biederman 		},
47aa4bf44dSChristian Brauner 	},
4822d917d8SEric W. Biederman 	.gid_map = {
49aa4bf44dSChristian Brauner 		{
5022d917d8SEric W. Biederman 			.extent[0] = {
5122d917d8SEric W. Biederman 				.first = 0,
5222d917d8SEric W. Biederman 				.lower_first = 0,
534b06a81fSEric W. Biederman 				.count = 4294967295U,
5422d917d8SEric W. Biederman 			},
55*2077006dSChristian Brauner 			.nr_extents = 1,
5622d917d8SEric W. Biederman 		},
57aa4bf44dSChristian Brauner 	},
58f76d207aSEric W. Biederman 	.projid_map = {
59aa4bf44dSChristian Brauner 		{
60f76d207aSEric W. Biederman 			.extent[0] = {
61f76d207aSEric W. Biederman 				.first = 0,
62f76d207aSEric W. Biederman 				.lower_first = 0,
63f76d207aSEric W. Biederman 				.count = 4294967295U,
64f76d207aSEric W. Biederman 			},
65*2077006dSChristian Brauner 			.nr_extents = 1,
66f76d207aSEric W. Biederman 		},
67aa4bf44dSChristian Brauner 	},
68265cbd62SKirill Tkhai 	.ns.count = REFCOUNT_INIT(3),
69783291e6SEric W. Biederman 	.owner = GLOBAL_ROOT_UID,
70783291e6SEric W. Biederman 	.group = GLOBAL_ROOT_GID,
71435d5f4bSAl Viro 	.ns.inum = PROC_USER_INIT_INO,
7233c42940SAl Viro #ifdef CONFIG_USER_NS
7333c42940SAl Viro 	.ns.ops = &userns_operations,
7433c42940SAl Viro #endif
759cc46516SEric W. Biederman 	.flags = USERNS_INIT_FLAGS,
76b206f281SDavid Howells #ifdef CONFIG_KEYS
77b206f281SDavid Howells 	.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
780f44e4d9SDavid Howells 	.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
79f36f8c75SDavid Howells #endif
8021ca59b3SChristian Brauner #if IS_ENABLED(CONFIG_BINFMT_MISC)
8121ca59b3SChristian Brauner 	.binfmt_misc = &init_binfmt_misc,
8221ca59b3SChristian Brauner #endif
83aee16ce7SPavel Emelyanov };
84aee16ce7SPavel Emelyanov EXPORT_SYMBOL_GPL(init_user_ns);
85aee16ce7SPavel Emelyanov 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  * UID task count cache, to get fast user lookup in "alloc_uid"
881da177e4SLinus Torvalds  * when changing user ID's (ie setuid() and friends).
891da177e4SLinus Torvalds  */
901da177e4SLinus Torvalds 
91b3e90f37SYoann Congal #define UIDHASH_BITS	(IS_ENABLED(CONFIG_BASE_SMALL) ? 3 : 7)
927b44ab97SEric W. Biederman #define UIDHASH_SZ	(1 << UIDHASH_BITS)
931da177e4SLinus Torvalds #define UIDHASH_MASK		(UIDHASH_SZ - 1)
941da177e4SLinus Torvalds #define __uidhashfn(uid)	(((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK)
957b44ab97SEric W. Biederman #define uidhashentry(uid)	(uidhash_table + __uidhashfn((__kuid_val(uid))))
961da177e4SLinus Torvalds 
97e18b890bSChristoph Lameter static struct kmem_cache *uid_cachep;
98de83dbd9SJason Yan static struct hlist_head uidhash_table[UIDHASH_SZ];
994021cb27SIngo Molnar 
1004021cb27SIngo Molnar /*
1014021cb27SIngo Molnar  * The uidhash_lock is mostly taken from process context, but it is
1024021cb27SIngo Molnar  * occasionally also taken from softirq/tasklet context, when
1034021cb27SIngo Molnar  * task-structs get RCU-freed. Hence all locking must be softirq-safe.
1043fa97c9dSAndrew Morton  * But free_uid() is also called with local interrupts disabled, and running
1053fa97c9dSAndrew Morton  * local_bh_enable() with local interrupts disabled is an error - we'll run
1063fa97c9dSAndrew Morton  * softirq callbacks, and they can unconditionally enable interrupts, and
1073fa97c9dSAndrew Morton  * the caller of free_uid() didn't expect that..
1084021cb27SIngo Molnar  */
1091da177e4SLinus Torvalds static DEFINE_SPINLOCK(uidhash_lock);
1101da177e4SLinus Torvalds 
111783291e6SEric W. Biederman /* root_user.__count is 1, for init task cred */
1121da177e4SLinus Torvalds struct user_struct root_user = {
113fc371912SSebastian Andrzej Siewior 	.__count	= REFCOUNT_INIT(1),
1147b44ab97SEric W. Biederman 	.uid		= GLOBAL_ROOT_UID,
115bef3efbeSLuck, Tony 	.ratelimit	= RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0),
1161da177e4SLinus Torvalds };
1171da177e4SLinus Torvalds 
1185cb350baSDhaval Giani /*
1195cb350baSDhaval Giani  * These routines must be called with the uidhash spinlock held!
1205cb350baSDhaval Giani  */
uid_hash_insert(struct user_struct * up,struct hlist_head * hashent)12140aeb400SAlexey Dobriyan static void uid_hash_insert(struct user_struct *up, struct hlist_head *hashent)
1225cb350baSDhaval Giani {
1235cb350baSDhaval Giani 	hlist_add_head(&up->uidhash_node, hashent);
1245cb350baSDhaval Giani }
1255cb350baSDhaval Giani 
uid_hash_remove(struct user_struct * up)12640aeb400SAlexey Dobriyan static void uid_hash_remove(struct user_struct *up)
1275cb350baSDhaval Giani {
1285cb350baSDhaval Giani 	hlist_del_init(&up->uidhash_node);
1295cb350baSDhaval Giani }
1305cb350baSDhaval Giani 
uid_hash_find(kuid_t uid,struct hlist_head * hashent)1317b44ab97SEric W. Biederman static struct user_struct *uid_hash_find(kuid_t uid, struct hlist_head *hashent)
1323959214fSKay Sievers {
1333959214fSKay Sievers 	struct user_struct *user;
1343959214fSKay Sievers 
135b67bfe0dSSasha Levin 	hlist_for_each_entry(user, hashent, uidhash_node) {
1367b44ab97SEric W. Biederman 		if (uid_eq(user->uid, uid)) {
137fc371912SSebastian Andrzej Siewior 			refcount_inc(&user->__count);
1383959214fSKay Sievers 			return user;
1393959214fSKay Sievers 		}
1403959214fSKay Sievers 	}
1413959214fSKay Sievers 
1423959214fSKay Sievers 	return NULL;
1433959214fSKay Sievers }
1443959214fSKay Sievers 
user_epoll_alloc(struct user_struct * up)1451e1c1583SNicholas Piggin static int user_epoll_alloc(struct user_struct *up)
1461e1c1583SNicholas Piggin {
1471e1c1583SNicholas Piggin #ifdef CONFIG_EPOLL
1481e1c1583SNicholas Piggin 	return percpu_counter_init(&up->epoll_watches, 0, GFP_KERNEL);
1491e1c1583SNicholas Piggin #else
1501e1c1583SNicholas Piggin 	return 0;
1511e1c1583SNicholas Piggin #endif
1521e1c1583SNicholas Piggin }
1531e1c1583SNicholas Piggin 
user_epoll_free(struct user_struct * up)1541e1c1583SNicholas Piggin static void user_epoll_free(struct user_struct *up)
1551e1c1583SNicholas Piggin {
1561e1c1583SNicholas Piggin #ifdef CONFIG_EPOLL
1571e1c1583SNicholas Piggin 	percpu_counter_destroy(&up->epoll_watches);
1581e1c1583SNicholas Piggin #endif
1591e1c1583SNicholas Piggin }
1601e1c1583SNicholas Piggin 
1615cb350baSDhaval Giani /* IRQs are disabled and uidhash_lock is held upon function entry.
1625cb350baSDhaval Giani  * IRQ state (as stored in flags) is restored and uidhash_lock released
1635cb350baSDhaval Giani  * upon function exit.
1645cb350baSDhaval Giani  */
free_user(struct user_struct * up,unsigned long flags)16518b6e041SSerge Hallyn static void free_user(struct user_struct *up, unsigned long flags)
166571428beSNamhyung Kim 	__releases(&uidhash_lock)
1675cb350baSDhaval Giani {
1685cb350baSDhaval Giani 	uid_hash_remove(up);
1695cb350baSDhaval Giani 	spin_unlock_irqrestore(&uidhash_lock, flags);
1701e1c1583SNicholas Piggin 	user_epoll_free(up);
1715cb350baSDhaval Giani 	kmem_cache_free(uid_cachep, up);
1725cb350baSDhaval Giani }
17324e377a8SSrivatsa Vaddagiri 
1741da177e4SLinus Torvalds /*
1751da177e4SLinus Torvalds  * Locate the user_struct for the passed UID.  If found, take a ref on it.  The
1761da177e4SLinus Torvalds  * caller must undo that ref with free_uid().
1771da177e4SLinus Torvalds  *
1781da177e4SLinus Torvalds  * If the user_struct could not be found, return NULL.
1791da177e4SLinus Torvalds  */
find_user(kuid_t uid)1807b44ab97SEric W. Biederman struct user_struct *find_user(kuid_t uid)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds 	struct user_struct *ret;
1833fa97c9dSAndrew Morton 	unsigned long flags;
1841da177e4SLinus Torvalds 
1853fa97c9dSAndrew Morton 	spin_lock_irqsave(&uidhash_lock, flags);
1867b44ab97SEric W. Biederman 	ret = uid_hash_find(uid, uidhashentry(uid));
1873fa97c9dSAndrew Morton 	spin_unlock_irqrestore(&uidhash_lock, flags);
1881da177e4SLinus Torvalds 	return ret;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds 
free_uid(struct user_struct * up)1911da177e4SLinus Torvalds void free_uid(struct user_struct *up)
1921da177e4SLinus Torvalds {
1933fa97c9dSAndrew Morton 	unsigned long flags;
1943fa97c9dSAndrew Morton 
19536f57413SAndrew Morton 	if (!up)
19636f57413SAndrew Morton 		return;
19736f57413SAndrew Morton 
198ce0a568dSAnna-Maria Gleixner 	if (refcount_dec_and_lock_irqsave(&up->__count, &uidhash_lock, &flags))
1995cb350baSDhaval Giani 		free_user(up, flags);
2001da177e4SLinus Torvalds }
201ce5a23c8SJason Gunthorpe EXPORT_SYMBOL_GPL(free_uid);
2021da177e4SLinus Torvalds 
alloc_uid(kuid_t uid)2037b44ab97SEric W. Biederman struct user_struct *alloc_uid(kuid_t uid)
2041da177e4SLinus Torvalds {
2057b44ab97SEric W. Biederman 	struct hlist_head *hashent = uidhashentry(uid);
2068eb703e4SPavel Emelyanov 	struct user_struct *up, *new;
2071da177e4SLinus Torvalds 
2083fa97c9dSAndrew Morton 	spin_lock_irq(&uidhash_lock);
2091da177e4SLinus Torvalds 	up = uid_hash_find(uid, hashent);
2103fa97c9dSAndrew Morton 	spin_unlock_irq(&uidhash_lock);
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	if (!up) {
213354a1f4dSAndrew Morton 		new = kmem_cache_zalloc(uid_cachep, GFP_KERNEL);
2148eb703e4SPavel Emelyanov 		if (!new)
2156c4e121fSRasmus Villemoes 			return NULL;
2165e8869bbSPavel Emelyanov 
2171da177e4SLinus Torvalds 		new->uid = uid;
218fc371912SSebastian Andrzej Siewior 		refcount_set(&new->__count, 1);
2191e1c1583SNicholas Piggin 		if (user_epoll_alloc(new)) {
2201e1c1583SNicholas Piggin 			kmem_cache_free(uid_cachep, new);
2211e1c1583SNicholas Piggin 			return NULL;
2221e1c1583SNicholas Piggin 		}
223bef3efbeSLuck, Tony 		ratelimit_state_init(&new->ratelimit, HZ, 100);
224bef3efbeSLuck, Tony 		ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE);
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 		/*
2271da177e4SLinus Torvalds 		 * Before adding this, check whether we raced
2281da177e4SLinus Torvalds 		 * on adding the same user already..
2291da177e4SLinus Torvalds 		 */
2303fa97c9dSAndrew Morton 		spin_lock_irq(&uidhash_lock);
2311da177e4SLinus Torvalds 		up = uid_hash_find(uid, hashent);
2321da177e4SLinus Torvalds 		if (up) {
2331e1c1583SNicholas Piggin 			user_epoll_free(new);
2341da177e4SLinus Torvalds 			kmem_cache_free(uid_cachep, new);
2351da177e4SLinus Torvalds 		} else {
2361da177e4SLinus Torvalds 			uid_hash_insert(new, hashent);
2371da177e4SLinus Torvalds 			up = new;
2381da177e4SLinus Torvalds 		}
2393fa97c9dSAndrew Morton 		spin_unlock_irq(&uidhash_lock);
2401da177e4SLinus Torvalds 	}
2415cb350baSDhaval Giani 
2421da177e4SLinus Torvalds 	return up;
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds 
uid_cache_init(void)2451da177e4SLinus Torvalds static int __init uid_cache_init(void)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	int n;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	uid_cachep = kmem_cache_create("uid_cache", sizeof(struct user_struct),
25020c2df83SPaul Mundt 			0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	for(n = 0; n < UIDHASH_SZ; ++n)
2537b44ab97SEric W. Biederman 		INIT_HLIST_HEAD(uidhash_table + n);
2541da177e4SLinus Torvalds 
2551e1c1583SNicholas Piggin 	if (user_epoll_alloc(&root_user))
2561e1c1583SNicholas Piggin 		panic("root_user epoll percpu counter alloc failed");
2571e1c1583SNicholas Piggin 
2581da177e4SLinus Torvalds 	/* Insert the root user immediately (init already runs as root) */
2593fa97c9dSAndrew Morton 	spin_lock_irq(&uidhash_lock);
2607b44ab97SEric W. Biederman 	uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID));
2613fa97c9dSAndrew Morton 	spin_unlock_irq(&uidhash_lock);
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	return 0;
2641da177e4SLinus Torvalds }
265c96d6660SPaul Gortmaker subsys_initcall(uid_cache_init);
266