"Fossies" - the Fresh Open Source Software Archive

Member "lxc-4.0.10/src/lxc/cgroups/cgroup2_devices.c" (16 Jul 2021, 20061 Bytes) of package /linux/misc/lxc-4.0.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cgroup2_devices.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.0.9_vs_4.0.10.

    1 /* SPDX-License-Identifier: LGPL-2.1+ */
    2 
    3 /* Parts of this taken from systemd's implementation. */
    4 
    5 #ifndef _GNU_SOURCE
    6 #define _GNU_SOURCE 1
    7 #endif
    8 #include <errno.h>
    9 #include <fcntl.h>
   10 #include <stdbool.h>
   11 #include <stddef.h>
   12 #include <stdint.h>
   13 #include <sys/stat.h>
   14 #include <sys/syscall.h>
   15 #include <sys/types.h>
   16 #include <unistd.h>
   17 
   18 #include "cgroup2_devices.h"
   19 #include "config.h"
   20 #include "file_utils.h"
   21 #include "log.h"
   22 #include "macro.h"
   23 #include "memory_utils.h"
   24 
   25 lxc_log_define(cgroup2_devices, cgroup);
   26 
   27 #define BPF_LOG_BUF_SIZE (1 << 23) /* 8MB */
   28 #ifndef BPF_LOG_LEVEL1
   29 #define BPF_LOG_LEVEL1 1
   30 #endif
   31 
   32 #ifndef BPF_LOG_LEVEL2
   33 #define BPF_LOG_LEVEL2 2
   34 #endif
   35 
   36 #ifndef BPF_LOG_LEVEL
   37 #define BPF_LOG_LEVEL (BPF_LOG_LEVEL1 | BPF_LOG_LEVEL2)
   38 #endif
   39 
   40 static int bpf_program_add_instructions(struct bpf_program *prog,
   41                     const struct bpf_insn *instructions,
   42                     size_t count)
   43 {
   44 
   45     struct bpf_insn *new_insn;
   46 
   47     if (prog->kernel_fd >= 0)
   48         return log_error_errno(-1, EBUSY, "Refusing to update bpf cgroup program that's already loaded");
   49 
   50     new_insn = realloc(prog->instructions, sizeof(struct bpf_insn) * (count + prog->n_instructions));
   51     if (!new_insn)
   52         return log_error_errno(-1, ENOMEM, "Failed to reallocate bpf cgroup program");
   53     prog->instructions = new_insn;
   54     memset(prog->instructions + prog->n_instructions, 0,
   55            sizeof(struct bpf_insn) * count);
   56     memcpy(prog->instructions + prog->n_instructions, instructions,
   57            sizeof(struct bpf_insn) * count);
   58     prog->n_instructions += count;
   59 
   60     return 0;
   61 }
   62 
   63 /* Memory load, dst_reg = *(uint *) (src_reg + off16) */
   64 #define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                               \
   65     ((struct bpf_insn){.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
   66                .dst_reg = DST,                             \
   67                .src_reg = SRC,                             \
   68                .off = OFF,                                 \
   69                .imm = 0})
   70 
   71 /* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
   72 #define BPF_ALU32_IMM(OP, DST, IMM)                              \
   73     ((struct bpf_insn){.code = BPF_ALU | BPF_OP(OP) | BPF_K, \
   74                .dst_reg = DST,                       \
   75                .src_reg = 0,                         \
   76                .off = 0,                             \
   77                .imm = IMM})
   78 
   79 /* Short form of mov, dst_reg = src_reg */
   80 #define BPF_MOV64_IMM(DST, IMM)                                 \
   81     ((struct bpf_insn){.code = BPF_ALU64 | BPF_MOV | BPF_K, \
   82                .dst_reg = DST,                      \
   83                .src_reg = 0,                        \
   84                .off = 0,                            \
   85                .imm = IMM})
   86 
   87 #define BPF_MOV32_REG(DST, SRC)                               \
   88     ((struct bpf_insn){.code = BPF_ALU | BPF_MOV | BPF_X, \
   89                .dst_reg = DST,                    \
   90                .src_reg = SRC,                    \
   91                .off = 0,                          \
   92                .imm = 0})
   93 
   94 /* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
   95 #define BPF_JMP_REG(OP, DST, SRC, OFF)                           \
   96     ((struct bpf_insn){.code = BPF_JMP | BPF_OP(OP) | BPF_X, \
   97                .dst_reg = DST,                       \
   98                .src_reg = SRC,                       \
   99                .off = OFF,                           \
  100                .imm = 0})
  101 
  102 /* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
  103 #define BPF_JMP_IMM(OP, DST, IMM, OFF)                           \
  104     ((struct bpf_insn){.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
  105                .dst_reg = DST,                       \
  106                .src_reg = 0,                         \
  107                .off = OFF,                           \
  108                .imm = IMM})
  109 
  110 /* Program exit */
  111 #define BPF_EXIT_INSN()                                \
  112     ((struct bpf_insn){.code = BPF_JMP | BPF_EXIT, \
  113                .dst_reg = 0,               \
  114                .src_reg = 0,               \
  115                .off = 0,                   \
  116                .imm = 0})
  117 
  118 static int bpf_access_mask(const char *acc, __u32 *mask)
  119 {
  120     if (!acc)
  121         return 0;
  122 
  123     for (; *acc; acc++) {
  124         switch (*acc) {
  125         case 'r':
  126             *mask |= BPF_DEVCG_ACC_READ;
  127             break;
  128         case 'w':
  129             *mask |= BPF_DEVCG_ACC_WRITE;
  130             break;
  131         case 'm':
  132             *mask |= BPF_DEVCG_ACC_MKNOD;
  133             break;
  134         default:
  135             return -EINVAL;
  136         }
  137     }
  138 
  139     return 0;
  140 }
  141 
  142 static int bpf_device_type(char type)
  143 {
  144     switch (type) {
  145     case 'a':
  146         return 0;
  147     case 'b':
  148         return BPF_DEVCG_DEV_BLOCK;
  149     case 'c':
  150         return BPF_DEVCG_DEV_CHAR;
  151     }
  152 
  153     return -1;
  154 }
  155 
  156 static inline bool bpf_device_all_access(__u32 access_mask)
  157 {
  158     return access_mask == (BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD);
  159 }
  160 
  161 struct bpf_program *bpf_program_new(uint32_t prog_type)
  162 {
  163     __do_free struct bpf_program *prog = NULL;
  164 
  165     prog = zalloc(sizeof(struct bpf_program));
  166     if (!prog)
  167         return ret_set_errno(NULL, ENOMEM);
  168 
  169     prog->prog_type = prog_type;
  170     prog->kernel_fd = -EBADF;
  171     prog->fd_cgroup = -EBADF;
  172     /*
  173      * By default a allowlist is used unless the user tells us otherwise.
  174      */
  175     prog->device_list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
  176 
  177     return move_ptr(prog);
  178 }
  179 
  180 int bpf_program_init(struct bpf_program *prog)
  181 {
  182     const struct bpf_insn pre_insn[] = {
  183         /* load device type to r2 */
  184         BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offsetof(struct bpf_cgroup_dev_ctx, access_type)),
  185         BPF_ALU32_IMM(BPF_AND, BPF_REG_2, 0xFFFF),
  186 
  187         /* load access type to r3 */
  188         BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, offsetof(struct bpf_cgroup_dev_ctx, access_type)),
  189         BPF_ALU32_IMM(BPF_RSH, BPF_REG_3, 16),
  190 
  191         /* load major number to r4 */
  192         BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, offsetof(struct bpf_cgroup_dev_ctx, major)),
  193 
  194         /* load minor number to r5 */
  195         BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_1, offsetof(struct bpf_cgroup_dev_ctx, minor)),
  196     };
  197 
  198     if (!prog)
  199         return ret_set_errno(-1, EINVAL);
  200 
  201     return bpf_program_add_instructions(prog, pre_insn, ARRAY_SIZE(pre_insn));
  202 }
  203 
  204 int bpf_program_append_device(struct bpf_program *prog, struct device_item *device)
  205 {
  206     int jump_nr = 1;
  207     __u32 access_mask = 0;
  208     int device_type, ret;
  209     struct bpf_insn bpf_access_decision[2];
  210 
  211     if (!prog || !device)
  212         return ret_set_errno(-1, EINVAL);
  213 
  214     ret = bpf_access_mask(device->access, &access_mask);
  215     if (ret < 0)
  216         return log_error_errno(ret, -ret, "Invalid access mask specified %s", device->access);
  217 
  218     if (!bpf_device_all_access(access_mask))
  219         jump_nr++;
  220 
  221     device_type = bpf_device_type(device->type);
  222     if (device_type < 0)
  223         return log_error_errno(-1, EINVAL, "Invalid bpf cgroup device type %c", device->type);
  224 
  225     if (device_type > 0)
  226         jump_nr++;
  227 
  228     if (device->major >= 0)
  229         jump_nr++;
  230 
  231     if (device->minor >= 0)
  232         jump_nr++;
  233 
  234     if (!bpf_device_all_access(access_mask)) {
  235         struct bpf_insn ins[] = {
  236             BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
  237             BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access_mask),
  238             BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, jump_nr--),
  239         };
  240 
  241         ret = bpf_program_add_instructions(prog, ins, ARRAY_SIZE(ins));
  242         if (ret)
  243             return log_error_errno(-1, errno, "Failed to add instructions to bpf cgroup program");
  244     }
  245 
  246     if (device_type > 0) {
  247         struct bpf_insn ins[] = {
  248             BPF_JMP_IMM(BPF_JNE, BPF_REG_2, device_type, jump_nr--),
  249         };
  250 
  251         ret = bpf_program_add_instructions(prog, ins, ARRAY_SIZE(ins));
  252         if (ret)
  253             return log_error_errno(-1, errno, "Failed to add instructions to bpf cgroup program");
  254     }
  255 
  256     if (device->major >= 0) {
  257         struct bpf_insn ins[] = {
  258             BPF_JMP_IMM(BPF_JNE, BPF_REG_4, device->major, jump_nr--),
  259         };
  260 
  261         ret = bpf_program_add_instructions(prog, ins, ARRAY_SIZE(ins));
  262         if (ret)
  263             return log_error_errno(-1, errno, "Failed to add instructions to bpf cgroup program");
  264     }
  265 
  266     if (device->minor >= 0) {
  267         struct bpf_insn ins[] = {
  268             BPF_JMP_IMM(BPF_JNE, BPF_REG_5, device->minor, jump_nr--),
  269         };
  270 
  271         ret = bpf_program_add_instructions(prog, ins, ARRAY_SIZE(ins));
  272         if (ret)
  273             return log_error_errno(-1, errno, "Failed to add instructions to bpf cgroup program");
  274     }
  275 
  276     bpf_access_decision[0] = BPF_MOV64_IMM(BPF_REG_0, device->allow);
  277     bpf_access_decision[1] = BPF_EXIT_INSN();
  278     ret = bpf_program_add_instructions(prog, bpf_access_decision,
  279                        ARRAY_SIZE(bpf_access_decision));
  280     if (ret)
  281         return log_error_errno(-1, errno, "Failed to add instructions to bpf cgroup program");
  282 
  283     return 0;
  284 }
  285 
  286 int bpf_program_finalize(struct bpf_program *prog)
  287 {
  288     struct bpf_insn ins[2];
  289 
  290     if (!prog)
  291         return ret_set_errno(-1, EINVAL);
  292 
  293     TRACE("Device bpf program %s all devices by default",
  294           prog->device_list_type == LXC_BPF_DEVICE_CGROUP_ALLOWLIST
  295           ? "blocks"
  296           : "allows");
  297 
  298     ins[0] = BPF_MOV64_IMM(BPF_REG_0, prog->device_list_type);
  299     ins[1] = BPF_EXIT_INSN();
  300     return bpf_program_add_instructions(prog, ins, ARRAY_SIZE(ins));
  301 }
  302 
  303 static int bpf_program_load_kernel(struct bpf_program *prog)
  304 {
  305     __do_free char *log_buf = NULL;
  306     __u32 log_level = 0, log_size = 0;
  307     union bpf_attr *attr;
  308 
  309     if (prog->kernel_fd >= 0)
  310         return 0;
  311 
  312     if (lxc_log_trace()) {
  313         log_buf = zalloc(BPF_LOG_BUF_SIZE);
  314         if (!log_buf) {
  315             WARN("Failed to allocate bpf log buffer");
  316         } else {
  317             log_level = BPF_LOG_LEVEL;
  318             log_size = BPF_LOG_BUF_SIZE;
  319         }
  320     }
  321 
  322     attr = &(union bpf_attr){
  323         .prog_type  = prog->prog_type,
  324         .insns      = PTR_TO_U64(prog->instructions),
  325         .insn_cnt   = prog->n_instructions,
  326         .license    = PTR_TO_U64("GPL"),
  327         .log_buf    = PTR_TO_U64(log_buf),
  328         .log_level  = log_level,
  329         .log_size   = log_size,
  330     };
  331 
  332     prog->kernel_fd = bpf(BPF_PROG_LOAD, attr, sizeof(*attr));
  333     if (prog->kernel_fd < 0)
  334         return log_error_errno(-1, errno, "Failed to load bpf program: %s",
  335                        log_buf ?: "(null)");
  336 
  337     TRACE("Loaded bpf program: %s", log_buf ?: "(null)");
  338     return 0;
  339 }
  340 
  341 static int bpf_program_cgroup_attach(struct bpf_program *prog, int type,
  342                      int fd_cgroup, __u32 flags)
  343 {
  344     __do_close int fd_attach = -EBADF;
  345     int ret;
  346     union bpf_attr *attr;
  347 
  348     if (prog->fd_cgroup >= 0 || prog->kernel_fd >= 0)
  349         return ret_errno(EBUSY);
  350 
  351     if (fd_cgroup < 0)
  352         return ret_errno(EBADF);
  353 
  354     if (flags & ~(BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI | BPF_F_REPLACE))
  355         return syserror_set(-EINVAL, "Invalid flags for bpf program");
  356 
  357     /*
  358      * Don't allow the bpf program to be overwritten for now. If we ever
  359      * allow this we need to verify that the attach_flags of the current
  360      * bpf program and the attach_flags of the new program match.
  361      */
  362     if (flags & BPF_F_ALLOW_OVERRIDE)
  363         INFO("Allowing to override bpf program");
  364 
  365     /* Leave the caller's fd alone. */
  366     fd_attach = dup_cloexec(fd_cgroup);
  367     if (fd_attach < 0)
  368         return -errno;
  369 
  370     ret = bpf_program_load_kernel(prog);
  371     if (ret < 0)
  372         return syserror("Failed to load bpf program");
  373 
  374     attr = &(union bpf_attr){
  375         .attach_type    = type,
  376         .target_fd  = fd_attach,
  377         .attach_bpf_fd  = prog->kernel_fd,
  378         .attach_flags   = flags,
  379     };
  380 
  381     ret = bpf(BPF_PROG_ATTACH, attr, sizeof(*attr));
  382     if (ret < 0)
  383         return syserror("Failed to attach bpf program");
  384 
  385     prog->fd_cgroup     = move_fd(fd_attach);
  386     prog->attached_type = type;
  387     prog->attached_flags    = flags;
  388 
  389     TRACE("Attached bpf program to cgroup %d", prog->fd_cgroup);
  390     return 0;
  391 }
  392 
  393 int bpf_program_cgroup_detach(struct bpf_program *prog)
  394 {
  395     __do_close int fd_cgroup = -EBADF, fd_kernel = -EBADF;
  396     int ret;
  397     union bpf_attr *attr;
  398 
  399     if (!prog)
  400         return 0;
  401 
  402     /* Ensure that these fds are wiped. */
  403     fd_cgroup = move_fd(prog->fd_cgroup);
  404     fd_kernel = move_fd(prog->kernel_fd);
  405 
  406     if (fd_cgroup < 0 || fd_kernel < 0)
  407         return 0;
  408 
  409     attr = &(union bpf_attr){
  410         .attach_type    = prog->attached_type,
  411         .target_fd  = fd_cgroup,
  412         .attach_bpf_fd  = fd_kernel,
  413     };
  414 
  415     ret = bpf(BPF_PROG_DETACH, attr, sizeof(*attr));
  416     if (ret < 0)
  417         return syserror("Failed to detach bpf program from cgroup %d", fd_cgroup);
  418 
  419     TRACE("Detached bpf program from cgroup %d", fd_cgroup);
  420 
  421     return 0;
  422 }
  423 
  424 void bpf_device_program_free(struct cgroup_ops *ops)
  425 {
  426     if (ops->cgroup2_devices) {
  427         (void)bpf_program_cgroup_detach(ops->cgroup2_devices);
  428         bpf_program_free(ops->cgroup2_devices);
  429         ops->cgroup2_devices = NULL;
  430     }
  431 }
  432 
  433 static inline bool bpf_device_list_block_all(const struct bpf_devices *bpf_devices)
  434 {
  435     /* LXC_BPF_DEVICE_CGROUP_ALLOWLIST  -> block ("allowlist") all devices. */
  436     return bpf_devices->list_type == LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
  437 }
  438 
  439 static inline bool bpf_device_add(const struct bpf_devices *bpf_devices,
  440                   struct device_item *device)
  441 {
  442     /* We're blocking all devices so skip individual deny rules. */
  443     if (bpf_device_list_block_all(bpf_devices) && !device->allow)
  444         return log_trace(false, "Device cgroup blocks all devices; skipping specific deny rules");
  445 
  446     /* We're allowing all devices so skip individual allow rules. */
  447     if (!bpf_device_list_block_all(bpf_devices) && device->allow)
  448         return log_trace(false, "Device cgroup allows all devices; skipping specific allow rules");
  449 
  450     return true;
  451 }
  452 
  453 int bpf_list_add_device(struct bpf_devices *bpf_devices,
  454             struct device_item *device)
  455 {
  456     __do_free struct lxc_list *list_elem = NULL;
  457     __do_free struct device_item *new_device = NULL;
  458     struct lxc_list *it;
  459 
  460     if (!bpf_devices || !device)
  461         return ret_errno(EINVAL);
  462 
  463     /* Check whether this determines the list type. */
  464     if (device->type == 'a' &&
  465         device->major < 0 &&
  466         device->minor < 0 &&
  467         is_empty_string(device->access)) {
  468         if (device->allow) {
  469             bpf_devices->list_type = LXC_BPF_DEVICE_CGROUP_DENYLIST;
  470             TRACE("Device cgroup will allow (\"denylist\") all devices by default");
  471         } else {
  472             bpf_devices->list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
  473             TRACE("Device cgroup will block (\"allowlist\") all devices by default");
  474         }
  475 
  476         /* Reset the device list. */
  477         lxc_clear_cgroup2_devices(bpf_devices);
  478         TRACE("Resetting cgroup device list");
  479         return 1; /* The device list was altered. */
  480     }
  481 
  482     TRACE("Processing new device rule: type %c, major %d, minor %d, access %s, allow %d",
  483           device->type, device->major, device->minor, device->access, device->allow);
  484 
  485     lxc_list_for_each(it, &bpf_devices->device_item) {
  486         struct device_item *cur = it->elem;
  487 
  488         if (cur->type != device->type)
  489             continue;
  490         if (cur->major != device->major)
  491             continue;
  492         if (cur->minor != device->minor)
  493             continue;
  494         if (!strequal(cur->access, device->access))
  495             continue;
  496 
  497         if (!bpf_device_add(bpf_devices, cur))
  498             continue;
  499 
  500         /*
  501          * The rule is switched from allow to deny or vica versa so
  502          * don't bother allocating just flip the existing one.
  503          */
  504         if (cur->allow != device->allow) {
  505             cur->allow = device->allow;
  506 
  507             return log_trace(1, "Switched existing device rule"); /* The device list was altered. */
  508         }
  509 
  510 
  511         return log_trace(0, "Reused existing device rule"); /* The device list wasn't altered. */
  512     }
  513 
  514     list_elem = malloc(sizeof(*list_elem));
  515     if (!list_elem)
  516         return syserror_set(ENOMEM, "Failed to allocate new device list");
  517 
  518     new_device = memdup(device, sizeof(struct device_item));
  519     if (!new_device)
  520         return syserror_set(ENOMEM, "Failed to allocate new device item");
  521 
  522     lxc_list_add_elem(list_elem, move_ptr(new_device));
  523     lxc_list_add_tail(&bpf_devices->device_item, move_ptr(list_elem));
  524 
  525     return log_trace(1, "Added new device rule"); /* The device list was altered. */
  526 }
  527 
  528 bool bpf_devices_cgroup_supported(void)
  529 {
  530     __do_bpf_program_free struct bpf_program *prog = NULL;
  531     const struct bpf_insn insn[] = {
  532         BPF_MOV64_IMM(BPF_REG_0, 1),
  533         BPF_EXIT_INSN(),
  534     };
  535     int ret;
  536 
  537     if (geteuid() != 0)
  538         return log_trace(false,
  539                  "The bpf device cgroup requires real root");
  540 
  541     prog = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE);
  542     if (!prog)
  543         return log_trace(false, "Failed to allocate new bpf device cgroup program");
  544 
  545     ret = bpf_program_init(prog);
  546     if (ret)
  547         return log_error_errno(false, ENOMEM, "Failed to initialize bpf program");
  548 
  549     ret = bpf_program_add_instructions(prog, insn, ARRAY_SIZE(insn));
  550     if (ret < 0)
  551         return log_trace(false, "Failed to add new instructions to bpf device cgroup program");
  552 
  553     ret = bpf_program_load_kernel(prog);
  554     if (ret < 0)
  555         return log_trace(false, "Failed to load new bpf device cgroup program");
  556 
  557     return log_trace(true, "The bpf device cgroup is supported");
  558 }
  559 
  560 static struct bpf_program *__bpf_cgroup_devices(struct bpf_devices *bpf_devices)
  561 {
  562     __do_bpf_program_free struct bpf_program *prog = NULL;
  563     int ret;
  564     struct lxc_list *it;
  565 
  566     prog = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE);
  567     if (!prog)
  568         return syserror_ret(NULL, "Failed to create new bpf program");
  569 
  570     ret = bpf_program_init(prog);
  571     if (ret)
  572         return syserror_ret(NULL, "Failed to initialize bpf program");
  573 
  574     prog->device_list_type = bpf_devices->list_type;
  575     TRACE("Device cgroup %s all devices by default",
  576           bpf_device_list_block_all(bpf_devices) ? "blocks" : "allows");
  577 
  578     lxc_list_for_each(it, &bpf_devices->device_item) {
  579         struct device_item *cur = it->elem;
  580 
  581         TRACE("Processing device rule: type %c, major %d, minor %d, access %s, allow %d",
  582               cur->type, cur->major, cur->minor, cur->access, cur->allow);
  583 
  584         if (!bpf_device_add(bpf_devices, cur))
  585             continue;
  586 
  587         ret = bpf_program_append_device(prog, cur);
  588         if (ret)
  589             return syserror_ret(NULL, "Failed adding new device rule");
  590 
  591         TRACE("Added new device rule");
  592     }
  593 
  594     ret = bpf_program_finalize(prog);
  595     if (ret)
  596         return syserror_ret(NULL, "Failed to finalize device program");
  597 
  598     return move_ptr(prog);
  599 }
  600 
  601 bool bpf_cgroup_devices_attach(struct cgroup_ops *ops,
  602                    struct bpf_devices *bpf_devices)
  603 {
  604     __do_bpf_program_free struct bpf_program *prog = NULL;
  605     int ret;
  606 
  607     prog = __bpf_cgroup_devices(bpf_devices);
  608     if (!prog)
  609         return syserror_ret(false, "Failed to create bpf program");
  610 
  611     ret = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE,
  612                     ops->unified->dfd_lim,
  613                     BPF_F_ALLOW_MULTI);
  614     if (ret)
  615         return syserror_ret(false, "Failed to attach bpf program");
  616 
  617     /* Replace old bpf program. */
  618     swap(prog, ops->cgroup2_devices);
  619     return log_trace(true, "Attached bpf program");
  620 }
  621 
  622 bool bpf_cgroup_devices_update(struct cgroup_ops *ops,
  623                    struct bpf_devices *bpf_devices,
  624                    struct device_item *new)
  625 {
  626     __do_bpf_program_free struct bpf_program *prog = NULL;
  627     static int can_use_bpf_replace = -1;
  628     struct bpf_program *prog_old;
  629     union bpf_attr *attr;
  630     int ret;
  631 
  632     if (!ops)
  633         return ret_set_errno(false, EINVAL);
  634 
  635     if (!pure_unified_layout(ops))
  636         return ret_set_errno(false, EINVAL);
  637 
  638     if (ops->unified->dfd_lim < 0)
  639         return ret_set_errno(false, EBADF);
  640 
  641     /*
  642      * Note that bpf_list_add_device() returns 1 if it altered the device
  643      * list and 0 if it didn't; both return values indicate success.
  644      * Only a negative return value indicates an error.
  645      */
  646     ret = bpf_list_add_device(bpf_devices, new);
  647     if (ret < 0)
  648         return false;
  649 
  650     if (ret == 0)
  651         return log_trace(true, "Device bpf program unaltered");
  652 
  653     /* No previous device program attached. */
  654     prog_old = ops->cgroup2_devices;
  655     if (!prog_old)
  656         return bpf_cgroup_devices_attach(ops, bpf_devices);
  657 
  658     prog = __bpf_cgroup_devices(bpf_devices);
  659     if (!prog)
  660         return syserror_ret(false, "Failed to create bpf program");
  661 
  662     ret = bpf_program_load_kernel(prog);
  663     if (ret < 0)
  664         return syserror_ret(false, "Failed to load bpf program");
  665 
  666     attr = &(union bpf_attr){
  667         .attach_type    = prog_old->attached_type,
  668         .target_fd  = prog_old->fd_cgroup,
  669         .attach_bpf_fd  = prog->kernel_fd,
  670     };
  671 
  672     switch (can_use_bpf_replace) {
  673     case 1:
  674         attr->replace_bpf_fd = prog_old->kernel_fd;
  675         attr->attach_flags = BPF_F_REPLACE | BPF_F_ALLOW_MULTI;
  676 
  677         ret = bpf(BPF_PROG_ATTACH, attr, sizeof(*attr));
  678         break;
  679     case -1:
  680         attr->replace_bpf_fd = prog_old->kernel_fd;
  681         attr->attach_flags = BPF_F_REPLACE | BPF_F_ALLOW_MULTI;
  682 
  683         can_use_bpf_replace = !bpf(BPF_PROG_ATTACH, attr, sizeof(*attr));
  684         if (can_use_bpf_replace > 0)
  685             break;
  686 
  687         __fallthrough;
  688     case 0:
  689         attr->attach_flags = BPF_F_ALLOW_MULTI;
  690         attr->replace_bpf_fd = 0;
  691 
  692         ret = bpf(BPF_PROG_ATTACH, attr, sizeof(*attr));
  693         break;
  694     }
  695     if (ret < 0)
  696         return syserror_ret(false, "Failed to update bpf program");
  697 
  698     if (can_use_bpf_replace > 0) {
  699         /* The old program was automatically detached by the kernel. */
  700         close_prot_errno_disarm(prog_old->kernel_fd);
  701         /* The new bpf program now owns the cgroup fd. */
  702         prog->fd_cgroup = move_fd(prog_old->fd_cgroup);
  703         TRACE("Replaced existing bpf program");
  704     } else {
  705         TRACE("Appended bpf program");
  706     }
  707     prog->attached_type  = prog_old->attached_type;
  708     prog->attached_flags = attr->attach_flags;
  709     swap(prog, ops->cgroup2_devices);
  710 
  711     return true;
  712 }