Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/core/bpf-devices.c
Line
Count
Source (jump to first uncovered line)
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
0
#define PASS_JUMP_OFF 4096
8
9
0
static int bpf_access_type(const char *acc) {
10
0
        int r = 0;
11
0
12
0
        assert(acc);
13
0
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
0
                }
28
0
29
0
        return r;
30
0
}
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
0
                BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 6), /* compare device type */
35
0
                BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
36
0
                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
37
0
                BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 3), /* compare access type */
38
0
                BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 2), /* compare major */
39
0
                BPF_JMP_IMM(BPF_JNE, BPF_REG_5, minor, 1), /* compare minor */
40
0
                BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
41
0
        };
42
0
        int r, access;
43
0
44
0
        assert(prog);
45
0
        assert(acc);
46
0
47
0
        access = bpf_access_type(acc);
48
0
        if (access <= 0)
49
0
                return -EINVAL;
50
0
51
0
        insn[2].imm = access;
52
0
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
0
57
0
        return r;
58
0
}
59
60
0
int cgroup_bpf_whitelist_major(BPFProgram *prog, int type, int major, const char *acc) {
61
0
        struct bpf_insn insn[] = {
62
0
                BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
63
0
                BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
64
0
                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
65
0
                BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 2), /* compare access type */
66
0
                BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 1), /* compare major */
67
0
                BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
68
0
        };
69
0
        int r, access;
70
0
71
0
        assert(prog);
72
0
        assert(acc);
73
0
74
0
        access = bpf_access_type(acc);
75
0
        if (access <= 0)
76
0
                return -EINVAL;
77
0
78
0
        insn[2].imm = access;
79
0
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
0
84
0
        return r;
85
0
}
86
87
0
int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc) {
88
0
        struct bpf_insn insn[] = {
89
0
                BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
90
0
                BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
91
0
                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
92
0
                BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 1), /* compare access type */
93
0
                BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
94
0
        };
95
0
        int r, access;
96
0
97
0
        assert(prog);
98
0
        assert(acc);
99
0
100
0
        access = bpf_access_type(acc);
101
0
        if (access <= 0)
102
0
                return -EINVAL;
103
0
104
0
        insn[2].imm = access;
105
0
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
0
110
0
        return r;
111
0
}
112
113
0
int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist) {
114
0
        struct bpf_insn pre_insn[] = {
115
0
                /* load device type to r2 */
116
0
                BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
117
0
                            offsetof(struct bpf_cgroup_dev_ctx, access_type)),
118
0
119
0
                /* load access type to r3 */
120
0
                BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
121
0
                            offsetof(struct bpf_cgroup_dev_ctx, access_type)),
122
0
                BPF_ALU32_IMM(BPF_RSH, BPF_REG_3, 16),
123
0
124
0
                /* load major number to r4 */
125
0
                BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1,
126
0
                            offsetof(struct bpf_cgroup_dev_ctx, major)),
127
0
128
0
                /* load minor number to r5 */
129
0
                BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_1,
130
0
                            offsetof(struct bpf_cgroup_dev_ctx, minor)),
131
0
        };
132
0
133
0
        _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
134
0
        int r;
135
0
136
0
        assert(ret);
137
0
138
0
        if (policy == CGROUP_AUTO && !whitelist)
139
0
                return 0;
140
0
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
0
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
0
        }
150
0
151
0
        *ret = TAKE_PTR(prog);
152
0
153
0
        return 0;
154
0
}
155
156
0
int cgroup_apply_device_bpf(Unit *u, BPFProgram *prog, CGroupDevicePolicy policy, bool whitelist) {
157
0
        struct bpf_insn post_insn[] = {
158
0
                /* return DENY */
159
0
                BPF_MOV64_IMM(BPF_REG_0, 0),
160
0
                BPF_JMP_A(1),
161
0
162
0
        };
163
0
164
0
        struct bpf_insn exit_insn[] = {
165
0
                /* else return ALLOW */
166
0
                BPF_MOV64_IMM(BPF_REG_0, 1),
167
0
                BPF_EXIT_INSN()
168
0
        };
169
0
170
0
        _cleanup_free_ char *path = NULL;
171
0
        int r;
172
0
173
0
        if (!prog) {
174
0
                /* Remove existing program. */
175
0
                u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
176
0
                return 0;
177
0
        }
178
0
179
0
        if (policy != CGROUP_STRICT || whitelist) {
180
0
                size_t off;
181
0
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
0
186
0
                /* 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
0
190
0
                        if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
191
0
                                ins->off = prog->n_instructions - off - 1;
192
0
                }
193
0
        } else
194
0
                /* Explicitly forbid everything. */
195
0
                exit_insn[0].imm = 0;
196
0
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
0
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
0
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
0
209
0
        /* 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
0
212
0
        /* Remember that this BPF program is installed now. */
213
0
        u->bpf_device_control_installed = bpf_program_ref(prog);
214
0
215
0
        return 0;
216
0
}
217
218
0
int bpf_devices_supported(void) {
219
0
        struct bpf_insn trivial[] = {
220
0
                BPF_MOV64_IMM(BPF_REG_0, 1),
221
0
                BPF_EXIT_INSN()
222
0
        };
223
0
224
0
        _cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
225
0
        static int supported = -1;
226
0
        int r;
227
0
228
0
        /* Checks whether BPF device controller is supported. For this, we check five things:
229
0
         *
230
0
         * a) whether we are privileged
231
0
         * b) whether the unified hierarchy is being used
232
0
         * c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
233
0
         */
234
0
235
0
        if (supported >= 0)
236
0
                return supported;
237
0
238
0
        if (geteuid() != 0) {
239
0
                log_debug("Not enough privileges, BPF device control is not supported.");
240
0
                return supported = 0;
241
0
        }
242
0
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
0
        }
250
0
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
0
        }
256
0
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
0
        }
262
0
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
0
        }
268
0
269
0
        return supported = 1;
270
0
}