Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * device_cgroup.c - device cgroup subsystem
0004  *
0005  * Copyright 2007 IBM Corp
0006  */
0007 
0008 #include <linux/bpf-cgroup.h>
0009 #include <linux/device_cgroup.h>
0010 #include <linux/cgroup.h>
0011 #include <linux/ctype.h>
0012 #include <linux/list.h>
0013 #include <linux/uaccess.h>
0014 #include <linux/seq_file.h>
0015 #include <linux/slab.h>
0016 #include <linux/rcupdate.h>
0017 #include <linux/mutex.h>
0018 
0019 #ifdef CONFIG_CGROUP_DEVICE
0020 
0021 static DEFINE_MUTEX(devcgroup_mutex);
0022 
0023 enum devcg_behavior {
0024     DEVCG_DEFAULT_NONE,
0025     DEVCG_DEFAULT_ALLOW,
0026     DEVCG_DEFAULT_DENY,
0027 };
0028 
0029 /*
0030  * exception list locking rules:
0031  * hold devcgroup_mutex for update/read.
0032  * hold rcu_read_lock() for read.
0033  */
0034 
0035 struct dev_exception_item {
0036     u32 major, minor;
0037     short type;
0038     short access;
0039     struct list_head list;
0040     struct rcu_head rcu;
0041 };
0042 
0043 struct dev_cgroup {
0044     struct cgroup_subsys_state css;
0045     struct list_head exceptions;
0046     enum devcg_behavior behavior;
0047 };
0048 
0049 static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
0050 {
0051     return s ? container_of(s, struct dev_cgroup, css) : NULL;
0052 }
0053 
0054 static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
0055 {
0056     return css_to_devcgroup(task_css(task, devices_cgrp_id));
0057 }
0058 
0059 /*
0060  * called under devcgroup_mutex
0061  */
0062 static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
0063 {
0064     struct dev_exception_item *ex, *tmp, *new;
0065 
0066     lockdep_assert_held(&devcgroup_mutex);
0067 
0068     list_for_each_entry(ex, orig, list) {
0069         new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
0070         if (!new)
0071             goto free_and_exit;
0072         list_add_tail(&new->list, dest);
0073     }
0074 
0075     return 0;
0076 
0077 free_and_exit:
0078     list_for_each_entry_safe(ex, tmp, dest, list) {
0079         list_del(&ex->list);
0080         kfree(ex);
0081     }
0082     return -ENOMEM;
0083 }
0084 
0085 /*
0086  * called under devcgroup_mutex
0087  */
0088 static int dev_exception_add(struct dev_cgroup *dev_cgroup,
0089                  struct dev_exception_item *ex)
0090 {
0091     struct dev_exception_item *excopy, *walk;
0092 
0093     lockdep_assert_held(&devcgroup_mutex);
0094 
0095     excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
0096     if (!excopy)
0097         return -ENOMEM;
0098 
0099     list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
0100         if (walk->type != ex->type)
0101             continue;
0102         if (walk->major != ex->major)
0103             continue;
0104         if (walk->minor != ex->minor)
0105             continue;
0106 
0107         walk->access |= ex->access;
0108         kfree(excopy);
0109         excopy = NULL;
0110     }
0111 
0112     if (excopy != NULL)
0113         list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
0114     return 0;
0115 }
0116 
0117 /*
0118  * called under devcgroup_mutex
0119  */
0120 static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
0121                  struct dev_exception_item *ex)
0122 {
0123     struct dev_exception_item *walk, *tmp;
0124 
0125     lockdep_assert_held(&devcgroup_mutex);
0126 
0127     list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
0128         if (walk->type != ex->type)
0129             continue;
0130         if (walk->major != ex->major)
0131             continue;
0132         if (walk->minor != ex->minor)
0133             continue;
0134 
0135         walk->access &= ~ex->access;
0136         if (!walk->access) {
0137             list_del_rcu(&walk->list);
0138             kfree_rcu(walk, rcu);
0139         }
0140     }
0141 }
0142 
0143 static void __dev_exception_clean(struct dev_cgroup *dev_cgroup)
0144 {
0145     struct dev_exception_item *ex, *tmp;
0146 
0147     list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
0148         list_del_rcu(&ex->list);
0149         kfree_rcu(ex, rcu);
0150     }
0151 }
0152 
0153 /**
0154  * dev_exception_clean - frees all entries of the exception list
0155  * @dev_cgroup: dev_cgroup with the exception list to be cleaned
0156  *
0157  * called under devcgroup_mutex
0158  */
0159 static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
0160 {
0161     lockdep_assert_held(&devcgroup_mutex);
0162 
0163     __dev_exception_clean(dev_cgroup);
0164 }
0165 
0166 static inline bool is_devcg_online(const struct dev_cgroup *devcg)
0167 {
0168     return (devcg->behavior != DEVCG_DEFAULT_NONE);
0169 }
0170 
0171 /**
0172  * devcgroup_online - initializes devcgroup's behavior and exceptions based on
0173  *            parent's
0174  * @css: css getting online
0175  * returns 0 in case of success, error code otherwise
0176  */
0177 static int devcgroup_online(struct cgroup_subsys_state *css)
0178 {
0179     struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
0180     struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent);
0181     int ret = 0;
0182 
0183     mutex_lock(&devcgroup_mutex);
0184 
0185     if (parent_dev_cgroup == NULL)
0186         dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
0187     else {
0188         ret = dev_exceptions_copy(&dev_cgroup->exceptions,
0189                       &parent_dev_cgroup->exceptions);
0190         if (!ret)
0191             dev_cgroup->behavior = parent_dev_cgroup->behavior;
0192     }
0193     mutex_unlock(&devcgroup_mutex);
0194 
0195     return ret;
0196 }
0197 
0198 static void devcgroup_offline(struct cgroup_subsys_state *css)
0199 {
0200     struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
0201 
0202     mutex_lock(&devcgroup_mutex);
0203     dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
0204     mutex_unlock(&devcgroup_mutex);
0205 }
0206 
0207 /*
0208  * called from kernel/cgroup.c with cgroup_lock() held.
0209  */
0210 static struct cgroup_subsys_state *
0211 devcgroup_css_alloc(struct cgroup_subsys_state *parent_css)
0212 {
0213     struct dev_cgroup *dev_cgroup;
0214 
0215     dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
0216     if (!dev_cgroup)
0217         return ERR_PTR(-ENOMEM);
0218     INIT_LIST_HEAD(&dev_cgroup->exceptions);
0219     dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
0220 
0221     return &dev_cgroup->css;
0222 }
0223 
0224 static void devcgroup_css_free(struct cgroup_subsys_state *css)
0225 {
0226     struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
0227 
0228     __dev_exception_clean(dev_cgroup);
0229     kfree(dev_cgroup);
0230 }
0231 
0232 #define DEVCG_ALLOW 1
0233 #define DEVCG_DENY 2
0234 #define DEVCG_LIST 3
0235 
0236 #define MAJMINLEN 13
0237 #define ACCLEN 4
0238 
0239 static void set_access(char *acc, short access)
0240 {
0241     int idx = 0;
0242     memset(acc, 0, ACCLEN);
0243     if (access & DEVCG_ACC_READ)
0244         acc[idx++] = 'r';
0245     if (access & DEVCG_ACC_WRITE)
0246         acc[idx++] = 'w';
0247     if (access & DEVCG_ACC_MKNOD)
0248         acc[idx++] = 'm';
0249 }
0250 
0251 static char type_to_char(short type)
0252 {
0253     if (type == DEVCG_DEV_ALL)
0254         return 'a';
0255     if (type == DEVCG_DEV_CHAR)
0256         return 'c';
0257     if (type == DEVCG_DEV_BLOCK)
0258         return 'b';
0259     return 'X';
0260 }
0261 
0262 static void set_majmin(char *str, unsigned m)
0263 {
0264     if (m == ~0)
0265         strcpy(str, "*");
0266     else
0267         sprintf(str, "%u", m);
0268 }
0269 
0270 static int devcgroup_seq_show(struct seq_file *m, void *v)
0271 {
0272     struct dev_cgroup *devcgroup = css_to_devcgroup(seq_css(m));
0273     struct dev_exception_item *ex;
0274     char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
0275 
0276     rcu_read_lock();
0277     /*
0278      * To preserve the compatibility:
0279      * - Only show the "all devices" when the default policy is to allow
0280      * - List the exceptions in case the default policy is to deny
0281      * This way, the file remains as a "whitelist of devices"
0282      */
0283     if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
0284         set_access(acc, DEVCG_ACC_MASK);
0285         set_majmin(maj, ~0);
0286         set_majmin(min, ~0);
0287         seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL),
0288                maj, min, acc);
0289     } else {
0290         list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
0291             set_access(acc, ex->access);
0292             set_majmin(maj, ex->major);
0293             set_majmin(min, ex->minor);
0294             seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
0295                    maj, min, acc);
0296         }
0297     }
0298     rcu_read_unlock();
0299 
0300     return 0;
0301 }
0302 
0303 /**
0304  * match_exception  - iterates the exception list trying to find a complete match
0305  * @exceptions: list of exceptions
0306  * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
0307  * @major: device file major number, ~0 to match all
0308  * @minor: device file minor number, ~0 to match all
0309  * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
0310  *
0311  * It is considered a complete match if an exception is found that will
0312  * contain the entire range of provided parameters.
0313  *
0314  * Return: true in case it matches an exception completely
0315  */
0316 static bool match_exception(struct list_head *exceptions, short type,
0317                 u32 major, u32 minor, short access)
0318 {
0319     struct dev_exception_item *ex;
0320 
0321     list_for_each_entry_rcu(ex, exceptions, list) {
0322         if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
0323             continue;
0324         if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
0325             continue;
0326         if (ex->major != ~0 && ex->major != major)
0327             continue;
0328         if (ex->minor != ~0 && ex->minor != minor)
0329             continue;
0330         /* provided access cannot have more than the exception rule */
0331         if (access & (~ex->access))
0332             continue;
0333         return true;
0334     }
0335     return false;
0336 }
0337 
0338 /**
0339  * match_exception_partial - iterates the exception list trying to find a partial match
0340  * @exceptions: list of exceptions
0341  * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
0342  * @major: device file major number, ~0 to match all
0343  * @minor: device file minor number, ~0 to match all
0344  * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
0345  *
0346  * It is considered a partial match if an exception's range is found to
0347  * contain *any* of the devices specified by provided parameters. This is
0348  * used to make sure no extra access is being granted that is forbidden by
0349  * any of the exception list.
0350  *
0351  * Return: true in case the provided range mat matches an exception completely
0352  */
0353 static bool match_exception_partial(struct list_head *exceptions, short type,
0354                     u32 major, u32 minor, short access)
0355 {
0356     struct dev_exception_item *ex;
0357 
0358     list_for_each_entry_rcu(ex, exceptions, list,
0359                 lockdep_is_held(&devcgroup_mutex)) {
0360         if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
0361             continue;
0362         if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
0363             continue;
0364         /*
0365          * We must be sure that both the exception and the provided
0366          * range aren't masking all devices
0367          */
0368         if (ex->major != ~0 && major != ~0 && ex->major != major)
0369             continue;
0370         if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)
0371             continue;
0372         /*
0373          * In order to make sure the provided range isn't matching
0374          * an exception, all its access bits shouldn't match the
0375          * exception's access bits
0376          */
0377         if (!(access & ex->access))
0378             continue;
0379         return true;
0380     }
0381     return false;
0382 }
0383 
0384 /**
0385  * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions
0386  * @dev_cgroup: dev cgroup to be tested against
0387  * @refex: new exception
0388  * @behavior: behavior of the exception's dev_cgroup
0389  *
0390  * This is used to make sure a child cgroup won't have more privileges
0391  * than its parent
0392  */
0393 static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
0394                   struct dev_exception_item *refex,
0395                   enum devcg_behavior behavior)
0396 {
0397     bool match = false;
0398 
0399     RCU_LOCKDEP_WARN(!rcu_read_lock_held() &&
0400              !lockdep_is_held(&devcgroup_mutex),
0401              "device_cgroup:verify_new_ex called without proper synchronization");
0402 
0403     if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
0404         if (behavior == DEVCG_DEFAULT_ALLOW) {
0405             /*
0406              * new exception in the child doesn't matter, only
0407              * adding extra restrictions
0408              */ 
0409             return true;
0410         } else {
0411             /*
0412              * new exception in the child will add more devices
0413              * that can be acessed, so it can't match any of
0414              * parent's exceptions, even slightly
0415              */ 
0416             match = match_exception_partial(&dev_cgroup->exceptions,
0417                             refex->type,
0418                             refex->major,
0419                             refex->minor,
0420                             refex->access);
0421 
0422             if (match)
0423                 return false;
0424             return true;
0425         }
0426     } else {
0427         /*
0428          * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore
0429          * the new exception will add access to more devices and must
0430          * be contained completely in an parent's exception to be
0431          * allowed
0432          */
0433         match = match_exception(&dev_cgroup->exceptions, refex->type,
0434                     refex->major, refex->minor,
0435                     refex->access);
0436 
0437         if (match)
0438             /* parent has an exception that matches the proposed */
0439             return true;
0440         else
0441             return false;
0442     }
0443     return false;
0444 }
0445 
0446 /*
0447  * parent_has_perm:
0448  * when adding a new allow rule to a device exception list, the rule
0449  * must be allowed in the parent device
0450  */
0451 static int parent_has_perm(struct dev_cgroup *childcg,
0452                   struct dev_exception_item *ex)
0453 {
0454     struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);
0455 
0456     if (!parent)
0457         return 1;
0458     return verify_new_ex(parent, ex, childcg->behavior);
0459 }
0460 
0461 /**
0462  * parent_allows_removal - verify if it's ok to remove an exception
0463  * @childcg: child cgroup from where the exception will be removed
0464  * @ex: exception being removed
0465  *
0466  * When removing an exception in cgroups with default ALLOW policy, it must
0467  * be checked if removing it will give the child cgroup more access than the
0468  * parent.
0469  *
0470  * Return: true if it's ok to remove exception, false otherwise
0471  */
0472 static bool parent_allows_removal(struct dev_cgroup *childcg,
0473                   struct dev_exception_item *ex)
0474 {
0475     struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);
0476 
0477     if (!parent)
0478         return true;
0479 
0480     /* It's always allowed to remove access to devices */
0481     if (childcg->behavior == DEVCG_DEFAULT_DENY)
0482         return true;
0483 
0484     /*
0485      * Make sure you're not removing part or a whole exception existing in
0486      * the parent cgroup
0487      */
0488     return !match_exception_partial(&parent->exceptions, ex->type,
0489                     ex->major, ex->minor, ex->access);
0490 }
0491 
0492 /**
0493  * may_allow_all - checks if it's possible to change the behavior to
0494  *         allow based on parent's rules.
0495  * @parent: device cgroup's parent
0496  * returns: != 0 in case it's allowed, 0 otherwise
0497  */
0498 static inline int may_allow_all(struct dev_cgroup *parent)
0499 {
0500     if (!parent)
0501         return 1;
0502     return parent->behavior == DEVCG_DEFAULT_ALLOW;
0503 }
0504 
0505 /**
0506  * revalidate_active_exceptions - walks through the active exception list and
0507  *                revalidates the exceptions based on parent's
0508  *                behavior and exceptions. The exceptions that
0509  *                are no longer valid will be removed.
0510  *                Called with devcgroup_mutex held.
0511  * @devcg: cgroup which exceptions will be checked
0512  *
0513  * This is one of the three key functions for hierarchy implementation.
0514  * This function is responsible for re-evaluating all the cgroup's active
0515  * exceptions due to a parent's exception change.
0516  * Refer to Documentation/admin-guide/cgroup-v1/devices.rst for more details.
0517  */
0518 static void revalidate_active_exceptions(struct dev_cgroup *devcg)
0519 {
0520     struct dev_exception_item *ex;
0521     struct list_head *this, *tmp;
0522 
0523     list_for_each_safe(this, tmp, &devcg->exceptions) {
0524         ex = container_of(this, struct dev_exception_item, list);
0525         if (!parent_has_perm(devcg, ex))
0526             dev_exception_rm(devcg, ex);
0527     }
0528 }
0529 
0530 /**
0531  * propagate_exception - propagates a new exception to the children
0532  * @devcg_root: device cgroup that added a new exception
0533  * @ex: new exception to be propagated
0534  *
0535  * returns: 0 in case of success, != 0 in case of error
0536  */
0537 static int propagate_exception(struct dev_cgroup *devcg_root,
0538                    struct dev_exception_item *ex)
0539 {
0540     struct cgroup_subsys_state *pos;
0541     int rc = 0;
0542 
0543     rcu_read_lock();
0544 
0545     css_for_each_descendant_pre(pos, &devcg_root->css) {
0546         struct dev_cgroup *devcg = css_to_devcgroup(pos);
0547 
0548         /*
0549          * Because devcgroup_mutex is held, no devcg will become
0550          * online or offline during the tree walk (see on/offline
0551          * methods), and online ones are safe to access outside RCU
0552          * read lock without bumping refcnt.
0553          */
0554         if (pos == &devcg_root->css || !is_devcg_online(devcg))
0555             continue;
0556 
0557         rcu_read_unlock();
0558 
0559         /*
0560          * in case both root's behavior and devcg is allow, a new
0561          * restriction means adding to the exception list
0562          */
0563         if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
0564             devcg->behavior == DEVCG_DEFAULT_ALLOW) {
0565             rc = dev_exception_add(devcg, ex);
0566             if (rc)
0567                 return rc;
0568         } else {
0569             /*
0570              * in the other possible cases:
0571              * root's behavior: allow, devcg's: deny
0572              * root's behavior: deny, devcg's: deny
0573              * the exception will be removed
0574              */
0575             dev_exception_rm(devcg, ex);
0576         }
0577         revalidate_active_exceptions(devcg);
0578 
0579         rcu_read_lock();
0580     }
0581 
0582     rcu_read_unlock();
0583     return rc;
0584 }
0585 
0586 /*
0587  * Modify the exception list using allow/deny rules.
0588  * CAP_SYS_ADMIN is needed for this.  It's at least separate from CAP_MKNOD
0589  * so we can give a container CAP_MKNOD to let it create devices but not
0590  * modify the exception list.
0591  * It seems likely we'll want to add a CAP_CONTAINER capability to allow
0592  * us to also grant CAP_SYS_ADMIN to containers without giving away the
0593  * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
0594  *
0595  * Taking rules away is always allowed (given CAP_SYS_ADMIN).  Granting
0596  * new access is only allowed if you're in the top-level cgroup, or your
0597  * parent cgroup has the access you're asking for.
0598  */
0599 static int devcgroup_update_access(struct dev_cgroup *devcgroup,
0600                    int filetype, char *buffer)
0601 {
0602     const char *b;
0603     char temp[12];      /* 11 + 1 characters needed for a u32 */
0604     int count, rc = 0;
0605     struct dev_exception_item ex;
0606     struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent);
0607 
0608     if (!capable(CAP_SYS_ADMIN))
0609         return -EPERM;
0610 
0611     memset(&ex, 0, sizeof(ex));
0612     b = buffer;
0613 
0614     switch (*b) {
0615     case 'a':
0616         switch (filetype) {
0617         case DEVCG_ALLOW:
0618             if (css_has_online_children(&devcgroup->css))
0619                 return -EINVAL;
0620 
0621             if (!may_allow_all(parent))
0622                 return -EPERM;
0623             dev_exception_clean(devcgroup);
0624             devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
0625             if (!parent)
0626                 break;
0627 
0628             rc = dev_exceptions_copy(&devcgroup->exceptions,
0629                          &parent->exceptions);
0630             if (rc)
0631                 return rc;
0632             break;
0633         case DEVCG_DENY:
0634             if (css_has_online_children(&devcgroup->css))
0635                 return -EINVAL;
0636 
0637             dev_exception_clean(devcgroup);
0638             devcgroup->behavior = DEVCG_DEFAULT_DENY;
0639             break;
0640         default:
0641             return -EINVAL;
0642         }
0643         return 0;
0644     case 'b':
0645         ex.type = DEVCG_DEV_BLOCK;
0646         break;
0647     case 'c':
0648         ex.type = DEVCG_DEV_CHAR;
0649         break;
0650     default:
0651         return -EINVAL;
0652     }
0653     b++;
0654     if (!isspace(*b))
0655         return -EINVAL;
0656     b++;
0657     if (*b == '*') {
0658         ex.major = ~0;
0659         b++;
0660     } else if (isdigit(*b)) {
0661         memset(temp, 0, sizeof(temp));
0662         for (count = 0; count < sizeof(temp) - 1; count++) {
0663             temp[count] = *b;
0664             b++;
0665             if (!isdigit(*b))
0666                 break;
0667         }
0668         rc = kstrtou32(temp, 10, &ex.major);
0669         if (rc)
0670             return -EINVAL;
0671     } else {
0672         return -EINVAL;
0673     }
0674     if (*b != ':')
0675         return -EINVAL;
0676     b++;
0677 
0678     /* read minor */
0679     if (*b == '*') {
0680         ex.minor = ~0;
0681         b++;
0682     } else if (isdigit(*b)) {
0683         memset(temp, 0, sizeof(temp));
0684         for (count = 0; count < sizeof(temp) - 1; count++) {
0685             temp[count] = *b;
0686             b++;
0687             if (!isdigit(*b))
0688                 break;
0689         }
0690         rc = kstrtou32(temp, 10, &ex.minor);
0691         if (rc)
0692             return -EINVAL;
0693     } else {
0694         return -EINVAL;
0695     }
0696     if (!isspace(*b))
0697         return -EINVAL;
0698     for (b++, count = 0; count < 3; count++, b++) {
0699         switch (*b) {
0700         case 'r':
0701             ex.access |= DEVCG_ACC_READ;
0702             break;
0703         case 'w':
0704             ex.access |= DEVCG_ACC_WRITE;
0705             break;
0706         case 'm':
0707             ex.access |= DEVCG_ACC_MKNOD;
0708             break;
0709         case '\n':
0710         case '\0':
0711             count = 3;
0712             break;
0713         default:
0714             return -EINVAL;
0715         }
0716     }
0717 
0718     switch (filetype) {
0719     case DEVCG_ALLOW:
0720         /*
0721          * If the default policy is to allow by default, try to remove
0722          * an matching exception instead. And be silent about it: we
0723          * don't want to break compatibility
0724          */
0725         if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
0726             /* Check if the parent allows removing it first */
0727             if (!parent_allows_removal(devcgroup, &ex))
0728                 return -EPERM;
0729             dev_exception_rm(devcgroup, &ex);
0730             break;
0731         }
0732 
0733         if (!parent_has_perm(devcgroup, &ex))
0734             return -EPERM;
0735         rc = dev_exception_add(devcgroup, &ex);
0736         break;
0737     case DEVCG_DENY:
0738         /*
0739          * If the default policy is to deny by default, try to remove
0740          * an matching exception instead. And be silent about it: we
0741          * don't want to break compatibility
0742          */
0743         if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
0744             dev_exception_rm(devcgroup, &ex);
0745         else
0746             rc = dev_exception_add(devcgroup, &ex);
0747 
0748         if (rc)
0749             break;
0750         /* we only propagate new restrictions */
0751         rc = propagate_exception(devcgroup, &ex);
0752         break;
0753     default:
0754         rc = -EINVAL;
0755     }
0756     return rc;
0757 }
0758 
0759 static ssize_t devcgroup_access_write(struct kernfs_open_file *of,
0760                       char *buf, size_t nbytes, loff_t off)
0761 {
0762     int retval;
0763 
0764     mutex_lock(&devcgroup_mutex);
0765     retval = devcgroup_update_access(css_to_devcgroup(of_css(of)),
0766                      of_cft(of)->private, strstrip(buf));
0767     mutex_unlock(&devcgroup_mutex);
0768     return retval ?: nbytes;
0769 }
0770 
0771 static struct cftype dev_cgroup_files[] = {
0772     {
0773         .name = "allow",
0774         .write = devcgroup_access_write,
0775         .private = DEVCG_ALLOW,
0776     },
0777     {
0778         .name = "deny",
0779         .write = devcgroup_access_write,
0780         .private = DEVCG_DENY,
0781     },
0782     {
0783         .name = "list",
0784         .seq_show = devcgroup_seq_show,
0785         .private = DEVCG_LIST,
0786     },
0787     { } /* terminate */
0788 };
0789 
0790 struct cgroup_subsys devices_cgrp_subsys = {
0791     .css_alloc = devcgroup_css_alloc,
0792     .css_free = devcgroup_css_free,
0793     .css_online = devcgroup_online,
0794     .css_offline = devcgroup_offline,
0795     .legacy_cftypes = dev_cgroup_files,
0796 };
0797 
0798 /**
0799  * devcgroup_legacy_check_permission - checks if an inode operation is permitted
0800  * @dev_cgroup: the dev cgroup to be tested against
0801  * @type: device type
0802  * @major: device major number
0803  * @minor: device minor number
0804  * @access: combination of DEVCG_ACC_WRITE, DEVCG_ACC_READ and DEVCG_ACC_MKNOD
0805  *
0806  * returns 0 on success, -EPERM case the operation is not permitted
0807  */
0808 static int devcgroup_legacy_check_permission(short type, u32 major, u32 minor,
0809                     short access)
0810 {
0811     struct dev_cgroup *dev_cgroup;
0812     bool rc;
0813 
0814     rcu_read_lock();
0815     dev_cgroup = task_devcgroup(current);
0816     if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW)
0817         /* Can't match any of the exceptions, even partially */
0818         rc = !match_exception_partial(&dev_cgroup->exceptions,
0819                           type, major, minor, access);
0820     else
0821         /* Need to match completely one exception to be allowed */
0822         rc = match_exception(&dev_cgroup->exceptions, type, major,
0823                      minor, access);
0824     rcu_read_unlock();
0825 
0826     if (!rc)
0827         return -EPERM;
0828 
0829     return 0;
0830 }
0831 
0832 #endif /* CONFIG_CGROUP_DEVICE */
0833 
0834 #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
0835 
0836 int devcgroup_check_permission(short type, u32 major, u32 minor, short access)
0837 {
0838     int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
0839 
0840     if (rc)
0841         return rc;
0842 
0843     #ifdef CONFIG_CGROUP_DEVICE
0844     return devcgroup_legacy_check_permission(type, major, minor, access);
0845 
0846     #else /* CONFIG_CGROUP_DEVICE */
0847     return 0;
0848 
0849     #endif /* CONFIG_CGROUP_DEVICE */
0850 }
0851 EXPORT_SYMBOL(devcgroup_check_permission);
0852 #endif /* defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) */