LCOV - code coverage report
Current view: top level - core - bpf-devices.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 8 123 6.5 %
Date: 2019-08-23 13:36:53 Functions: 1 7 14.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 5 110 4.5 %

           Branch data     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                 :         44 : int bpf_devices_supported(void) {
     219                 :         44 :         struct bpf_insn trivial[] = {
     220                 :            :                 BPF_MOV64_IMM(BPF_REG_0, 1),
     221                 :            :                 BPF_EXIT_INSN()
     222                 :            :         };
     223                 :            : 
     224                 :         44 :         _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         [ +  + ]:         44 :         if (supported >= 0)
     236                 :         24 :                 return supported;
     237                 :            : 
     238         [ +  - ]:         20 :         if (geteuid() != 0) {
     239         [ +  + ]:         20 :                 log_debug("Not enough privileges, BPF device control is not supported.");
     240                 :         20 :                 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