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