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_bpf_whitelist_class(BPFProgram
*prog
, int type
, const char *acc
) {
88 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 */
100 access
= bpf_access_type(acc
);
104 insn
[2].imm
= access
;
106 r
= bpf_program_add_instructions(prog
, insn
, ELEMENTSOF(insn
));
108 log_error_errno(r
, "Extending device control BPF program failed: %m");
113 int cgroup_init_device_bpf(BPFProgram
**ret
, CGroupDevicePolicy policy
, bool whitelist
) {
114 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
)),
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),
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
)),
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
)),
133 _cleanup_(bpf_program_unrefp
) BPFProgram
*prog
= NULL
;
138 if (policy
== CGROUP_AUTO
&& !whitelist
)
141 r
= bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE
, &prog
);
143 return log_error_errno(r
, "Loading device control BPF program failed: %m");
145 if (policy
== CGROUP_CLOSED
|| whitelist
) {
146 r
= bpf_program_add_instructions(prog
, pre_insn
, ELEMENTSOF(pre_insn
));
148 return log_error_errno(r
, "Extending device control BPF program failed: %m");
151 *ret
= TAKE_PTR(prog
);
156 int cgroup_apply_device_bpf(Unit
*u
, BPFProgram
*prog
, CGroupDevicePolicy policy
, bool whitelist
) {
157 struct bpf_insn post_insn
[] = {
159 BPF_MOV64_IMM(BPF_REG_0
, 0),
164 struct bpf_insn exit_insn
[] = {
165 /* else return ALLOW */
166 BPF_MOV64_IMM(BPF_REG_0
, 1),
170 _cleanup_free_
char *path
= NULL
;
174 /* Remove existing program. */
175 u
->bpf_device_control_installed
= bpf_program_unref(u
->bpf_device_control_installed
);
179 if (policy
!= CGROUP_STRICT
|| whitelist
) {
182 r
= bpf_program_add_instructions(prog
, post_insn
, ELEMENTSOF(post_insn
));
184 return log_error_errno(r
, "Extending device control BPF program failed: %m");
186 /* Fixup PASS_JUMP_OFF jump offsets. */
187 for (off
= 0; off
< prog
->n_instructions
; off
++) {
188 struct bpf_insn
*ins
= &prog
->instructions
[off
];
190 if (ins
->code
== (BPF_JMP
| BPF_JA
) && ins
->off
== PASS_JUMP_OFF
)
191 ins
->off
= prog
->n_instructions
- off
- 1;
194 /* Explicitly forbid everything. */
195 exit_insn
[0].imm
= 0;
197 r
= bpf_program_add_instructions(prog
, exit_insn
, ELEMENTSOF(exit_insn
));
199 return log_error_errno(r
, "Extending device control BPF program failed: %m");
201 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, u
->cgroup_path
, NULL
, &path
);
203 return log_error_errno(r
, "Failed to determine cgroup path: %m");
206 r
= bpf_program_cgroup_attach(prog
, BPF_CGROUP_DEVICE
, path
, BPF_F_ALLOW_MULTI
);
208 return log_error_errno(r
, "Attaching device control BPF program to cgroup %s failed: %m", path
);
210 /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
211 u
->bpf_device_control_installed
= bpf_program_unref(u
->bpf_device_control_installed
);
213 /* Remember that this BPF program is installed now. */
214 u
->bpf_device_control_installed
= bpf_program_ref(prog
);
219 int bpf_devices_supported(void) {
220 struct bpf_insn trivial
[] = {
221 BPF_MOV64_IMM(BPF_REG_0
, 1),
225 _cleanup_(bpf_program_unrefp
) BPFProgram
*program
= NULL
;
226 static int supported
= -1;
229 /* Checks whether BPF device controller is supported. For this, we check five things:
231 * a) whether we are privileged
232 * b) whether the unified hierarchy is being used
233 * c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
239 if (geteuid() != 0) {
240 log_debug("Not enough privileges, BPF device control is not supported.");
241 return supported
= 0;
244 r
= cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER
);
246 return log_error_errno(r
, "Can't determine whether the unified hierarchy is used: %m");
248 log_debug("Not running with unified cgroups, BPF device control is not supported.");
249 return supported
= 0;
252 r
= bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE
, &program
);
254 log_debug_errno(r
, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
255 return supported
= 0;
258 r
= bpf_program_add_instructions(program
, trivial
, ELEMENTSOF(trivial
));
260 log_debug_errno(r
, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
261 return supported
= 0;
264 r
= bpf_program_load_kernel(program
, NULL
, 0);
266 log_debug_errno(r
, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
267 return supported
= 0;
270 return supported
= 1;