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