jail: add support for cgroup devices as in OCI run-time spec
authorDaniel Golle <daniel@makrotopia.org>
Fri, 28 May 2021 16:17:35 +0000 (18:17 +0200)
committerDaniel Golle <daniel@makrotopia.org>
Sat, 10 Jul 2021 02:26:04 +0000 (03:26 +0100)
Implement eBPF generator to emulate cgroup-v1 devices.{allow,deny}
as we got only cgroup-v2 available while the spec was written having
cgroups-v1 in mind.
Instead of literally emulating the legacy behavior, do like other
runtimes do as well when running on cgroup-v2: simply translate each
device rule into a bunch of eBPF instructions and then execute them
in reverse order, prepended by some default rules covering /dev/null,
/dev/random, /dev/tty, ...

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
CMakeLists.txt
jail/cgroups-bpf.c [new file with mode: 0644]
jail/cgroups-bpf.h [new file with mode: 0644]
jail/cgroups.c
jail/jail.c

index c0770b3b0baa102a4bf8781bbcc1500c6492a517..b7ea50267d29d216f35dd147dc766fef02bc9751 100644 (file)
@@ -112,7 +112,7 @@ SET(SOURCES_OCI_SECCOMP jail/seccomp-oci.c)
 ENDIF()
 
 IF(JAIL_SUPPORT)
-ADD_EXECUTABLE(ujail jail/jail.c jail/cgroups.c jail/elf.c jail/fs.c jail/capabilities.c ${SOURCES_OCI_SECCOMP})
+ADD_EXECUTABLE(ujail jail/jail.c jail/cgroups.c jail/cgroups-bpf.c jail/elf.c jail/fs.c jail/capabilities.c ${SOURCES_OCI_SECCOMP})
 TARGET_LINK_LIBRARIES(ujail ${ubox} ${ubus} ${blobmsg_json})
 INSTALL(TARGETS ujail
        RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
diff --git a/jail/cgroups-bpf.c b/jail/cgroups-bpf.c
new file mode 100644 (file)
index 0000000..b0079d9
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2021 Daniel Golle <daniel@makrotopia.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * somehow emulate devices.allow/devices.deny using eBPF
+ *
+ * OCI run-time spec defines the syntax for allowing/denying access
+ * to devices according to the definition of cgroup-v1 in the Kernel
+ * as described in Documentation/admin-guide/cgroup-v1.
+ */
+
+#include <assert.h>
+#include <linux/bpf.h>
+#include <sys/reg.h>
+#include <sys/syscall.h>
+
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/list.h>
+
+#include "cgroups.h"
+#include "cgroups-bpf.h"
+#include "log.h"
+
+static struct bpf_insn *program = NULL;
+static int bpf_total_insn = 0;
+static const char *license = "GPL";
+
+static int
+syscall_bpf (int cmd, union bpf_attr *attr, unsigned int size)
+{
+       return (int) syscall (__NR_bpf, cmd, attr, size);
+}
+
+/* from crun/src/libcrun/ebpf.c */
+#define BPF_ALU32_IMM(OP, DST, IMM) \
+       ((struct bpf_insn){ .code = BPF_ALU | BPF_OP (OP) | BPF_K, .dst_reg = DST, .src_reg = 0, .off = 0, .imm = IMM })
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
+       ((struct bpf_insn){                    \
+               .code = BPF_LDX | BPF_SIZE (SIZE) | BPF_MEM, .dst_reg = DST, .src_reg = SRC, .off = OFF, .imm = 0 })
+
+#define BPF_MOV64_REG(DST, SRC) \
+       ((struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_X, .dst_reg = DST, .src_reg = SRC, .off = 0, .imm = 0 })
+
+#define BPF_JMP_A(OFF) \
+       ((struct bpf_insn){ .code = BPF_JMP | BPF_JA, .dst_reg = 0, .src_reg = 0, .off = OFF, .imm = 0 })
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
+       ((struct bpf_insn){ .code = BPF_JMP | BPF_OP (OP) | BPF_K, .dst_reg = DST, .src_reg = 0, .off = OFF, .imm = IMM })
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF) \
+       ((struct bpf_insn){ .code = BPF_JMP | BPF_OP (OP) | BPF_X, .dst_reg = DST, .src_reg = SRC, .off = OFF, .imm = 0 })
+
+#define BPF_MOV64_IMM(DST, IMM) \
+       ((struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = DST, .src_reg = 0, .off = 0, .imm = IMM })
+
+#define BPF_MOV32_REG(DST, SRC) \
+       ((struct bpf_insn){ .code = BPF_ALU | BPF_MOV | BPF_X, .dst_reg = DST, .src_reg = SRC, .off = 0, .imm = 0 })
+
+#define BPF_EXIT_INSN() \
+       ((struct bpf_insn){ .code = BPF_JMP | BPF_EXIT, .dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0 })
+
+/* taken from systemd.  */
+static const struct bpf_insn pre_insn[] = {
+       /* type -> R2.  */
+       BPF_LDX_MEM (BPF_W, BPF_REG_2, BPF_REG_1, 0),
+       BPF_ALU32_IMM (BPF_AND, BPF_REG_2, 0xFFFF),
+       /* access -> R3.  */
+       BPF_LDX_MEM (BPF_W, BPF_REG_3, BPF_REG_1, 0),
+       BPF_ALU32_IMM (BPF_RSH, BPF_REG_3, 16),
+       /* major -> R4.  */
+       BPF_LDX_MEM (BPF_W, BPF_REG_4, BPF_REG_1, 4),
+       /* minor -> R5.  */
+       BPF_LDX_MEM (BPF_W, BPF_REG_5, BPF_REG_1, 8),
+};
+
+enum {
+       OCI_LINUX_CGROUPS_DEVICES_ALLOW,
+       OCI_LINUX_CGROUPS_DEVICES_TYPE,
+       OCI_LINUX_CGROUPS_DEVICES_MAJOR,
+       OCI_LINUX_CGROUPS_DEVICES_MINOR,
+       OCI_LINUX_CGROUPS_DEVICES_ACCESS,
+       __OCI_LINUX_CGROUPS_DEVICES_MAX,
+};
+
+static const struct blobmsg_policy oci_linux_cgroups_devices_policy[] = {
+       [OCI_LINUX_CGROUPS_DEVICES_ALLOW] = { "allow", BLOBMSG_TYPE_BOOL },
+       [OCI_LINUX_CGROUPS_DEVICES_TYPE] = { "type", BLOBMSG_TYPE_STRING },
+       [OCI_LINUX_CGROUPS_DEVICES_MAJOR] = { "major", BLOBMSG_CAST_INT64 },
+       [OCI_LINUX_CGROUPS_DEVICES_MINOR] = { "minor", BLOBMSG_CAST_INT64 },
+       [OCI_LINUX_CGROUPS_DEVICES_ACCESS] = { "access", BLOBMSG_TYPE_STRING },
+};
+
+/*
+ * cgroup-v1 devices got a (default) behaviour and a list of exceptions.
+ * define datatypes similar to the legacy kernel code.
+ */
+#define DEVCG_DEV_ALL (BPF_DEVCG_DEV_BLOCK | BPF_DEVCG_DEV_CHAR)
+#define DEVCG_ACC_ALL (BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD)
+
+enum devcg_behavior {
+       DEVCG_DEFAULT_NONE,
+       DEVCG_DEFAULT_ALLOW,
+       DEVCG_DEFAULT_DENY,
+};
+
+struct dev_exception_item {
+       uint32_t                major, minor;
+       short                   type;
+       short                   access;
+       struct list_head        list;
+       bool                    allow;
+};
+
+/*
+ * add a bunch of default rules
+ */
+static int add_default_exceptions(struct list_head *exceptions)
+{
+       int i, ret = 0;
+       struct dev_exception_item *cur;
+       /* from crun/src/libcrun/cgroup.c */
+       const struct dev_exception_item defrules[] = {
+               /* always allow mknod */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = ~0,  .minor = ~0,  .access = BPF_DEVCG_ACC_MKNOD },
+               { .allow = true, .type = BPF_DEVCG_DEV_BLOCK, .major = ~0,  .minor = ~0,  .access = BPF_DEVCG_ACC_MKNOD },
+               /* /dev/null */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 1,   .minor = 3,   .access = DEVCG_ACC_ALL },
+               /* /dev/random */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 1,   .minor = 8,   .access = DEVCG_ACC_ALL },
+               /* /dev/full */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 1,   .minor = 7,   .access = DEVCG_ACC_ALL },
+               /* /dev/tty */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 5,   .minor = 0,   .access = DEVCG_ACC_ALL },
+               /* /dev/zero */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 1,   .minor = 5,   .access = DEVCG_ACC_ALL },
+               /* /dev/urandom */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 1,   .minor = 9,   .access = DEVCG_ACC_ALL },
+               /* /dev/console */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 5,   .minor = 1,   .access = DEVCG_ACC_ALL },
+               /* /dev/pts/[0-255] */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 136, .minor = ~0,  .access = DEVCG_ACC_ALL },
+               /* /dev/ptmx */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 5,   .minor = 2,   .access = DEVCG_ACC_ALL },
+               /* /dev/net/tun */
+               { .allow = true, .type = BPF_DEVCG_DEV_CHAR,  .major = 10,  .minor = 200, .access = DEVCG_ACC_ALL },
+       };
+
+       for (i = 0; i < (sizeof(defrules) / sizeof(struct dev_exception_item)); ++i) {
+               cur = malloc(sizeof(struct dev_exception_item));
+               if (!cur) {
+                       ret = ENOMEM;
+                       break;
+               }
+               /* add defaults to list in reverse order (last item will be first in list) */
+               memcpy(cur, &defrules[i], sizeof(struct dev_exception_item));
+               list_add(&cur->list, exceptions);
+       }
+
+       return ret;
+}
+
+/*
+ * free all exceptions in the list
+ */
+static void flush_exceptions(struct list_head *freelist)
+{
+       struct dev_exception_item *dl, *dln;
+
+       if (!list_empty(freelist))
+               list_for_each_entry_safe(dl, dln, freelist, list) {
+                       list_del(&dl->list);
+                       free(dl);
+               }
+}
+
+/*
+ * parse OCI cgroups devices and translate into cgroups-v2 eBPF program
+ */
+int parseOCIlinuxcgroups_devices(struct blob_attr *msg)
+{
+       struct blob_attr *tb[__OCI_LINUX_CGROUPS_DEVICES_MAX];
+       struct blob_attr *cur;
+       int rem, ret = 0;
+       int bpf_type, bpf_access;
+       unsigned char acidx;
+       bool allow = false,
+            has_access = false,
+            has_type = false,
+            has_major = false,
+            has_minor = false;
+       int total_ins = 0,
+           cur_ins = 0,
+           pre_insn_len = sizeof(pre_insn) / sizeof(struct bpf_insn),
+           next_ins;
+       char *access, *devtype;
+       uint32_t devmajor, devminor;
+       struct dev_exception_item *dl;
+       struct list_head exceptions;
+       enum devcg_behavior behavior = DEVCG_DEFAULT_ALLOW;
+       INIT_LIST_HEAD(&exceptions);
+
+       /* parse according to OCI spec */
+       blobmsg_for_each_attr(cur, msg, rem) {
+               blobmsg_parse(oci_linux_cgroups_devices_policy, __OCI_LINUX_CGROUPS_DEVICES_MAX,
+                             tb, blobmsg_data(cur), blobmsg_len(cur));
+
+               if (!tb[OCI_LINUX_CGROUPS_DEVICES_ALLOW]) {
+                       ret = EINVAL;
+                       goto out;
+               }
+
+               allow = blobmsg_get_bool(tb[OCI_LINUX_CGROUPS_DEVICES_ALLOW]);
+
+               bpf_access = 0;
+               if (tb[OCI_LINUX_CGROUPS_DEVICES_ACCESS]) {
+                       access = blobmsg_get_string(tb[OCI_LINUX_CGROUPS_DEVICES_ACCESS]);
+                       if ((strlen(access) > 3) || (strlen(access) == 0)) {
+                               ret = EINVAL;
+                               goto out;
+                       }
+
+                       for (acidx = 0; acidx < strlen(access); ++acidx) {
+                               switch (access[acidx]) {
+                                       case 'r':
+                                               bpf_access |= BPF_DEVCG_ACC_READ;
+                                               break;
+                                       case 'w':
+                                               bpf_access |= BPF_DEVCG_ACC_WRITE;
+                                               break;
+                                       case 'm':
+                                               bpf_access |= BPF_DEVCG_ACC_MKNOD;
+                                               break;
+                                       default:
+                                               ret = EINVAL;
+                                               goto out;
+                               }
+                       }
+               }
+
+               if (!bpf_access)
+                       bpf_access = DEVCG_ACC_ALL;
+
+               bpf_type = 0;
+               if (tb[OCI_LINUX_CGROUPS_DEVICES_TYPE]) {
+                       devtype = blobmsg_get_string(tb[OCI_LINUX_CGROUPS_DEVICES_TYPE]);
+
+                       switch (devtype[0]) {
+                               case 'c':
+                                       bpf_type = BPF_DEVCG_DEV_CHAR;
+                                       break;
+                               case 'b':
+                                       bpf_type = BPF_DEVCG_DEV_BLOCK;
+                                       break;
+                               case 'a':
+                                       bpf_type = DEVCG_DEV_ALL;
+                                       break;
+                               default:
+                                       ret = EINVAL;
+                                       goto out;
+                       }
+               }
+
+               if (!bpf_type)
+                       bpf_type = DEVCG_DEV_ALL;
+
+               if (tb[OCI_LINUX_CGROUPS_DEVICES_MAJOR])
+                       devmajor = blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_DEVICES_MAJOR]);
+               else
+                       devmajor = ~0;
+
+               if (tb[OCI_LINUX_CGROUPS_DEVICES_MINOR])
+                       devminor = blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_DEVICES_MINOR]);
+               else
+                       devminor = ~0;
+
+               if (bpf_type == DEVCG_DEV_ALL) {
+                       /* wildcard => change default policy and flush all existing rules */
+                       flush_exceptions(&exceptions);
+                       behavior = allow?DEVCG_DEFAULT_ALLOW:DEVCG_DEFAULT_DENY;
+               } else {
+                       /* allocate and populate record for exception */
+                       dl = malloc(sizeof(struct dev_exception_item));
+                       if (!dl) {
+                               ret = ENOSPC;
+                               break;
+                       }
+                       dl->allow = allow;
+                       dl->type = bpf_type;
+                       dl->access = bpf_access;
+                       dl->major = devmajor;
+                       dl->minor = devminor;
+
+                       /* push to exceptions list, last goes first */
+                       list_add(&dl->list, &exceptions);
+               }
+       }
+       if (ret)
+               goto out;
+
+       /* add default rules */
+       ret = add_default_exceptions(&exceptions);
+       if (ret)
+               goto out;
+
+       /* calculate number of instructions to allocate */
+       list_for_each_entry(dl, &exceptions, list) {
+               has_access = dl->access != DEVCG_ACC_ALL;
+               has_type = dl->type != DEVCG_DEV_ALL;
+               has_major = dl->major != ~0;
+               has_minor = dl->minor != ~0;
+
+               total_ins += (has_type ? 1 : 0) + (has_access ? 3 : 0) + (has_major ? 1 : 0) + (has_minor ? 1 : 0) + 2;
+       }
+
+       /* acccount for loader instructions */
+       total_ins += pre_insn_len;
+
+       /* final accept/deny block */
+       total_ins += 2;
+
+       /* allocate memory for eBPF program */
+       program = calloc(total_ins, sizeof(struct bpf_insn));
+       if (!program) {
+               ret = ENOMEM;
+               goto out;
+       }
+
+       /* copy program loader instructions */
+       memcpy(program, &pre_insn, sizeof(pre_insn));
+       cur_ins = pre_insn_len;
+
+       /* generate eBPF program */
+       list_for_each_entry(dl, &exceptions, list) {
+               has_access = dl->access != DEVCG_ACC_ALL;
+               has_type = dl->type != DEVCG_DEV_ALL;
+               has_major = dl->major != ~0;
+               has_minor = dl->minor != ~0;
+
+               next_ins = (has_type ? 1 : 0) + (has_access ? 3 : 0) + (has_major ? 1 : 0) + (has_minor ? 1 : 0) + 1;
+
+               if (has_type) {
+                       program[cur_ins++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_2, dl->type, next_ins);
+                       --next_ins;
+               }
+
+               if (has_access) {
+                       program[cur_ins++] = BPF_MOV32_REG(BPF_REG_1, BPF_REG_3);
+                       program[cur_ins++] = BPF_ALU32_IMM(BPF_AND, BPF_REG_1, dl->access);
+                       program[cur_ins++] = BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, next_ins - 2);
+                       next_ins -= 3;
+               }
+
+               if (has_major) {
+                       program[cur_ins++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_4, dl->major, next_ins);
+                       --next_ins;
+               }
+
+               if (has_minor) {
+                       program[cur_ins++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_5, dl->minor, next_ins);
+                       --next_ins;
+               }
+
+               program[cur_ins++] = BPF_MOV64_IMM(BPF_REG_0, dl->allow ? 1 : 0);
+               program[cur_ins++] = BPF_EXIT_INSN();
+       }
+
+       /* default behavior */
+       program[cur_ins++] = BPF_MOV64_IMM(BPF_REG_0, (behavior == DEVCG_DEFAULT_ALLOW)?1:0);
+       program[cur_ins++] = BPF_EXIT_INSN();
+
+       if (debug) {
+               fprintf(stderr, "cgroup devices:\na > devices.%s\n",
+                       (behavior == DEVCG_DEFAULT_ALLOW)?"allow":"deny");
+
+               list_for_each_entry(dl, &exceptions, list)
+                       fprintf(stderr, "%c %d:%d %s%s%s > devices.%s\n",
+                               (dl->type == DEVCG_DEV_ALL)?'a':
+                                       (dl->type == BPF_DEVCG_DEV_CHAR)?'c':'b',
+                               (dl->major == ~0)?-1:dl->major,
+                               (dl->minor == ~0)?-1:dl->minor,
+                               (dl->access & BPF_DEVCG_ACC_READ)?"r":"",
+                               (dl->access & BPF_DEVCG_ACC_WRITE)?"w":"",
+                               (dl->access & BPF_DEVCG_ACC_MKNOD)?"m":"",
+                               (dl->allow)?"allow":"deny");
+
+               fprintf(stderr, "generated cgroup-devices eBPF program:\n");
+               fprintf(stderr, " [idx]\tcode\t dest\t src\t off\t imm\n");
+               for (cur_ins=0; cur_ins<total_ins; cur_ins++)
+                       fprintf(stderr, " [%03d]\t%02hhx\t%3hhu\t%3hhu\t%04hx\t%d\n", cur_ins,
+                               program[cur_ins].code,
+                               program[cur_ins].dst_reg,
+                               program[cur_ins].src_reg,
+                               program[cur_ins].off,
+                               program[cur_ins].imm);
+       }
+
+       assert(cur_ins == total_ins);
+       bpf_total_insn = total_ins;
+       ret = 0;
+
+out:
+       flush_exceptions(&exceptions);
+       return ret;
+}
+
+/*
+ * attach eBPF program to cgroup
+ */
+int attach_cgroups_ebpf(int cgroup_dirfd) {
+       int prog_fd;
+#if ( __WORDSIZE == 64 )
+       uint64_t program_ptr = (uint64_t)program;
+       uint64_t license_ptr = (uint64_t)license;
+#elif ( __WORDSIZE == 32 )
+       uint32_t program_ptr = (uint32_t)program;
+       uint32_t license_ptr = (uint32_t)license;
+#else
+#error
+#endif
+       union bpf_attr load_attr = {
+               .prog_type = BPF_PROG_TYPE_CGROUP_DEVICE,
+               .license   = license_ptr,
+               .insns     = program_ptr,
+               .insn_cnt  = bpf_total_insn,
+       };
+
+       if (!program)
+               return 0;
+
+       prog_fd = syscall_bpf(BPF_PROG_LOAD, &load_attr, sizeof(load_attr));
+       if (prog_fd < 0)
+               return EIO;
+
+       union bpf_attr attach_attr = {
+               .attach_type = BPF_CGROUP_DEVICE,
+               .target_fd = cgroup_dirfd,
+               .attach_bpf_fd = prog_fd,
+       };
+
+       return syscall_bpf(BPF_PROG_ATTACH, &attach_attr, sizeof (attach_attr));
+}
diff --git a/jail/cgroups-bpf.h b/jail/cgroups-bpf.h
new file mode 100644 (file)
index 0000000..7f33d85
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 Daniel Golle <daniel@makrotopia.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _JAIL_CGROUPS_BPF_H
+#define _JAIL_CGROUPS_BPF_H
+
+int parseOCIlinuxcgroups_devices(struct blob_attr *msg);
+int attach_cgroups_ebpf(int cgroup_dirfd);
+
+#endif
index 68bd189723ae32686555bcce2a9c87180f7a3382..185d18fd97f7c63f226f47813316a32058ab4416 100644 (file)
@@ -16,7 +16,6 @@
  * https://github.com/containers/crun/blob/0.14.1/crun.1.md#cgroup-v2
  *
  * ToDo:
- *  - convert cgroup1 devices to eBPF program
  *  - convert cgroup1 net_prio and net_cls to eBPF program
  *  - rdma (anyone?) intelrdt (anyone?)
  */
@@ -43,6 +42,7 @@
 
 #include "log.h"
 #include "cgroups.h"
+#include "cgroups-bpf.h"
 
 #define CGROUP_ROOT "/sys/fs/cgroup/"
 #define CGROUP_IO_WEIGHT_MAX 10000
@@ -197,11 +197,22 @@ void cgroups_apply(pid_t pid)
                close(fd);
        }
 
+       int dirfd = open(cgroup_path, O_DIRECTORY);
+       if (dirfd < 0) {
+               ERROR("can't open %s: %m\n", cgroup_path);
+       } else {
+               attach_cgroups_ebpf(dirfd);
+               close(dirfd);
+       }
+
        snprintf(ent, maxlen, "%s/%s", cgroup_path, "cgroup.procs");
        fd = open(ent, O_WRONLY);
-       assert(fd != -1);
-       dprintf(fd, "%d", pid);
-       close(fd);
+       if (fd < 0) {
+               ERROR("can't open %s: %m\n", cgroup_path);
+       } else {
+               dprintf(fd, "%d", pid);
+               close(fd);
+       }
 
        free(ent);
 }
@@ -349,7 +360,8 @@ static int parseOCIlinuxcgroups_legacy_blockio(struct blob_attr *msg)
        numweightstrs = 0;
 
        if (weight > -1)
-               asprintf(&weightstrs[numweightstrs++], "default %d", weight);
+               if (asprintf(&weightstrs[numweightstrs++], "default %d", weight) < 0)
+                       return ENOMEM;
 
        blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE], rem) {
                uint64_t major, minor;
@@ -382,7 +394,8 @@ static int parseOCIlinuxcgroups_legacy_blockio(struct blob_attr *msg)
                major = blobmsg_cast_u64(tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAJOR]);
                minor = blobmsg_cast_u64(tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MINOR]);
 
-               asprintf(&weightstrs[numweightstrs++], "%" PRIu64 ":%" PRIu64 " %u", major, minor, devweight);
+               if (asprintf(&weightstrs[numweightstrs++], "%" PRIu64 ":%" PRIu64 " %u", major, minor, devweight) < 0)
+                       return ENOMEM;
        }
 
        if (numweightstrs) {
@@ -785,8 +798,7 @@ int parseOCIlinuxcgroups(struct blob_attr *msg)
 
        blobmsg_parse(oci_linux_cgroups_policy, __OCI_LINUX_CGROUPS_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
 
-       if (tb[OCI_LINUX_CGROUPS_DEVICES] ||
-           tb[OCI_LINUX_CGROUPS_HUGEPAGELIMITS] ||
+       if (tb[OCI_LINUX_CGROUPS_HUGEPAGELIMITS] ||
            tb[OCI_LINUX_CGROUPS_INTELRDT] ||
            tb[OCI_LINUX_CGROUPS_NETWORK] ||
            tb[OCI_LINUX_CGROUPS_RDMA])
@@ -804,6 +816,12 @@ int parseOCIlinuxcgroups(struct blob_attr *msg)
                        return ret;
        }
 
+       if (tb[OCI_LINUX_CGROUPS_DEVICES]) {
+               ret = parseOCIlinuxcgroups_devices(tb[OCI_LINUX_CGROUPS_DEVICES]);
+               if (ret)
+                       return ret;
+       }
+
        if (tb[OCI_LINUX_CGROUPS_MEMORY]) {
                ret = parseOCIlinuxcgroups_legacy_memory(tb[OCI_LINUX_CGROUPS_MEMORY]);
                if (ret)
index ce4f50c64a06c516406fafbdc3d2b55fb6ded729..c350be280fdfe3d4d6ced2beeacd41aa68a1cd91 100644 (file)
@@ -514,8 +514,7 @@ static int apply_sysctl(const char *jail_root)
        if (!opts.sysctl)
                return 0;
 
-       asprintf(&procdir, "%s/proc", jail_root);
-       if (!procdir)
+       if (asprintf(&procdir, "%s/proc", jail_root) < 0)
                return ENOMEM;
 
        mkdir(procdir, 0700);
@@ -525,8 +524,7 @@ static int apply_sysctl(const char *jail_root)
        cur = opts.sysctl;
 
        while (*cur) {
-               asprintf(&fname, "%s/sys/%s", procdir, (*cur)->entry);
-               if (!fname)
+               if (asprintf(&fname, "%s/sys/%s", procdir, (*cur)->entry) < 0)
                        return ENOMEM;
 
                DEBUG("sysctl: writing '%s' to %s\n", (*cur)->value, fname);
@@ -2581,7 +2579,10 @@ int main(int argc, char **argv)
                        ret=-1;
                        goto errout;
                }
-               asprintf(&jsonfile, "%s/config.json", opts.ocibundle);
+               if (asprintf(&jsonfile, "%s/config.json", opts.ocibundle) < 0) {
+                       ret=-ENOMEM;
+                       goto errout;
+               }
                ocires = parseOCI(jsonfile);
                free(jsonfile);
                if (ocires) {