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) */