]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/bpf-devices.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / core / bpf-devices.c
CommitLineData
084c7007
RG
1/* SPDX-License-Identifier: LGPL-2.1+ */
2#include <linux/libbpf.h>
3
4#include "bpf-devices.h"
5#include "bpf-program.h"
6
7#define PASS_JUMP_OFF 4096
8
9static int bpf_access_type(const char *acc) {
10 int r = 0;
11
12 assert(acc);
13
14 for (; *acc; acc++)
15 switch(*acc) {
16 case 'r':
17 r |= BPF_DEVCG_ACC_READ;
18 break;
19 case 'w':
20 r |= BPF_DEVCG_ACC_WRITE;
21 break;
22 case 'm':
23 r |= BPF_DEVCG_ACC_MKNOD;
24 break;
25 default:
26 return -EINVAL;
27 }
28
29 return r;
30}
31
32int 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 */
41 };
42 int r, access;
43
44 assert(prog);
45 assert(acc);
46
47 access = bpf_access_type(acc);
48 if (access <= 0)
49 return -EINVAL;
50
51 insn[2].imm = access;
52
53 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
54 if (r < 0)
55 log_error_errno(r, "Extending device control BPF program failed: %m");
56
57 return r;
58}
59
60int 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 */
68 };
69 int r, access;
70
71 assert(prog);
72 assert(acc);
73
74 access = bpf_access_type(acc);
75 if (access <= 0)
76 return -EINVAL;
77
78 insn[2].imm = access;
79
80 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
81 if (r < 0)
82 log_error_errno(r, "Extending device control BPF program failed: %m");
8e8b5d2e
LP
83
84 return r;
85}
86
87int 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 */
94 };
95 int r, access;
96
97 assert(prog);
98 assert(acc);
99
100 access = bpf_access_type(acc);
101 if (access <= 0)
102 return -EINVAL;
103
104 insn[2].imm = access;
105
106 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
107 if (r < 0)
108 log_error_errno(r, "Extending device control BPF program failed: %m");
084c7007
RG
109
110 return r;
111}
112
113int 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)),
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 _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
134 int r;
135
136 assert(ret);
137
138 if (policy == CGROUP_AUTO && !whitelist)
139 return 0;
140
141 r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
142 if (r < 0)
143 return log_error_errno(r, "Loading device control BPF program failed: %m");
144
145 if (policy == CGROUP_CLOSED || whitelist) {
146 r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn));
147 if (r < 0)
148 return log_error_errno(r, "Extending device control BPF program failed: %m");
149 }
150
151 *ret = TAKE_PTR(prog);
152
153 return 0;
154}
155
156int cgroup_apply_device_bpf(Unit *u, BPFProgram *prog, CGroupDevicePolicy policy, bool whitelist) {
157 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 struct bpf_insn exit_insn[] = {
165 /* else return ALLOW */
166 BPF_MOV64_IMM(BPF_REG_0, 1),
167 BPF_EXIT_INSN()
168 };
169
170 _cleanup_free_ char *path = NULL;
084c7007
RG
171 int r;
172
173 if (!prog) {
174 /* Remove existing program. */
175 u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
176 return 0;
177 }
178
179 if (policy != CGROUP_STRICT || whitelist) {
180 size_t off;
181
182 r = bpf_program_add_instructions(prog, post_insn, ELEMENTSOF(post_insn));
183 if (r < 0)
184 return log_error_errno(r, "Extending device control BPF program failed: %m");
185
186 /* Fixup PASS_JUMP_OFF jump offsets. */
187 for (off = 0; off < prog->n_instructions; off++) {
188 struct bpf_insn *ins = &prog->instructions[off];
189
190 if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
191 ins->off = prog->n_instructions - off - 1;
192 }
193 } else
194 /* Explicitly forbid everything. */
195 exit_insn[0].imm = 0;
196
197 r = bpf_program_add_instructions(prog, exit_insn, ELEMENTSOF(exit_insn));
198 if (r < 0)
199 return log_error_errno(r, "Extending device control BPF program failed: %m");
200
201 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
202 if (r < 0)
203 return log_error_errno(r, "Failed to determine cgroup path: %m");
204
2af3eed1 205 r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, path, BPF_F_ALLOW_MULTI);
084c7007
RG
206 if (r < 0)
207 return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m", path);
208
2af3eed1
PH
209 /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
210 u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
211
084c7007
RG
212 /* Remember that this BPF program is installed now. */
213 u->bpf_device_control_installed = bpf_program_ref(prog);
214
215 return 0;
216}
217
218int bpf_devices_supported(void) {
219 struct bpf_insn trivial[] = {
220 BPF_MOV64_IMM(BPF_REG_0, 1),
221 BPF_EXIT_INSN()
222 };
223
224 _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 if (supported >= 0)
236 return supported;
237
238 if (geteuid() != 0) {
239 log_debug("Not enough privileges, BPF device control is not supported.");
240 return supported = 0;
241 }
242
243 r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
244 if (r < 0)
245 return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
246 if (r == 0) {
247 log_debug("Not running with unified cgroups, BPF device control is not supported.");
248 return supported = 0;
249 }
250
251 r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
252 if (r < 0) {
253 log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
254 return supported = 0;
255 }
256
257 r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial));
258 if (r < 0) {
259 log_debug_errno(r, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
260 return supported = 0;
261 }
262
263 r = bpf_program_load_kernel(program, NULL, 0);
264 if (r < 0) {
265 log_debug_errno(r, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
266 return supported = 0;
267 }
268
0b82cd25 269 return supported = 1;
084c7007 270}