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