cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

device_cgroup.c (21647B)


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