Blame view

kernel/ucount.c 5.52 KB
dbec28460   Eric W. Biederman   userns: Add per u...
1
2
3
4
5
6
7
8
9
10
  /*
   *  This program is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU General Public License as
   *  published by the Free Software Foundation, version 2 of the
   *  License.
   */
  
  #include <linux/stat.h>
  #include <linux/sysctl.h>
  #include <linux/slab.h>
f6b2db1a3   Eric W. Biederman   userns: Make the ...
11
  #include <linux/hash.h>
dbec28460   Eric W. Biederman   userns: Add per u...
12
  #include <linux/user_namespace.h>
f6b2db1a3   Eric W. Biederman   userns: Make the ...
13
14
15
16
17
18
19
20
21
  #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...
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
51
52
53
  #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,
  };
b376c3e1b   Eric W. Biederman   userns: Add a lim...
54
55
  static int zero = 0;
  static int int_max = INT_MAX;
25f9c0817   Eric W. Biederman   userns: Generaliz...
56
57
58
59
60
61
62
63
64
  #define UCOUNT_ENTRY(name) 				\
  	{						\
  		.procname	= name,			\
  		.maxlen		= sizeof(int),		\
  		.mode		= 0644,			\
  		.proc_handler	= proc_dointvec_minmax,	\
  		.extra1		= &zero,		\
  		.extra2		= &int_max,		\
  	}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
65
  static struct ctl_table user_table[] = {
25f9c0817   Eric W. Biederman   userns: Generaliz...
66
  	UCOUNT_ENTRY("max_user_namespaces"),
f333c700c   Eric W. Biederman   pidns: Add a limi...
67
  	UCOUNT_ENTRY("max_pid_namespaces"),
f7af3d1c0   Eric W. Biederman   utsns: Add a limi...
68
  	UCOUNT_ENTRY("max_uts_namespaces"),
aba356616   Eric W. Biederman   ipcns: Add a lim...
69
  	UCOUNT_ENTRY("max_ipc_namespaces"),
703286608   Eric W. Biederman   netns: Add a limi...
70
  	UCOUNT_ENTRY("max_net_namespaces"),
537f7ccb3   Eric W. Biederman   mntns: Add a limi...
71
  	UCOUNT_ENTRY("max_mnt_namespaces"),
d08311dd6   Eric W. Biederman   cgroupns: Add a l...
72
  	UCOUNT_ENTRY("max_cgroup_namespaces"),
dbec28460   Eric W. Biederman   userns: Add per u...
73
74
75
76
77
78
79
80
81
  	{ }
  };
  #endif /* CONFIG_SYSCTL */
  
  bool setup_userns_sysctls(struct user_namespace *ns)
  {
  #ifdef CONFIG_SYSCTL
  	struct ctl_table *tbl;
  	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
82
  	tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL);
dbec28460   Eric W. Biederman   userns: Add per u...
83
  	if (tbl) {
25f9c0817   Eric W. Biederman   userns: Generaliz...
84
85
86
87
  		int i;
  		for (i = 0; i < UCOUNT_COUNTS; i++) {
  			tbl[i].data = &ns->ucount_max[i];
  		}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
88
  		ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl);
dbec28460   Eric W. Biederman   userns: Add per u...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  	}
  	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 ...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  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;
c75568677   Nikolay Borisov   userns: Make ucou...
125
  	spin_lock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
126
127
  	ucounts = find_ucounts(ns, uid, hashent);
  	if (!ucounts) {
c75568677   Nikolay Borisov   userns: Make ucou...
128
  		spin_unlock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
129
130
131
132
133
134
135
136
  
  		new = kzalloc(sizeof(*new), GFP_KERNEL);
  		if (!new)
  			return NULL;
  
  		new->ns = ns;
  		new->uid = uid;
  		atomic_set(&new->count, 0);
c75568677   Nikolay Borisov   userns: Make ucou...
137
  		spin_lock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
138
139
140
141
142
143
144
145
146
147
  		ucounts = find_ucounts(ns, uid, hashent);
  		if (ucounts) {
  			kfree(new);
  		} else {
  			hlist_add_head(&new->node, hashent);
  			ucounts = new;
  		}
  	}
  	if (!atomic_add_unless(&ucounts->count, 1, INT_MAX))
  		ucounts = NULL;
c75568677   Nikolay Borisov   userns: Make ucou...
148
  	spin_unlock_irq(&ucounts_lock);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
149
150
151
152
153
  	return ucounts;
  }
  
  static void put_ucounts(struct ucounts *ucounts)
  {
c75568677   Nikolay Borisov   userns: Make ucou...
154
  	unsigned long flags;
f6b2db1a3   Eric W. Biederman   userns: Make the ...
155
  	if (atomic_dec_and_test(&ucounts->count)) {
c75568677   Nikolay Borisov   userns: Make ucou...
156
  		spin_lock_irqsave(&ucounts_lock, flags);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
157
  		hlist_del_init(&ucounts->node);
c75568677   Nikolay Borisov   userns: Make ucou...
158
  		spin_unlock_irqrestore(&ucounts_lock, flags);
f6b2db1a3   Eric W. Biederman   userns: Make the ...
159
160
161
162
  
  		kfree(ucounts);
  	}
  }
b376c3e1b   Eric W. Biederman   userns: Add a lim...
163
164
165
166
167
168
169
170
171
172
173
174
175
  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...
176
177
  struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,
  			   enum ucount_type type)
b376c3e1b   Eric W. Biederman   userns: Add a lim...
178
  {
f6b2db1a3   Eric W. Biederman   userns: Make the ...
179
180
181
182
183
184
  	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...
185
186
  		max = READ_ONCE(tns->ucount_max[type]);
  		if (!atomic_inc_below(&iter->ucount[type], max))
b376c3e1b   Eric W. Biederman   userns: Add a lim...
187
188
  			goto fail;
  	}
f6b2db1a3   Eric W. Biederman   userns: Make the ...
189
  	return ucounts;
b376c3e1b   Eric W. Biederman   userns: Add a lim...
190
  fail:
f6b2db1a3   Eric W. Biederman   userns: Make the ...
191
192
  	bad = iter;
  	for (iter = ucounts; iter != bad; iter = iter->ns->ucounts)
25f9c0817   Eric W. Biederman   userns: Generaliz...
193
  		atomic_dec(&iter->ucount[type]);
b376c3e1b   Eric W. Biederman   userns: Add a lim...
194

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