xref: /OK3568_Linux_fs/kernel/security/device_cgroup.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * device_cgroup.c - device cgroup subsystem
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright 2007 IBM Corp
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/device_cgroup.h>
9*4882a593Smuzhiyun #include <linux/cgroup.h>
10*4882a593Smuzhiyun #include <linux/ctype.h>
11*4882a593Smuzhiyun #include <linux/list.h>
12*4882a593Smuzhiyun #include <linux/uaccess.h>
13*4882a593Smuzhiyun #include <linux/seq_file.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <linux/rcupdate.h>
16*4882a593Smuzhiyun #include <linux/mutex.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #ifdef CONFIG_CGROUP_DEVICE
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static DEFINE_MUTEX(devcgroup_mutex);
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun enum devcg_behavior {
23*4882a593Smuzhiyun 	DEVCG_DEFAULT_NONE,
24*4882a593Smuzhiyun 	DEVCG_DEFAULT_ALLOW,
25*4882a593Smuzhiyun 	DEVCG_DEFAULT_DENY,
26*4882a593Smuzhiyun };
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * exception list locking rules:
30*4882a593Smuzhiyun  * hold devcgroup_mutex for update/read.
31*4882a593Smuzhiyun  * hold rcu_read_lock() for read.
32*4882a593Smuzhiyun  */
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun struct dev_exception_item {
35*4882a593Smuzhiyun 	u32 major, minor;
36*4882a593Smuzhiyun 	short type;
37*4882a593Smuzhiyun 	short access;
38*4882a593Smuzhiyun 	struct list_head list;
39*4882a593Smuzhiyun 	struct rcu_head rcu;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun struct dev_cgroup {
43*4882a593Smuzhiyun 	struct cgroup_subsys_state css;
44*4882a593Smuzhiyun 	struct list_head exceptions;
45*4882a593Smuzhiyun 	enum devcg_behavior behavior;
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun 
css_to_devcgroup(struct cgroup_subsys_state * s)48*4882a593Smuzhiyun static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	return s ? container_of(s, struct dev_cgroup, css) : NULL;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
task_devcgroup(struct task_struct * task)53*4882a593Smuzhiyun static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun 	return css_to_devcgroup(task_css(task, devices_cgrp_id));
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /*
59*4882a593Smuzhiyun  * called under devcgroup_mutex
60*4882a593Smuzhiyun  */
dev_exceptions_copy(struct list_head * dest,struct list_head * orig)61*4882a593Smuzhiyun static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	struct dev_exception_item *ex, *tmp, *new;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	lockdep_assert_held(&devcgroup_mutex);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	list_for_each_entry(ex, orig, list) {
68*4882a593Smuzhiyun 		new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
69*4882a593Smuzhiyun 		if (!new)
70*4882a593Smuzhiyun 			goto free_and_exit;
71*4882a593Smuzhiyun 		list_add_tail(&new->list, dest);
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	return 0;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun free_and_exit:
77*4882a593Smuzhiyun 	list_for_each_entry_safe(ex, tmp, dest, list) {
78*4882a593Smuzhiyun 		list_del(&ex->list);
79*4882a593Smuzhiyun 		kfree(ex);
80*4882a593Smuzhiyun 	}
81*4882a593Smuzhiyun 	return -ENOMEM;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /*
85*4882a593Smuzhiyun  * called under devcgroup_mutex
86*4882a593Smuzhiyun  */
dev_exception_add(struct dev_cgroup * dev_cgroup,struct dev_exception_item * ex)87*4882a593Smuzhiyun static int dev_exception_add(struct dev_cgroup *dev_cgroup,
88*4882a593Smuzhiyun 			     struct dev_exception_item *ex)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	struct dev_exception_item *excopy, *walk;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	lockdep_assert_held(&devcgroup_mutex);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
95*4882a593Smuzhiyun 	if (!excopy)
96*4882a593Smuzhiyun 		return -ENOMEM;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
99*4882a593Smuzhiyun 		if (walk->type != ex->type)
100*4882a593Smuzhiyun 			continue;
101*4882a593Smuzhiyun 		if (walk->major != ex->major)
102*4882a593Smuzhiyun 			continue;
103*4882a593Smuzhiyun 		if (walk->minor != ex->minor)
104*4882a593Smuzhiyun 			continue;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 		walk->access |= ex->access;
107*4882a593Smuzhiyun 		kfree(excopy);
108*4882a593Smuzhiyun 		excopy = NULL;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	if (excopy != NULL)
112*4882a593Smuzhiyun 		list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
113*4882a593Smuzhiyun 	return 0;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun /*
117*4882a593Smuzhiyun  * called under devcgroup_mutex
118*4882a593Smuzhiyun  */
dev_exception_rm(struct dev_cgroup * dev_cgroup,struct dev_exception_item * ex)119*4882a593Smuzhiyun static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
120*4882a593Smuzhiyun 			     struct dev_exception_item *ex)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	struct dev_exception_item *walk, *tmp;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	lockdep_assert_held(&devcgroup_mutex);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
127*4882a593Smuzhiyun 		if (walk->type != ex->type)
128*4882a593Smuzhiyun 			continue;
129*4882a593Smuzhiyun 		if (walk->major != ex->major)
130*4882a593Smuzhiyun 			continue;
131*4882a593Smuzhiyun 		if (walk->minor != ex->minor)
132*4882a593Smuzhiyun 			continue;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 		walk->access &= ~ex->access;
135*4882a593Smuzhiyun 		if (!walk->access) {
136*4882a593Smuzhiyun 			list_del_rcu(&walk->list);
137*4882a593Smuzhiyun 			kfree_rcu(walk, rcu);
138*4882a593Smuzhiyun 		}
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
__dev_exception_clean(struct dev_cgroup * dev_cgroup)142*4882a593Smuzhiyun static void __dev_exception_clean(struct dev_cgroup *dev_cgroup)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun 	struct dev_exception_item *ex, *tmp;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
147*4882a593Smuzhiyun 		list_del_rcu(&ex->list);
148*4882a593Smuzhiyun 		kfree_rcu(ex, rcu);
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun /**
153*4882a593Smuzhiyun  * dev_exception_clean - frees all entries of the exception list
154*4882a593Smuzhiyun  * @dev_cgroup: dev_cgroup with the exception list to be cleaned
155*4882a593Smuzhiyun  *
156*4882a593Smuzhiyun  * called under devcgroup_mutex
157*4882a593Smuzhiyun  */
dev_exception_clean(struct dev_cgroup * dev_cgroup)158*4882a593Smuzhiyun static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	lockdep_assert_held(&devcgroup_mutex);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	__dev_exception_clean(dev_cgroup);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
is_devcg_online(const struct dev_cgroup * devcg)165*4882a593Smuzhiyun static inline bool is_devcg_online(const struct dev_cgroup *devcg)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun 	return (devcg->behavior != DEVCG_DEFAULT_NONE);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun /**
171*4882a593Smuzhiyun  * devcgroup_online - initializes devcgroup's behavior and exceptions based on
172*4882a593Smuzhiyun  * 		      parent's
173*4882a593Smuzhiyun  * @css: css getting online
174*4882a593Smuzhiyun  * returns 0 in case of success, error code otherwise
175*4882a593Smuzhiyun  */
devcgroup_online(struct cgroup_subsys_state * css)176*4882a593Smuzhiyun static int devcgroup_online(struct cgroup_subsys_state *css)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
179*4882a593Smuzhiyun 	struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent);
180*4882a593Smuzhiyun 	int ret = 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	mutex_lock(&devcgroup_mutex);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	if (parent_dev_cgroup == NULL)
185*4882a593Smuzhiyun 		dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
186*4882a593Smuzhiyun 	else {
187*4882a593Smuzhiyun 		ret = dev_exceptions_copy(&dev_cgroup->exceptions,
188*4882a593Smuzhiyun 					  &parent_dev_cgroup->exceptions);
189*4882a593Smuzhiyun 		if (!ret)
190*4882a593Smuzhiyun 			dev_cgroup->behavior = parent_dev_cgroup->behavior;
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 	mutex_unlock(&devcgroup_mutex);
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return ret;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
devcgroup_offline(struct cgroup_subsys_state * css)197*4882a593Smuzhiyun static void devcgroup_offline(struct cgroup_subsys_state *css)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	mutex_lock(&devcgroup_mutex);
202*4882a593Smuzhiyun 	dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
203*4882a593Smuzhiyun 	mutex_unlock(&devcgroup_mutex);
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun /*
207*4882a593Smuzhiyun  * called from kernel/cgroup.c with cgroup_lock() held.
208*4882a593Smuzhiyun  */
209*4882a593Smuzhiyun static struct cgroup_subsys_state *
devcgroup_css_alloc(struct cgroup_subsys_state * parent_css)210*4882a593Smuzhiyun devcgroup_css_alloc(struct cgroup_subsys_state *parent_css)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	struct dev_cgroup *dev_cgroup;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
215*4882a593Smuzhiyun 	if (!dev_cgroup)
216*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
217*4882a593Smuzhiyun 	INIT_LIST_HEAD(&dev_cgroup->exceptions);
218*4882a593Smuzhiyun 	dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	return &dev_cgroup->css;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun 
devcgroup_css_free(struct cgroup_subsys_state * css)223*4882a593Smuzhiyun static void devcgroup_css_free(struct cgroup_subsys_state *css)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun 	struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	__dev_exception_clean(dev_cgroup);
228*4882a593Smuzhiyun 	kfree(dev_cgroup);
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun #define DEVCG_ALLOW 1
232*4882a593Smuzhiyun #define DEVCG_DENY 2
233*4882a593Smuzhiyun #define DEVCG_LIST 3
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun #define MAJMINLEN 13
236*4882a593Smuzhiyun #define ACCLEN 4
237*4882a593Smuzhiyun 
set_access(char * acc,short access)238*4882a593Smuzhiyun static void set_access(char *acc, short access)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	int idx = 0;
241*4882a593Smuzhiyun 	memset(acc, 0, ACCLEN);
242*4882a593Smuzhiyun 	if (access & DEVCG_ACC_READ)
243*4882a593Smuzhiyun 		acc[idx++] = 'r';
244*4882a593Smuzhiyun 	if (access & DEVCG_ACC_WRITE)
245*4882a593Smuzhiyun 		acc[idx++] = 'w';
246*4882a593Smuzhiyun 	if (access & DEVCG_ACC_MKNOD)
247*4882a593Smuzhiyun 		acc[idx++] = 'm';
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
type_to_char(short type)250*4882a593Smuzhiyun static char type_to_char(short type)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	if (type == DEVCG_DEV_ALL)
253*4882a593Smuzhiyun 		return 'a';
254*4882a593Smuzhiyun 	if (type == DEVCG_DEV_CHAR)
255*4882a593Smuzhiyun 		return 'c';
256*4882a593Smuzhiyun 	if (type == DEVCG_DEV_BLOCK)
257*4882a593Smuzhiyun 		return 'b';
258*4882a593Smuzhiyun 	return 'X';
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
set_majmin(char * str,unsigned m)261*4882a593Smuzhiyun static void set_majmin(char *str, unsigned m)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	if (m == ~0)
264*4882a593Smuzhiyun 		strcpy(str, "*");
265*4882a593Smuzhiyun 	else
266*4882a593Smuzhiyun 		sprintf(str, "%u", m);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
devcgroup_seq_show(struct seq_file * m,void * v)269*4882a593Smuzhiyun static int devcgroup_seq_show(struct seq_file *m, void *v)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	struct dev_cgroup *devcgroup = css_to_devcgroup(seq_css(m));
272*4882a593Smuzhiyun 	struct dev_exception_item *ex;
273*4882a593Smuzhiyun 	char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	rcu_read_lock();
276*4882a593Smuzhiyun 	/*
277*4882a593Smuzhiyun 	 * To preserve the compatibility:
278*4882a593Smuzhiyun 	 * - Only show the "all devices" when the default policy is to allow
279*4882a593Smuzhiyun 	 * - List the exceptions in case the default policy is to deny
280*4882a593Smuzhiyun 	 * This way, the file remains as a "whitelist of devices"
281*4882a593Smuzhiyun 	 */
282*4882a593Smuzhiyun 	if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
283*4882a593Smuzhiyun 		set_access(acc, DEVCG_ACC_MASK);
284*4882a593Smuzhiyun 		set_majmin(maj, ~0);
285*4882a593Smuzhiyun 		set_majmin(min, ~0);
286*4882a593Smuzhiyun 		seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL),
287*4882a593Smuzhiyun 			   maj, min, acc);
288*4882a593Smuzhiyun 	} else {
289*4882a593Smuzhiyun 		list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
290*4882a593Smuzhiyun 			set_access(acc, ex->access);
291*4882a593Smuzhiyun 			set_majmin(maj, ex->major);
292*4882a593Smuzhiyun 			set_majmin(min, ex->minor);
293*4882a593Smuzhiyun 			seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
294*4882a593Smuzhiyun 				   maj, min, acc);
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 	}
297*4882a593Smuzhiyun 	rcu_read_unlock();
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	return 0;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun /**
303*4882a593Smuzhiyun  * match_exception	- iterates the exception list trying to find a complete match
304*4882a593Smuzhiyun  * @exceptions: list of exceptions
305*4882a593Smuzhiyun  * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
306*4882a593Smuzhiyun  * @major: device file major number, ~0 to match all
307*4882a593Smuzhiyun  * @minor: device file minor number, ~0 to match all
308*4882a593Smuzhiyun  * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
309*4882a593Smuzhiyun  *
310*4882a593Smuzhiyun  * It is considered a complete match if an exception is found that will
311*4882a593Smuzhiyun  * contain the entire range of provided parameters.
312*4882a593Smuzhiyun  *
313*4882a593Smuzhiyun  * Return: true in case it matches an exception completely
314*4882a593Smuzhiyun  */
match_exception(struct list_head * exceptions,short type,u32 major,u32 minor,short access)315*4882a593Smuzhiyun static bool match_exception(struct list_head *exceptions, short type,
316*4882a593Smuzhiyun 			    u32 major, u32 minor, short access)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	struct dev_exception_item *ex;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	list_for_each_entry_rcu(ex, exceptions, list) {
321*4882a593Smuzhiyun 		if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
322*4882a593Smuzhiyun 			continue;
323*4882a593Smuzhiyun 		if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
324*4882a593Smuzhiyun 			continue;
325*4882a593Smuzhiyun 		if (ex->major != ~0 && ex->major != major)
326*4882a593Smuzhiyun 			continue;
327*4882a593Smuzhiyun 		if (ex->minor != ~0 && ex->minor != minor)
328*4882a593Smuzhiyun 			continue;
329*4882a593Smuzhiyun 		/* provided access cannot have more than the exception rule */
330*4882a593Smuzhiyun 		if (access & (~ex->access))
331*4882a593Smuzhiyun 			continue;
332*4882a593Smuzhiyun 		return true;
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 	return false;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun /**
338*4882a593Smuzhiyun  * match_exception_partial - iterates the exception list trying to find a partial match
339*4882a593Smuzhiyun  * @exceptions: list of exceptions
340*4882a593Smuzhiyun  * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
341*4882a593Smuzhiyun  * @major: device file major number, ~0 to match all
342*4882a593Smuzhiyun  * @minor: device file minor number, ~0 to match all
343*4882a593Smuzhiyun  * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
344*4882a593Smuzhiyun  *
345*4882a593Smuzhiyun  * It is considered a partial match if an exception's range is found to
346*4882a593Smuzhiyun  * contain *any* of the devices specified by provided parameters. This is
347*4882a593Smuzhiyun  * used to make sure no extra access is being granted that is forbidden by
348*4882a593Smuzhiyun  * any of the exception list.
349*4882a593Smuzhiyun  *
350*4882a593Smuzhiyun  * Return: true in case the provided range mat matches an exception completely
351*4882a593Smuzhiyun  */
match_exception_partial(struct list_head * exceptions,short type,u32 major,u32 minor,short access)352*4882a593Smuzhiyun static bool match_exception_partial(struct list_head *exceptions, short type,
353*4882a593Smuzhiyun 				    u32 major, u32 minor, short access)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun 	struct dev_exception_item *ex;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	list_for_each_entry_rcu(ex, exceptions, list,
358*4882a593Smuzhiyun 				lockdep_is_held(&devcgroup_mutex)) {
359*4882a593Smuzhiyun 		if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
360*4882a593Smuzhiyun 			continue;
361*4882a593Smuzhiyun 		if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
362*4882a593Smuzhiyun 			continue;
363*4882a593Smuzhiyun 		/*
364*4882a593Smuzhiyun 		 * We must be sure that both the exception and the provided
365*4882a593Smuzhiyun 		 * range aren't masking all devices
366*4882a593Smuzhiyun 		 */
367*4882a593Smuzhiyun 		if (ex->major != ~0 && major != ~0 && ex->major != major)
368*4882a593Smuzhiyun 			continue;
369*4882a593Smuzhiyun 		if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)
370*4882a593Smuzhiyun 			continue;
371*4882a593Smuzhiyun 		/*
372*4882a593Smuzhiyun 		 * In order to make sure the provided range isn't matching
373*4882a593Smuzhiyun 		 * an exception, all its access bits shouldn't match the
374*4882a593Smuzhiyun 		 * exception's access bits
375*4882a593Smuzhiyun 		 */
376*4882a593Smuzhiyun 		if (!(access & ex->access))
377*4882a593Smuzhiyun 			continue;
378*4882a593Smuzhiyun 		return true;
379*4882a593Smuzhiyun 	}
380*4882a593Smuzhiyun 	return false;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun /**
384*4882a593Smuzhiyun  * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions
385*4882a593Smuzhiyun  * @dev_cgroup: dev cgroup to be tested against
386*4882a593Smuzhiyun  * @refex: new exception
387*4882a593Smuzhiyun  * @behavior: behavior of the exception's dev_cgroup
388*4882a593Smuzhiyun  *
389*4882a593Smuzhiyun  * This is used to make sure a child cgroup won't have more privileges
390*4882a593Smuzhiyun  * than its parent
391*4882a593Smuzhiyun  */
verify_new_ex(struct dev_cgroup * dev_cgroup,struct dev_exception_item * refex,enum devcg_behavior behavior)392*4882a593Smuzhiyun static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
393*4882a593Smuzhiyun 		          struct dev_exception_item *refex,
394*4882a593Smuzhiyun 		          enum devcg_behavior behavior)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun 	bool match = false;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	RCU_LOCKDEP_WARN(!rcu_read_lock_held() &&
399*4882a593Smuzhiyun 			 !lockdep_is_held(&devcgroup_mutex),
400*4882a593Smuzhiyun 			 "device_cgroup:verify_new_ex called without proper synchronization");
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
403*4882a593Smuzhiyun 		if (behavior == DEVCG_DEFAULT_ALLOW) {
404*4882a593Smuzhiyun 			/*
405*4882a593Smuzhiyun 			 * new exception in the child doesn't matter, only
406*4882a593Smuzhiyun 			 * adding extra restrictions
407*4882a593Smuzhiyun 			 */
408*4882a593Smuzhiyun 			return true;
409*4882a593Smuzhiyun 		} else {
410*4882a593Smuzhiyun 			/*
411*4882a593Smuzhiyun 			 * new exception in the child will add more devices
412*4882a593Smuzhiyun 			 * that can be acessed, so it can't match any of
413*4882a593Smuzhiyun 			 * parent's exceptions, even slightly
414*4882a593Smuzhiyun 			 */
415*4882a593Smuzhiyun 			match = match_exception_partial(&dev_cgroup->exceptions,
416*4882a593Smuzhiyun 							refex->type,
417*4882a593Smuzhiyun 							refex->major,
418*4882a593Smuzhiyun 							refex->minor,
419*4882a593Smuzhiyun 							refex->access);
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 			if (match)
422*4882a593Smuzhiyun 				return false;
423*4882a593Smuzhiyun 			return true;
424*4882a593Smuzhiyun 		}
425*4882a593Smuzhiyun 	} else {
426*4882a593Smuzhiyun 		/*
427*4882a593Smuzhiyun 		 * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore
428*4882a593Smuzhiyun 		 * the new exception will add access to more devices and must
429*4882a593Smuzhiyun 		 * be contained completely in an parent's exception to be
430*4882a593Smuzhiyun 		 * allowed
431*4882a593Smuzhiyun 		 */
432*4882a593Smuzhiyun 		match = match_exception(&dev_cgroup->exceptions, refex->type,
433*4882a593Smuzhiyun 					refex->major, refex->minor,
434*4882a593Smuzhiyun 					refex->access);
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 		if (match)
437*4882a593Smuzhiyun 			/* parent has an exception that matches the proposed */
438*4882a593Smuzhiyun 			return true;
439*4882a593Smuzhiyun 		else
440*4882a593Smuzhiyun 			return false;
441*4882a593Smuzhiyun 	}
442*4882a593Smuzhiyun 	return false;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun /*
446*4882a593Smuzhiyun  * parent_has_perm:
447*4882a593Smuzhiyun  * when adding a new allow rule to a device exception list, the rule
448*4882a593Smuzhiyun  * must be allowed in the parent device
449*4882a593Smuzhiyun  */
parent_has_perm(struct dev_cgroup * childcg,struct dev_exception_item * ex)450*4882a593Smuzhiyun static int parent_has_perm(struct dev_cgroup *childcg,
451*4882a593Smuzhiyun 				  struct dev_exception_item *ex)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun 	struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	if (!parent)
456*4882a593Smuzhiyun 		return 1;
457*4882a593Smuzhiyun 	return verify_new_ex(parent, ex, childcg->behavior);
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun /**
461*4882a593Smuzhiyun  * parent_allows_removal - verify if it's ok to remove an exception
462*4882a593Smuzhiyun  * @childcg: child cgroup from where the exception will be removed
463*4882a593Smuzhiyun  * @ex: exception being removed
464*4882a593Smuzhiyun  *
465*4882a593Smuzhiyun  * When removing an exception in cgroups with default ALLOW policy, it must
466*4882a593Smuzhiyun  * be checked if removing it will give the child cgroup more access than the
467*4882a593Smuzhiyun  * parent.
468*4882a593Smuzhiyun  *
469*4882a593Smuzhiyun  * Return: true if it's ok to remove exception, false otherwise
470*4882a593Smuzhiyun  */
parent_allows_removal(struct dev_cgroup * childcg,struct dev_exception_item * ex)471*4882a593Smuzhiyun static bool parent_allows_removal(struct dev_cgroup *childcg,
472*4882a593Smuzhiyun 				  struct dev_exception_item *ex)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun 	struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	if (!parent)
477*4882a593Smuzhiyun 		return true;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	/* It's always allowed to remove access to devices */
480*4882a593Smuzhiyun 	if (childcg->behavior == DEVCG_DEFAULT_DENY)
481*4882a593Smuzhiyun 		return true;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	/*
484*4882a593Smuzhiyun 	 * Make sure you're not removing part or a whole exception existing in
485*4882a593Smuzhiyun 	 * the parent cgroup
486*4882a593Smuzhiyun 	 */
487*4882a593Smuzhiyun 	return !match_exception_partial(&parent->exceptions, ex->type,
488*4882a593Smuzhiyun 					ex->major, ex->minor, ex->access);
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun /**
492*4882a593Smuzhiyun  * may_allow_all - checks if it's possible to change the behavior to
493*4882a593Smuzhiyun  *		   allow based on parent's rules.
494*4882a593Smuzhiyun  * @parent: device cgroup's parent
495*4882a593Smuzhiyun  * returns: != 0 in case it's allowed, 0 otherwise
496*4882a593Smuzhiyun  */
may_allow_all(struct dev_cgroup * parent)497*4882a593Smuzhiyun static inline int may_allow_all(struct dev_cgroup *parent)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	if (!parent)
500*4882a593Smuzhiyun 		return 1;
501*4882a593Smuzhiyun 	return parent->behavior == DEVCG_DEFAULT_ALLOW;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun /**
505*4882a593Smuzhiyun  * revalidate_active_exceptions - walks through the active exception list and
506*4882a593Smuzhiyun  * 				  revalidates the exceptions based on parent's
507*4882a593Smuzhiyun  * 				  behavior and exceptions. The exceptions that
508*4882a593Smuzhiyun  * 				  are no longer valid will be removed.
509*4882a593Smuzhiyun  * 				  Called with devcgroup_mutex held.
510*4882a593Smuzhiyun  * @devcg: cgroup which exceptions will be checked
511*4882a593Smuzhiyun  *
512*4882a593Smuzhiyun  * This is one of the three key functions for hierarchy implementation.
513*4882a593Smuzhiyun  * This function is responsible for re-evaluating all the cgroup's active
514*4882a593Smuzhiyun  * exceptions due to a parent's exception change.
515*4882a593Smuzhiyun  * Refer to Documentation/admin-guide/cgroup-v1/devices.rst for more details.
516*4882a593Smuzhiyun  */
revalidate_active_exceptions(struct dev_cgroup * devcg)517*4882a593Smuzhiyun static void revalidate_active_exceptions(struct dev_cgroup *devcg)
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun 	struct dev_exception_item *ex;
520*4882a593Smuzhiyun 	struct list_head *this, *tmp;
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	list_for_each_safe(this, tmp, &devcg->exceptions) {
523*4882a593Smuzhiyun 		ex = container_of(this, struct dev_exception_item, list);
524*4882a593Smuzhiyun 		if (!parent_has_perm(devcg, ex))
525*4882a593Smuzhiyun 			dev_exception_rm(devcg, ex);
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun /**
530*4882a593Smuzhiyun  * propagate_exception - propagates a new exception to the children
531*4882a593Smuzhiyun  * @devcg_root: device cgroup that added a new exception
532*4882a593Smuzhiyun  * @ex: new exception to be propagated
533*4882a593Smuzhiyun  *
534*4882a593Smuzhiyun  * returns: 0 in case of success, != 0 in case of error
535*4882a593Smuzhiyun  */
propagate_exception(struct dev_cgroup * devcg_root,struct dev_exception_item * ex)536*4882a593Smuzhiyun static int propagate_exception(struct dev_cgroup *devcg_root,
537*4882a593Smuzhiyun 			       struct dev_exception_item *ex)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun 	struct cgroup_subsys_state *pos;
540*4882a593Smuzhiyun 	int rc = 0;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	rcu_read_lock();
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	css_for_each_descendant_pre(pos, &devcg_root->css) {
545*4882a593Smuzhiyun 		struct dev_cgroup *devcg = css_to_devcgroup(pos);
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 		/*
548*4882a593Smuzhiyun 		 * Because devcgroup_mutex is held, no devcg will become
549*4882a593Smuzhiyun 		 * online or offline during the tree walk (see on/offline
550*4882a593Smuzhiyun 		 * methods), and online ones are safe to access outside RCU
551*4882a593Smuzhiyun 		 * read lock without bumping refcnt.
552*4882a593Smuzhiyun 		 */
553*4882a593Smuzhiyun 		if (pos == &devcg_root->css || !is_devcg_online(devcg))
554*4882a593Smuzhiyun 			continue;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 		rcu_read_unlock();
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 		/*
559*4882a593Smuzhiyun 		 * in case both root's behavior and devcg is allow, a new
560*4882a593Smuzhiyun 		 * restriction means adding to the exception list
561*4882a593Smuzhiyun 		 */
562*4882a593Smuzhiyun 		if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
563*4882a593Smuzhiyun 		    devcg->behavior == DEVCG_DEFAULT_ALLOW) {
564*4882a593Smuzhiyun 			rc = dev_exception_add(devcg, ex);
565*4882a593Smuzhiyun 			if (rc)
566*4882a593Smuzhiyun 				return rc;
567*4882a593Smuzhiyun 		} else {
568*4882a593Smuzhiyun 			/*
569*4882a593Smuzhiyun 			 * in the other possible cases:
570*4882a593Smuzhiyun 			 * root's behavior: allow, devcg's: deny
571*4882a593Smuzhiyun 			 * root's behavior: deny, devcg's: deny
572*4882a593Smuzhiyun 			 * the exception will be removed
573*4882a593Smuzhiyun 			 */
574*4882a593Smuzhiyun 			dev_exception_rm(devcg, ex);
575*4882a593Smuzhiyun 		}
576*4882a593Smuzhiyun 		revalidate_active_exceptions(devcg);
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 		rcu_read_lock();
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	rcu_read_unlock();
582*4882a593Smuzhiyun 	return rc;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun /*
586*4882a593Smuzhiyun  * Modify the exception list using allow/deny rules.
587*4882a593Smuzhiyun  * CAP_SYS_ADMIN is needed for this.  It's at least separate from CAP_MKNOD
588*4882a593Smuzhiyun  * so we can give a container CAP_MKNOD to let it create devices but not
589*4882a593Smuzhiyun  * modify the exception list.
590*4882a593Smuzhiyun  * It seems likely we'll want to add a CAP_CONTAINER capability to allow
591*4882a593Smuzhiyun  * us to also grant CAP_SYS_ADMIN to containers without giving away the
592*4882a593Smuzhiyun  * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
593*4882a593Smuzhiyun  *
594*4882a593Smuzhiyun  * Taking rules away is always allowed (given CAP_SYS_ADMIN).  Granting
595*4882a593Smuzhiyun  * new access is only allowed if you're in the top-level cgroup, or your
596*4882a593Smuzhiyun  * parent cgroup has the access you're asking for.
597*4882a593Smuzhiyun  */
devcgroup_update_access(struct dev_cgroup * devcgroup,int filetype,char * buffer)598*4882a593Smuzhiyun static int devcgroup_update_access(struct dev_cgroup *devcgroup,
599*4882a593Smuzhiyun 				   int filetype, char *buffer)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun 	const char *b;
602*4882a593Smuzhiyun 	char temp[12];		/* 11 + 1 characters needed for a u32 */
603*4882a593Smuzhiyun 	int count, rc = 0;
604*4882a593Smuzhiyun 	struct dev_exception_item ex;
605*4882a593Smuzhiyun 	struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
608*4882a593Smuzhiyun 		return -EPERM;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	memset(&ex, 0, sizeof(ex));
611*4882a593Smuzhiyun 	b = buffer;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	switch (*b) {
614*4882a593Smuzhiyun 	case 'a':
615*4882a593Smuzhiyun 		switch (filetype) {
616*4882a593Smuzhiyun 		case DEVCG_ALLOW:
617*4882a593Smuzhiyun 			if (css_has_online_children(&devcgroup->css))
618*4882a593Smuzhiyun 				return -EINVAL;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 			if (!may_allow_all(parent))
621*4882a593Smuzhiyun 				return -EPERM;
622*4882a593Smuzhiyun 			dev_exception_clean(devcgroup);
623*4882a593Smuzhiyun 			devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
624*4882a593Smuzhiyun 			if (!parent)
625*4882a593Smuzhiyun 				break;
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 			rc = dev_exceptions_copy(&devcgroup->exceptions,
628*4882a593Smuzhiyun 						 &parent->exceptions);
629*4882a593Smuzhiyun 			if (rc)
630*4882a593Smuzhiyun 				return rc;
631*4882a593Smuzhiyun 			break;
632*4882a593Smuzhiyun 		case DEVCG_DENY:
633*4882a593Smuzhiyun 			if (css_has_online_children(&devcgroup->css))
634*4882a593Smuzhiyun 				return -EINVAL;
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 			dev_exception_clean(devcgroup);
637*4882a593Smuzhiyun 			devcgroup->behavior = DEVCG_DEFAULT_DENY;
638*4882a593Smuzhiyun 			break;
639*4882a593Smuzhiyun 		default:
640*4882a593Smuzhiyun 			return -EINVAL;
641*4882a593Smuzhiyun 		}
642*4882a593Smuzhiyun 		return 0;
643*4882a593Smuzhiyun 	case 'b':
644*4882a593Smuzhiyun 		ex.type = DEVCG_DEV_BLOCK;
645*4882a593Smuzhiyun 		break;
646*4882a593Smuzhiyun 	case 'c':
647*4882a593Smuzhiyun 		ex.type = DEVCG_DEV_CHAR;
648*4882a593Smuzhiyun 		break;
649*4882a593Smuzhiyun 	default:
650*4882a593Smuzhiyun 		return -EINVAL;
651*4882a593Smuzhiyun 	}
652*4882a593Smuzhiyun 	b++;
653*4882a593Smuzhiyun 	if (!isspace(*b))
654*4882a593Smuzhiyun 		return -EINVAL;
655*4882a593Smuzhiyun 	b++;
656*4882a593Smuzhiyun 	if (*b == '*') {
657*4882a593Smuzhiyun 		ex.major = ~0;
658*4882a593Smuzhiyun 		b++;
659*4882a593Smuzhiyun 	} else if (isdigit(*b)) {
660*4882a593Smuzhiyun 		memset(temp, 0, sizeof(temp));
661*4882a593Smuzhiyun 		for (count = 0; count < sizeof(temp) - 1; count++) {
662*4882a593Smuzhiyun 			temp[count] = *b;
663*4882a593Smuzhiyun 			b++;
664*4882a593Smuzhiyun 			if (!isdigit(*b))
665*4882a593Smuzhiyun 				break;
666*4882a593Smuzhiyun 		}
667*4882a593Smuzhiyun 		rc = kstrtou32(temp, 10, &ex.major);
668*4882a593Smuzhiyun 		if (rc)
669*4882a593Smuzhiyun 			return -EINVAL;
670*4882a593Smuzhiyun 	} else {
671*4882a593Smuzhiyun 		return -EINVAL;
672*4882a593Smuzhiyun 	}
673*4882a593Smuzhiyun 	if (*b != ':')
674*4882a593Smuzhiyun 		return -EINVAL;
675*4882a593Smuzhiyun 	b++;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	/* read minor */
678*4882a593Smuzhiyun 	if (*b == '*') {
679*4882a593Smuzhiyun 		ex.minor = ~0;
680*4882a593Smuzhiyun 		b++;
681*4882a593Smuzhiyun 	} else if (isdigit(*b)) {
682*4882a593Smuzhiyun 		memset(temp, 0, sizeof(temp));
683*4882a593Smuzhiyun 		for (count = 0; count < sizeof(temp) - 1; count++) {
684*4882a593Smuzhiyun 			temp[count] = *b;
685*4882a593Smuzhiyun 			b++;
686*4882a593Smuzhiyun 			if (!isdigit(*b))
687*4882a593Smuzhiyun 				break;
688*4882a593Smuzhiyun 		}
689*4882a593Smuzhiyun 		rc = kstrtou32(temp, 10, &ex.minor);
690*4882a593Smuzhiyun 		if (rc)
691*4882a593Smuzhiyun 			return -EINVAL;
692*4882a593Smuzhiyun 	} else {
693*4882a593Smuzhiyun 		return -EINVAL;
694*4882a593Smuzhiyun 	}
695*4882a593Smuzhiyun 	if (!isspace(*b))
696*4882a593Smuzhiyun 		return -EINVAL;
697*4882a593Smuzhiyun 	for (b++, count = 0; count < 3; count++, b++) {
698*4882a593Smuzhiyun 		switch (*b) {
699*4882a593Smuzhiyun 		case 'r':
700*4882a593Smuzhiyun 			ex.access |= DEVCG_ACC_READ;
701*4882a593Smuzhiyun 			break;
702*4882a593Smuzhiyun 		case 'w':
703*4882a593Smuzhiyun 			ex.access |= DEVCG_ACC_WRITE;
704*4882a593Smuzhiyun 			break;
705*4882a593Smuzhiyun 		case 'm':
706*4882a593Smuzhiyun 			ex.access |= DEVCG_ACC_MKNOD;
707*4882a593Smuzhiyun 			break;
708*4882a593Smuzhiyun 		case '\n':
709*4882a593Smuzhiyun 		case '\0':
710*4882a593Smuzhiyun 			count = 3;
711*4882a593Smuzhiyun 			break;
712*4882a593Smuzhiyun 		default:
713*4882a593Smuzhiyun 			return -EINVAL;
714*4882a593Smuzhiyun 		}
715*4882a593Smuzhiyun 	}
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun 	switch (filetype) {
718*4882a593Smuzhiyun 	case DEVCG_ALLOW:
719*4882a593Smuzhiyun 		/*
720*4882a593Smuzhiyun 		 * If the default policy is to allow by default, try to remove
721*4882a593Smuzhiyun 		 * an matching exception instead. And be silent about it: we
722*4882a593Smuzhiyun 		 * don't want to break compatibility
723*4882a593Smuzhiyun 		 */
724*4882a593Smuzhiyun 		if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
725*4882a593Smuzhiyun 			/* Check if the parent allows removing it first */
726*4882a593Smuzhiyun 			if (!parent_allows_removal(devcgroup, &ex))
727*4882a593Smuzhiyun 				return -EPERM;
728*4882a593Smuzhiyun 			dev_exception_rm(devcgroup, &ex);
729*4882a593Smuzhiyun 			break;
730*4882a593Smuzhiyun 		}
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 		if (!parent_has_perm(devcgroup, &ex))
733*4882a593Smuzhiyun 			return -EPERM;
734*4882a593Smuzhiyun 		rc = dev_exception_add(devcgroup, &ex);
735*4882a593Smuzhiyun 		break;
736*4882a593Smuzhiyun 	case DEVCG_DENY:
737*4882a593Smuzhiyun 		/*
738*4882a593Smuzhiyun 		 * If the default policy is to deny by default, try to remove
739*4882a593Smuzhiyun 		 * an matching exception instead. And be silent about it: we
740*4882a593Smuzhiyun 		 * don't want to break compatibility
741*4882a593Smuzhiyun 		 */
742*4882a593Smuzhiyun 		if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
743*4882a593Smuzhiyun 			dev_exception_rm(devcgroup, &ex);
744*4882a593Smuzhiyun 		else
745*4882a593Smuzhiyun 			rc = dev_exception_add(devcgroup, &ex);
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 		if (rc)
748*4882a593Smuzhiyun 			break;
749*4882a593Smuzhiyun 		/* we only propagate new restrictions */
750*4882a593Smuzhiyun 		rc = propagate_exception(devcgroup, &ex);
751*4882a593Smuzhiyun 		break;
752*4882a593Smuzhiyun 	default:
753*4882a593Smuzhiyun 		rc = -EINVAL;
754*4882a593Smuzhiyun 	}
755*4882a593Smuzhiyun 	return rc;
756*4882a593Smuzhiyun }
757*4882a593Smuzhiyun 
devcgroup_access_write(struct kernfs_open_file * of,char * buf,size_t nbytes,loff_t off)758*4882a593Smuzhiyun static ssize_t devcgroup_access_write(struct kernfs_open_file *of,
759*4882a593Smuzhiyun 				      char *buf, size_t nbytes, loff_t off)
760*4882a593Smuzhiyun {
761*4882a593Smuzhiyun 	int retval;
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 	mutex_lock(&devcgroup_mutex);
764*4882a593Smuzhiyun 	retval = devcgroup_update_access(css_to_devcgroup(of_css(of)),
765*4882a593Smuzhiyun 					 of_cft(of)->private, strstrip(buf));
766*4882a593Smuzhiyun 	mutex_unlock(&devcgroup_mutex);
767*4882a593Smuzhiyun 	return retval ?: nbytes;
768*4882a593Smuzhiyun }
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun static struct cftype dev_cgroup_files[] = {
771*4882a593Smuzhiyun 	{
772*4882a593Smuzhiyun 		.name = "allow",
773*4882a593Smuzhiyun 		.write = devcgroup_access_write,
774*4882a593Smuzhiyun 		.private = DEVCG_ALLOW,
775*4882a593Smuzhiyun 	},
776*4882a593Smuzhiyun 	{
777*4882a593Smuzhiyun 		.name = "deny",
778*4882a593Smuzhiyun 		.write = devcgroup_access_write,
779*4882a593Smuzhiyun 		.private = DEVCG_DENY,
780*4882a593Smuzhiyun 	},
781*4882a593Smuzhiyun 	{
782*4882a593Smuzhiyun 		.name = "list",
783*4882a593Smuzhiyun 		.seq_show = devcgroup_seq_show,
784*4882a593Smuzhiyun 		.private = DEVCG_LIST,
785*4882a593Smuzhiyun 	},
786*4882a593Smuzhiyun 	{ }	/* terminate */
787*4882a593Smuzhiyun };
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun struct cgroup_subsys devices_cgrp_subsys = {
790*4882a593Smuzhiyun 	.css_alloc = devcgroup_css_alloc,
791*4882a593Smuzhiyun 	.css_free = devcgroup_css_free,
792*4882a593Smuzhiyun 	.css_online = devcgroup_online,
793*4882a593Smuzhiyun 	.css_offline = devcgroup_offline,
794*4882a593Smuzhiyun 	.legacy_cftypes = dev_cgroup_files,
795*4882a593Smuzhiyun };
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun /**
798*4882a593Smuzhiyun  * devcgroup_legacy_check_permission - checks if an inode operation is permitted
799*4882a593Smuzhiyun  * @dev_cgroup: the dev cgroup to be tested against
800*4882a593Smuzhiyun  * @type: device type
801*4882a593Smuzhiyun  * @major: device major number
802*4882a593Smuzhiyun  * @minor: device minor number
803*4882a593Smuzhiyun  * @access: combination of DEVCG_ACC_WRITE, DEVCG_ACC_READ and DEVCG_ACC_MKNOD
804*4882a593Smuzhiyun  *
805*4882a593Smuzhiyun  * returns 0 on success, -EPERM case the operation is not permitted
806*4882a593Smuzhiyun  */
devcgroup_legacy_check_permission(short type,u32 major,u32 minor,short access)807*4882a593Smuzhiyun static int devcgroup_legacy_check_permission(short type, u32 major, u32 minor,
808*4882a593Smuzhiyun 					short access)
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun 	struct dev_cgroup *dev_cgroup;
811*4882a593Smuzhiyun 	bool rc;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	rcu_read_lock();
814*4882a593Smuzhiyun 	dev_cgroup = task_devcgroup(current);
815*4882a593Smuzhiyun 	if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW)
816*4882a593Smuzhiyun 		/* Can't match any of the exceptions, even partially */
817*4882a593Smuzhiyun 		rc = !match_exception_partial(&dev_cgroup->exceptions,
818*4882a593Smuzhiyun 					      type, major, minor, access);
819*4882a593Smuzhiyun 	else
820*4882a593Smuzhiyun 		/* Need to match completely one exception to be allowed */
821*4882a593Smuzhiyun 		rc = match_exception(&dev_cgroup->exceptions, type, major,
822*4882a593Smuzhiyun 				     minor, access);
823*4882a593Smuzhiyun 	rcu_read_unlock();
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	if (!rc)
826*4882a593Smuzhiyun 		return -EPERM;
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun 	return 0;
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun #endif /* CONFIG_CGROUP_DEVICE */
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
834*4882a593Smuzhiyun 
devcgroup_check_permission(short type,u32 major,u32 minor,short access)835*4882a593Smuzhiyun int devcgroup_check_permission(short type, u32 major, u32 minor, short access)
836*4882a593Smuzhiyun {
837*4882a593Smuzhiyun 	int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	if (rc)
840*4882a593Smuzhiyun 		return -EPERM;
841*4882a593Smuzhiyun 
842*4882a593Smuzhiyun 	#ifdef CONFIG_CGROUP_DEVICE
843*4882a593Smuzhiyun 	return devcgroup_legacy_check_permission(type, major, minor, access);
844*4882a593Smuzhiyun 
845*4882a593Smuzhiyun 	#else /* CONFIG_CGROUP_DEVICE */
846*4882a593Smuzhiyun 	return 0;
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 	#endif /* CONFIG_CGROUP_DEVICE */
849*4882a593Smuzhiyun }
850*4882a593Smuzhiyun EXPORT_SYMBOL(devcgroup_check_permission);
851*4882a593Smuzhiyun #endif /* defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) */
852