LCOV - code coverage report
Current view: top level - core - bpf-devices.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 8 123 6.5 %
Date: 2019-08-22 15:41:25 Functions: 1 7 14.3 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : #include <linux/bpf_insn.h>
       3             : 
       4             : #include "bpf-devices.h"
       5             : #include "bpf-program.h"
       6             : 
       7             : #define PASS_JUMP_OFF 4096
       8             : 
       9           0 : static int bpf_access_type(const char *acc) {
      10           0 :         int r = 0;
      11             : 
      12           0 :         assert(acc);
      13             : 
      14           0 :         for (; *acc; acc++)
      15           0 :                 switch(*acc) {
      16           0 :                 case 'r':
      17           0 :                         r |= BPF_DEVCG_ACC_READ;
      18           0 :                         break;
      19           0 :                 case 'w':
      20           0 :                         r |= BPF_DEVCG_ACC_WRITE;
      21           0 :                         break;
      22           0 :                 case 'm':
      23           0 :                         r |= BPF_DEVCG_ACC_MKNOD;
      24           0 :                         break;
      25           0 :                 default:
      26           0 :                         return -EINVAL;
      27             :                 }
      28             : 
      29           0 :         return r;
      30             : }
      31             : 
      32           0 : int cgroup_bpf_whitelist_device(BPFProgram *prog, int type, int major, int minor, const char *acc) {
      33           0 :         struct bpf_insn insn[] = {
      34             :                 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 6), /* compare device type */
      35             :                 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
      36             :                 BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
      37             :                 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 3), /* compare access type */
      38             :                 BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 2), /* compare major */
      39             :                 BPF_JMP_IMM(BPF_JNE, BPF_REG_5, minor, 1), /* compare minor */
      40             :                 BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
      41             :         };
      42             :         int r, access;
      43             : 
      44           0 :         assert(prog);
      45           0 :         assert(acc);
      46             : 
      47           0 :         access = bpf_access_type(acc);
      48           0 :         if (access <= 0)
      49           0 :                 return -EINVAL;
      50             : 
      51           0 :         insn[2].imm = access;
      52             : 
      53           0 :         r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
      54           0 :         if (r < 0)
      55           0 :                 log_error_errno(r, "Extending device control BPF program failed: %m");
      56             : 
      57           0 :         return r;
      58             : }
      59             : 
      60           0 : int cgroup_bpf_whitelist_major(BPFProgram *prog, int type, int major, const char *acc) {
      61           0 :         struct bpf_insn insn[] = {
      62             :                 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
      63             :                 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
      64             :                 BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
      65             :                 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 2), /* compare access type */
      66             :                 BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 1), /* compare major */
      67             :                 BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
      68             :         };
      69             :         int r, access;
      70             : 
      71           0 :         assert(prog);
      72           0 :         assert(acc);
      73             : 
      74           0 :         access = bpf_access_type(acc);
      75           0 :         if (access <= 0)
      76           0 :                 return -EINVAL;
      77             : 
      78           0 :         insn[2].imm = access;
      79             : 
      80           0 :         r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
      81           0 :         if (r < 0)
      82           0 :                 log_error_errno(r, "Extending device control BPF program failed: %m");
      83             : 
      84           0 :         return r;
      85             : }
      86             : 
      87           0 : int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc) {
      88           0 :         struct bpf_insn insn[] = {
      89             :                 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
      90             :                 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
      91             :                 BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
      92             :                 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 1), /* compare access type */
      93             :                 BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
      94             :         };
      95             :         int r, access;
      96             : 
      97           0 :         assert(prog);
      98           0 :         assert(acc);
      99             : 
     100           0 :         access = bpf_access_type(acc);
     101           0 :         if (access <= 0)
     102           0 :                 return -EINVAL;
     103             : 
     104           0 :         insn[2].imm = access;
     105             : 
     106           0 :         r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
     107           0 :         if (r < 0)
     108           0 :                 log_error_errno(r, "Extending device control BPF program failed: %m");
     109             : 
     110           0 :         return r;
     111             : }
     112             : 
     113           0 : int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist) {
     114           0 :         struct bpf_insn pre_insn[] = {
     115             :                 /* load device type to r2 */
     116             :                 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
     117             :                             offsetof(struct bpf_cgroup_dev_ctx, access_type)),
     118             : 
     119             :                 /* load access type to r3 */
     120             :                 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
     121             :                             offsetof(struct bpf_cgroup_dev_ctx, access_type)),
     122             :                 BPF_ALU32_IMM(BPF_RSH, BPF_REG_3, 16),
     123             : 
     124             :                 /* load major number to r4 */
     125             :                 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1,
     126             :                             offsetof(struct bpf_cgroup_dev_ctx, major)),
     127             : 
     128             :                 /* load minor number to r5 */
     129             :                 BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_1,
     130             :                             offsetof(struct bpf_cgroup_dev_ctx, minor)),
     131             :         };
     132             : 
     133           0 :         _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
     134             :         int r;
     135             : 
     136           0 :         assert(ret);
     137             : 
     138           0 :         if (policy == CGROUP_AUTO && !whitelist)
     139           0 :                 return 0;
     140             : 
     141           0 :         r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
     142           0 :         if (r < 0)
     143           0 :                 return log_error_errno(r, "Loading device control BPF program failed: %m");
     144             : 
     145           0 :         if (policy == CGROUP_CLOSED || whitelist) {
     146           0 :                 r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn));
     147           0 :                 if (r < 0)
     148           0 :                         return log_error_errno(r, "Extending device control BPF program failed: %m");
     149             :         }
     150             : 
     151           0 :         *ret = TAKE_PTR(prog);
     152             : 
     153           0 :         return 0;
     154             : }
     155             : 
     156           0 : int cgroup_apply_device_bpf(Unit *u, BPFProgram *prog, CGroupDevicePolicy policy, bool whitelist) {
     157           0 :         struct bpf_insn post_insn[] = {
     158             :                 /* return DENY */
     159             :                 BPF_MOV64_IMM(BPF_REG_0, 0),
     160             :                 BPF_JMP_A(1),
     161             : 
     162             :         };
     163             : 
     164           0 :         struct bpf_insn exit_insn[] = {
     165             :                 /* else return ALLOW */
     166             :                 BPF_MOV64_IMM(BPF_REG_0, 1),
     167             :                 BPF_EXIT_INSN()
     168             :         };
     169             : 
     170           0 :         _cleanup_free_ char *path = NULL;
     171             :         int r;
     172             : 
     173           0 :         if (!prog) {
     174             :                 /* Remove existing program. */
     175           0 :                 u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
     176           0 :                 return 0;
     177             :         }
     178             : 
     179           0 :         if (policy != CGROUP_STRICT || whitelist) {
     180             :                 size_t off;
     181             : 
     182           0 :                 r = bpf_program_add_instructions(prog, post_insn, ELEMENTSOF(post_insn));
     183           0 :                 if (r < 0)
     184           0 :                         return log_error_errno(r, "Extending device control BPF program failed: %m");
     185             : 
     186             :                 /* Fixup PASS_JUMP_OFF jump offsets. */
     187           0 :                 for (off = 0; off < prog->n_instructions; off++) {
     188           0 :                         struct bpf_insn *ins = &prog->instructions[off];
     189             : 
     190           0 :                         if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
     191           0 :                                 ins->off = prog->n_instructions - off - 1;
     192             :                 }
     193             :         } else
     194             :                 /* Explicitly forbid everything. */
     195           0 :                 exit_insn[0].imm = 0;
     196             : 
     197           0 :         r = bpf_program_add_instructions(prog, exit_insn, ELEMENTSOF(exit_insn));
     198           0 :         if (r < 0)
     199           0 :                 return log_error_errno(r, "Extending device control BPF program failed: %m");
     200             : 
     201           0 :         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
     202           0 :         if (r < 0)
     203           0 :                 return log_error_errno(r, "Failed to determine cgroup path: %m");
     204             : 
     205           0 :         r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, path, BPF_F_ALLOW_MULTI);
     206           0 :         if (r < 0)
     207           0 :                 return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m", path);
     208             : 
     209             :         /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
     210           0 :         u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
     211             : 
     212             :         /* Remember that this BPF program is installed now. */
     213           0 :         u->bpf_device_control_installed = bpf_program_ref(prog);
     214             : 
     215           0 :         return 0;
     216             : }
     217             : 
     218          11 : int bpf_devices_supported(void) {
     219          11 :         struct bpf_insn trivial[] = {
     220             :                 BPF_MOV64_IMM(BPF_REG_0, 1),
     221             :                 BPF_EXIT_INSN()
     222             :         };
     223             : 
     224          11 :         _cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
     225             :         static int supported = -1;
     226             :         int r;
     227             : 
     228             :         /* Checks whether BPF device controller is supported. For this, we check five things:
     229             :          *
     230             :          * a) whether we are privileged
     231             :          * b) whether the unified hierarchy is being used
     232             :          * c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
     233             :          */
     234             : 
     235          11 :         if (supported >= 0)
     236           6 :                 return supported;
     237             : 
     238           5 :         if (geteuid() != 0) {
     239           5 :                 log_debug("Not enough privileges, BPF device control is not supported.");
     240           5 :                 return supported = 0;
     241             :         }
     242             : 
     243           0 :         r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
     244           0 :         if (r < 0)
     245           0 :                 return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
     246           0 :         if (r == 0) {
     247           0 :                 log_debug("Not running with unified cgroups, BPF device control is not supported.");
     248           0 :                 return supported = 0;
     249             :         }
     250             : 
     251           0 :         r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
     252           0 :         if (r < 0) {
     253           0 :                 log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
     254           0 :                 return supported = 0;
     255             :         }
     256             : 
     257           0 :         r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial));
     258           0 :         if (r < 0) {
     259           0 :                 log_debug_errno(r, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
     260           0 :                 return supported = 0;
     261             :         }
     262             : 
     263           0 :         r = bpf_program_load_kernel(program, NULL, 0);
     264           0 :         if (r < 0) {
     265           0 :                 log_debug_errno(r, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
     266           0 :                 return supported = 0;
     267             :         }
     268             : 
     269           0 :         return supported = 1;
     270             : }

Generated by: LCOV version 1.14