Blame view

kernel/ucount.c 5.61 KB
b886d83c5   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
dbec28460   Eric W. Biederman   userns: Add per u...
2
3
4
5
  
  #include <linux/stat.h>
  #include <linux/sysctl.h>
  #include <linux/slab.h>
5b825c3af   Ingo Molnar   sched/headers: Pr...
6
  #include <linux/cred.h>
f6b2db1a3   Eric W. Biederman   userns: Make the ...
7
  #include <linux/hash.h>
514c60324   Randy Dunlap   headers: untangle...
8
  #include <linux/kmemleak.h>
dbec28460   Eric W. Biederman   userns: Add per u...
9
  #include <linux/user_namespace.h>
f6b2db1a3   Eric W. Biederman   userns: Make the ...
10
11
12
13
14
15
16
17
18
  #define UCOUNTS_HASHTABLE_BITS 10
  static struct hlist_head ucounts_hashtable[(1 << UCOUNTS_HASHTABLE_BITS)];
  static DEFINE_SPINLOCK(ucounts_lock);
  
  #define ucounts_hashfn(ns, uid)						\
  	hash_long((unsigned long)__kuid_val(uid) + (unsigned long)(ns), \
  		  UCOUNTS_HASHTABLE_BITS)
  #define ucounts_hashentry(ns, uid)	\
  	(ucounts_hashtable + ucounts_hashfn(ns, uid))
dbec28460   Eric W. Biederman   userns: Add per u...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  #ifdef CONFIG_SYSCTL
  static struct ctl_table_set *
  set_lookup(struct ctl_table_root *root)
  {
  	return &current_user_ns()->set;
  }
  
  static int set_is_seen(struct ctl_table_set *set)
  {
  	return &current_user_ns()->set == set;
  }
  
  static int set_permissions(struct ctl_table_header *head,
  				  struct ctl_table *table)
  {
  	struct user_namespace *user_ns =
  		container_of(head->set, struct user_namespace, set);
  	int mode;
  
  	/* Allow users with CAP_SYS_RESOURCE unrestrained access */
  	if (ns_capable(user_ns, CAP_SYS_RESOURCE))
  		mode = (table->mode & S_IRWXU) >> 6;
  	else
  	/* Allow all others at most read-only access */
  		mode = table->mode & S_IROTH;
  	return (mode << 6) | (mode << 3) | mode;
  }
  
  static struct ctl_table_root set_root = {
  	.lookup = set_lookup,
  	.permissions = set_permissions,
  };
1cce1eea0   Nikolay Borisov   inotify: Convert ...
51
  #define UCOUNT_ENTRY(name)				\
25f9c0817   Eric W. Biederman   userns: Generaliz...
52
53
54
55
56
  	{						\
  		.procname	= name,			\
  		.maxlen		= sizeof(int),		\
  		.mode		= 0644,			\
  		.proc_handler	= proc_dointvec_minmax,	\
eec4844fa   Matteo Croce   proc/sysctl: add ...
57
58
  		.extra1		= SYSCTL_ZERO,		\
  		.extra2		= SYSCTL_INT_MAX,	\
25f9c0817   Eric W. Biederman   userns: Generaliz...
59
  	}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
60
  static struct ctl_table user_table[] = {
25f9c0817   Eric W. Biederman   userns: Generaliz...
61
  	UCOUNT_ENTRY("max_user_namespaces"),
f333c700c   Eric W. Biederman   pidns: Add a limi...
62
  	UCOUNT_ENTRY("max_pid_namespaces"),
f7af3d1c0   Eric W. Biederman   utsns: Add a limi...
63
  	UCOUNT_ENTRY("max_uts_namespaces"),
aba356616   Eric W. Biederman   ipcns: Add a lim...
64
  	UCOUNT_ENTRY("max_ipc_namespaces"),
703286608   Eric W. Biederman   netns: Add a limi...
65
  	UCOUNT_ENTRY("max_net_namespaces"),
537f7ccb3   Eric W. Biederman   mntns: Add a limi...
66
  	UCOUNT_ENTRY("max_mnt_namespaces"),
d08311dd6   Eric W. Biederman   cgroupns: Add a l...
67
  	UCOUNT_ENTRY("max_cgroup_namespaces"),
eeec26d5d   Dmitry Safonov   time/namespace: A...
68
  	UCOUNT_ENTRY("max_time_namespaces"),
1cce1eea0   Nikolay Borisov   inotify: Convert ...
69
70
71
72
  #ifdef CONFIG_INOTIFY_USER
  	UCOUNT_ENTRY("max_inotify_instances"),
  	UCOUNT_ENTRY("max_inotify_watches"),
  #endif
dbec28460   Eric W. Biederman   userns: Add per u...
73
74
75
76
77
78
79
80
  	{ }
  };
  #endif /* CONFIG_SYSCTL */
  
  bool setup_userns_sysctls(struct user_namespace *ns)
  {
  #ifdef CONFIG_SYSCTL
  	struct ctl_table *tbl;
0f538e3e7   Jan Kara   ucount: Make sure...
81
82
  
  	BUILD_BUG_ON(ARRAY_SIZE(user_table) != UCOUNT_COUNTS + 1);
dbec28460   Eric W. Biederman   userns: Add per u...
83
  	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
84
  	tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL);
dbec28460   Eric W. Biederman   userns: Add per u...
85
  	if (tbl) {
25f9c0817   Eric W. Biederman   userns: Generaliz...
86
87
88
89
  		int i;
  		for (i = 0; i < UCOUNT_COUNTS; i++) {
  			tbl[i].data = &ns->ucount_max[i];
  		}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
90
  		ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl);
dbec28460   Eric W. Biederman   userns: Add per u...
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  	}
  	if (!ns->sysctls) {
  		kfree(tbl);
  		retire_sysctl_set(&ns->set);
  		return false;
  	}
  #endif
  	return true;
  }
  
  void retire_userns_sysctls(struct user_namespace *ns)
  {
  #ifdef CONFIG_SYSCTL
  	struct ctl_table *tbl;
  
  	tbl = ns->sysctls->ctl_table_arg;
  	unregister_sysctl_table(ns->sysctls);
  	retire_sysctl_set(&ns->set);
  	kfree(tbl);
  #endif
  }
f6b2db1a3   Eric W. Biederman   userns: Make the ...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid, struct hlist_head *hashent)
  {
  	struct ucounts *ucounts;
  
  	hlist_for_each_entry(ucounts, hashent, node) {
  		if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns))
  			return ucounts;
  	}
  	return NULL;
  }
  
  static struct ucounts *get_ucounts(struct user_namespace *ns, kuid_t uid)
  {
  	struct hlist_head *hashent = ucounts_hashentry(ns, uid);
  	struct ucounts *ucounts, *new;
880a38547   Nikolay Borisov   userns: Make ucou...
127
  	spin_lock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
128
129
  	ucounts = find_ucounts(ns, uid, hashent);
  	if (!ucounts) {
880a38547   Nikolay Borisov   userns: Make ucou...
130
  		spin_unlock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
131
132
133
134
135
136
137
  
  		new = kzalloc(sizeof(*new), GFP_KERNEL);
  		if (!new)
  			return NULL;
  
  		new->ns = ns;
  		new->uid = uid;
040757f73   Eric W. Biederman   ucount: Remove th...
138
  		new->count = 0;
f6b2db1a3   Eric W. Biederman   userns: Make the ...
139

880a38547   Nikolay Borisov   userns: Make ucou...
140
  		spin_lock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
141
142
143
144
145
146
147
148
  		ucounts = find_ucounts(ns, uid, hashent);
  		if (ucounts) {
  			kfree(new);
  		} else {
  			hlist_add_head(&new->node, hashent);
  			ucounts = new;
  		}
  	}
040757f73   Eric W. Biederman   ucount: Remove th...
149
  	if (ucounts->count == INT_MAX)
f6b2db1a3   Eric W. Biederman   userns: Make the ...
150
  		ucounts = NULL;
040757f73   Eric W. Biederman   ucount: Remove th...
151
152
  	else
  		ucounts->count += 1;
880a38547   Nikolay Borisov   userns: Make ucou...
153
  	spin_unlock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
154
155
156
157
158
  	return ucounts;
  }
  
  static void put_ucounts(struct ucounts *ucounts)
  {
880a38547   Nikolay Borisov   userns: Make ucou...
159
  	unsigned long flags;
040757f73   Eric W. Biederman   ucount: Remove th...
160
161
162
  	spin_lock_irqsave(&ucounts_lock, flags);
  	ucounts->count -= 1;
  	if (!ucounts->count)
f6b2db1a3   Eric W. Biederman   userns: Make the ...
163
  		hlist_del_init(&ucounts->node);
040757f73   Eric W. Biederman   ucount: Remove th...
164
165
166
  	else
  		ucounts = NULL;
  	spin_unlock_irqrestore(&ucounts_lock, flags);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
167

040757f73   Eric W. Biederman   ucount: Remove th...
168
  	kfree(ucounts);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
169
  }
b376c3e1b   Eric W. Biederman   userns: Add a lim...
170
171
172
173
174
175
176
177
178
179
180
181
182
  static inline bool atomic_inc_below(atomic_t *v, int u)
  {
  	int c, old;
  	c = atomic_read(v);
  	for (;;) {
  		if (unlikely(c >= u))
  			return false;
  		old = atomic_cmpxchg(v, c, c+1);
  		if (likely(old == c))
  			return true;
  		c = old;
  	}
  }
25f9c0817   Eric W. Biederman   userns: Generaliz...
183
184
  struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,
  			   enum ucount_type type)
b376c3e1b   Eric W. Biederman   userns: Add a lim...
185
  {
f6b2db1a3   Eric W. Biederman   userns: Make the ...
186
187
188
189
190
191
  	struct ucounts *ucounts, *iter, *bad;
  	struct user_namespace *tns;
  	ucounts = get_ucounts(ns, uid);
  	for (iter = ucounts; iter; iter = tns->ucounts) {
  		int max;
  		tns = iter->ns;
25f9c0817   Eric W. Biederman   userns: Generaliz...
192
193
  		max = READ_ONCE(tns->ucount_max[type]);
  		if (!atomic_inc_below(&iter->ucount[type], max))
b376c3e1b   Eric W. Biederman   userns: Add a lim...
194
195
  			goto fail;
  	}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
196
  	return ucounts;
b376c3e1b   Eric W. Biederman   userns: Add a lim...
197
  fail:
f6b2db1a3   Eric W. Biederman   userns: Make the ...
198
199
  	bad = iter;
  	for (iter = ucounts; iter != bad; iter = iter->ns->ucounts)
25f9c0817   Eric W. Biederman   userns: Generaliz...
200
  		atomic_dec(&iter->ucount[type]);
b376c3e1b   Eric W. Biederman   userns: Add a lim...
201

f6b2db1a3   Eric W. Biederman   userns: Make the ...
202
203
  	put_ucounts(ucounts);
  	return NULL;
b376c3e1b   Eric W. Biederman   userns: Add a lim...
204
  }
25f9c0817   Eric W. Biederman   userns: Generaliz...
205
  void dec_ucount(struct ucounts *ucounts, enum ucount_type type)
b376c3e1b   Eric W. Biederman   userns: Add a lim...
206
  {
f6b2db1a3   Eric W. Biederman   userns: Make the ...
207
208
  	struct ucounts *iter;
  	for (iter = ucounts; iter; iter = iter->ns->ucounts) {
25f9c0817   Eric W. Biederman   userns: Generaliz...
209
  		int dec = atomic_dec_if_positive(&iter->ucount[type]);
b376c3e1b   Eric W. Biederman   userns: Add a lim...
210
211
  		WARN_ON_ONCE(dec < 0);
  	}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
212
  	put_ucounts(ucounts);
b376c3e1b   Eric W. Biederman   userns: Add a lim...
213
  }
dbec28460   Eric W. Biederman   userns: Add per u...
214
215
216
  static __init int user_namespace_sysctl_init(void)
  {
  #ifdef CONFIG_SYSCTL
f6b2db1a3   Eric W. Biederman   userns: Make the ...
217
  	static struct ctl_table_header *user_header;
dbec28460   Eric W. Biederman   userns: Add per u...
218
219
  	static struct ctl_table empty[1];
  	/*
f6b2db1a3   Eric W. Biederman   userns: Make the ...
220
  	 * It is necessary to register the user directory in the
dbec28460   Eric W. Biederman   userns: Add per u...
221
222
223
  	 * default set so that registrations in the child sets work
  	 * properly.
  	 */
f6b2db1a3   Eric W. Biederman   userns: Make the ...
224
  	user_header = register_sysctl("user", empty);
ed5bd7dc8   Luis R. Rodriguez   kernel/ucount.c: ...
225
  	kmemleak_ignore(user_header);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
226
  	BUG_ON(!user_header);
dbec28460   Eric W. Biederman   userns: Add per u...
227
228
229
230
231
  	BUG_ON(!setup_userns_sysctls(&init_user_ns));
  #endif
  	return 0;
  }
  subsys_initcall(user_namespace_sysctl_init);