Skip to content

Commit 8e8b5d2

Browse files
committed
cgroups: beef up DeviceAllow= syntax a bit
Previously we'd allow pattern expressions such as "char-input" to match all input devices. Internally, this would look up the right major to test in /proc/devices. With this commit the syntax is slightly extended: - "char-*" can be used to match any kind of character device, and similar "block-*. This expression would work previously already, but instead of actually installing a wildcard match it would install many individual matches for everything listed in /proc/devices. - "char-<MAJOR>" with "<MAJOR>" being a numerical parameter works now too. This allows clients to install whitelist items by specifying the major directly. The main reason to add these is to provide limited compat support for clients that for some reason contain whitelists with major/minor numbers (such as OCI containers).
1 parent 74c48bf commit 8e8b5d2

File tree

3 files changed

+73
-5
lines changed

3 files changed

+73
-5
lines changed

src/core/bpf-devices.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,32 @@ int cgroup_bpf_whitelist_major(BPFProgram *prog, int type, int major, const char
8484
return r;
8585
}
8686

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 */
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");
109+
110+
return r;
111+
}
112+
87113
int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist) {
88114
struct bpf_insn pre_insn[] = {
89115
/* load device type to r2 */

src/core/bpf-devices.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ int bpf_devices_supported(void);
1111

1212
int cgroup_bpf_whitelist_device(BPFProgram *p, int type, int major, int minor, const char *acc);
1313
int cgroup_bpf_whitelist_major(BPFProgram *p, int type, int major, const char *acc);
14+
int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc);
1415

1516
int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist);
1617
int cgroup_apply_device_bpf(Unit *u, BPFProgram *p, CGroupDevicePolicy policy, bool whitelist);

src/core/cgroup.c

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -509,21 +509,64 @@ static int whitelist_device(BPFProgram *prog, const char *path, const char *node
509509

510510
static int whitelist_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc) {
511511
_cleanup_fclose_ FILE *f = NULL;
512-
char *p, *w;
512+
char buf[2+DECIMAL_STR_MAX(unsigned)+3+4];
513513
bool good = false;
514+
unsigned maj;
514515
int r;
515516

516517
assert(path);
517518
assert(acc);
518519
assert(IN_SET(type, 'b', 'c'));
519520

521+
if (streq(name, "*")) {
522+
/* If the name is a wildcard, then apply this list to all devices of this type */
523+
524+
if (cg_all_unified() > 0) {
525+
if (!prog)
526+
return 0;
527+
528+
(void) cgroup_bpf_whitelist_class(prog, type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK, acc);
529+
} else {
530+
xsprintf(buf, "%c *:* %s", type, acc);
531+
532+
r = cg_set_attribute("devices", path, "devices.allow", buf);
533+
if (r < 0)
534+
log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
535+
"Failed to set devices.allow on %s: %m", path);
536+
return 0;
537+
}
538+
}
539+
540+
if (safe_atou(name, &maj) >= 0 && DEVICE_MAJOR_VALID(maj)) {
541+
/* The name is numeric and suitable as major. In that case, let's take is major, and create the entry
542+
* directly */
543+
544+
if (cg_all_unified() > 0) {
545+
if (!prog)
546+
return 0;
547+
548+
(void) cgroup_bpf_whitelist_major(prog,
549+
type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
550+
maj, acc);
551+
} else {
552+
xsprintf(buf, "%c %u:* %s", type, maj, acc);
553+
554+
r = cg_set_attribute("devices", path, "devices.allow", buf);
555+
if (r < 0)
556+
log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
557+
"Failed to set devices.allow on %s: %m", path);
558+
}
559+
560+
return 0;
561+
}
562+
520563
f = fopen("/proc/devices", "re");
521564
if (!f)
522565
return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s (%c): %m", name, type);
523566

524567
for (;;) {
525568
_cleanup_free_ char *line = NULL;
526-
unsigned maj;
569+
char *w, *p;
527570

528571
r = read_line(f, LONG_LINE_MAX, &line);
529572
if (r < 0)
@@ -576,8 +619,6 @@ static int whitelist_major(BPFProgram *prog, const char *path, const char *name,
576619
type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
577620
maj, acc);
578621
} else {
579-
char buf[2+DECIMAL_STR_MAX(unsigned)+3+4];
580-
581622
sprintf(buf,
582623
"%c %u:* %s",
583624
type,
@@ -1179,7 +1220,7 @@ static void cgroup_context_apply(
11791220
else if ((val = startswith(a->path, "char-")))
11801221
(void) whitelist_major(prog, path, val, 'c', acc);
11811222
else
1182-
log_unit_debug(u, "Ignoring device %s while writing cgroup attribute.", a->path);
1223+
log_unit_debug(u, "Ignoring device '%s' while writing cgroup attribute.", a->path);
11831224
}
11841225

11851226
r = cgroup_apply_device_bpf(u, prog, c->device_policy, c->device_allow);

0 commit comments

Comments
 (0)