1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 #include <linux/libbpf.h>
4 #include "bpf-devices.h"
5 #include "bpf-program.h"
7 #define PASS_JUMP_OFF 4096
9 static int bpf_access_type(const char *acc
) {
17 r
|= BPF_DEVCG_ACC_READ
;
20 r
|= BPF_DEVCG_ACC_WRITE
;
23 r
|= BPF_DEVCG_ACC_MKNOD
;
32 int cgroup_bpf_whitelist_device(BPFProgram
*prog
, int type
, int major
, int minor
, const char *acc
) {
33 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 */
47 access
= bpf_access_type(acc
);
53 r
= bpf_program_add_instructions(prog
, insn
, ELEMENTSOF(insn
));
55 log_error_errno(r
, "Extending device control BPF program failed: %m");
60 int cgroup_bpf_whitelist_major(BPFProgram
*prog
, int type
, int major
, const char *acc
) {
61 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 */
74 access
= bpf_access_type(acc
);
80 r
= bpf_program_add_instructions(prog
, insn
, ELEMENTSOF(insn
));
82 log_error_errno(r
, "Extending device control BPF program failed: %m");
87 int cgroup_init_device_bpf(BPFProgram
**ret
, CGroupDevicePolicy policy
, bool whitelist
) {
88 struct bpf_insn pre_insn
[] = {
89 /* load device type to r2 */
90 BPF_LDX_MEM(BPF_H
, BPF_REG_2
, BPF_REG_1
,
91 offsetof(struct bpf_cgroup_dev_ctx
, access_type
)),
93 /* load access type to r3 */
94 BPF_LDX_MEM(BPF_W
, BPF_REG_3
, BPF_REG_1
,
95 offsetof(struct bpf_cgroup_dev_ctx
, access_type
)),
96 BPF_ALU32_IMM(BPF_RSH
, BPF_REG_3
, 16),
98 /* load major number to r4 */
99 BPF_LDX_MEM(BPF_W
, BPF_REG_4
, BPF_REG_1
,
100 offsetof(struct bpf_cgroup_dev_ctx
, major
)),
102 /* load minor number to r5 */
103 BPF_LDX_MEM(BPF_W
, BPF_REG_5
, BPF_REG_1
,
104 offsetof(struct bpf_cgroup_dev_ctx
, minor
)),
107 _cleanup_(bpf_program_unrefp
) BPFProgram
*prog
= NULL
;
112 if (policy
== CGROUP_AUTO
&& !whitelist
)
115 r
= bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE
, &prog
);
117 return log_error_errno(r
, "Loading device control BPF program failed: %m");
119 if (policy
== CGROUP_CLOSED
|| whitelist
) {
120 r
= bpf_program_add_instructions(prog
, pre_insn
, ELEMENTSOF(pre_insn
));
122 return log_error_errno(r
, "Extending device control BPF program failed: %m");
125 *ret
= TAKE_PTR(prog
);
130 int cgroup_apply_device_bpf(Unit
*u
, BPFProgram
*prog
, CGroupDevicePolicy policy
, bool whitelist
) {
131 struct bpf_insn post_insn
[] = {
133 BPF_MOV64_IMM(BPF_REG_0
, 0),
138 struct bpf_insn exit_insn
[] = {
139 /* else return ALLOW */
140 BPF_MOV64_IMM(BPF_REG_0
, 1),
144 _cleanup_free_
char *path
= NULL
;
148 /* Remove existing program. */
149 u
->bpf_device_control_installed
= bpf_program_unref(u
->bpf_device_control_installed
);
153 if (policy
!= CGROUP_STRICT
|| whitelist
) {
156 r
= bpf_program_add_instructions(prog
, post_insn
, ELEMENTSOF(post_insn
));
158 return log_error_errno(r
, "Extending device control BPF program failed: %m");
160 /* Fixup PASS_JUMP_OFF jump offsets. */
161 for (off
= 0; off
< prog
->n_instructions
; off
++) {
162 struct bpf_insn
*ins
= &prog
->instructions
[off
];
164 if (ins
->code
== (BPF_JMP
| BPF_JA
) && ins
->off
== PASS_JUMP_OFF
)
165 ins
->off
= prog
->n_instructions
- off
- 1;
168 /* Explicitly forbid everything. */
169 exit_insn
[0].imm
= 0;
171 r
= bpf_program_add_instructions(prog
, exit_insn
, ELEMENTSOF(exit_insn
));
173 return log_error_errno(r
, "Extending device control BPF program failed: %m");
175 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, u
->cgroup_path
, NULL
, &path
);
177 return log_error_errno(r
, "Failed to determine cgroup path: %m");
180 r
= bpf_program_cgroup_attach(prog
, BPF_CGROUP_DEVICE
, path
, BPF_F_ALLOW_MULTI
);
182 return log_error_errno(r
, "Attaching device control BPF program to cgroup %s failed: %m", path
);
184 /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
185 u
->bpf_device_control_installed
= bpf_program_unref(u
->bpf_device_control_installed
);
187 /* Remember that this BPF program is installed now. */
188 u
->bpf_device_control_installed
= bpf_program_ref(prog
);
193 int bpf_devices_supported(void) {
194 struct bpf_insn trivial
[] = {
195 BPF_MOV64_IMM(BPF_REG_0
, 1),
199 _cleanup_(bpf_program_unrefp
) BPFProgram
*program
= NULL
;
200 static int supported
= -1;
203 /* Checks whether BPF device controller is supported. For this, we check five things:
205 * a) whether we are privileged
206 * b) whether the unified hierarchy is being used
207 * c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
213 if (geteuid() != 0) {
214 log_debug("Not enough privileges, BPF device control is not supported.");
215 return supported
= 0;
218 r
= cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER
);
220 return log_error_errno(r
, "Can't determine whether the unified hierarchy is used: %m");
222 log_debug("Not running with unified cgroups, BPF device control is not supported.");
223 return supported
= 0;
226 r
= bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE
, &program
);
228 log_debug_errno(r
, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
229 return supported
= 0;
232 r
= bpf_program_add_instructions(program
, trivial
, ELEMENTSOF(trivial
));
234 log_debug_errno(r
, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
235 return supported
= 0;
238 r
= bpf_program_load_kernel(program
, NULL
, 0);
240 log_debug_errno(r
, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
241 return supported
= 0;
244 return supported
= 1;