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 21 static DEFINE_MUTEX(devcgroup_mutex); 22 23 enum 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 35 struct 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 43 struct dev_cgroup { 44 struct cgroup_subsys_state css; 45 struct list_head exceptions; 46 enum devcg_behavior behavior; 47 }; 48 49 static 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 54 static 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 */ 62 static 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 77 free_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 */ 88 static 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 */ 120 static 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 143 static 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 */ 159 static 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 166 static 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 */ 177 static 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 198 static 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 */ 210 static struct cgroup_subsys_state * 211 devcgroup_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 224 static 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 239 static 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 251 static 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 262 static void set_majmin(char *str, unsigned m) 263 { 264 if (m == ~0) 265 strcpy(str, "*"); 266 else 267 sprintf(str, "%u", m); 268 } 269 270 static 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 */ 316 static 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 */ 353 static 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 */ 393 static 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 */ 451 static 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 */ 472 static 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 */ 498 static 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 */ 518 static 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 */ 537 static 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 */ 599 static 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 759 static 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 771 static 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 790 struct 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 */ 808 static 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 836 int 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 } 851 EXPORT_SYMBOL(devcgroup_check_permission); 852 #endif /* defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) */ 853