cgroups: make device cgroup handling smarter and simpler

parent fc4612cb
...@@ -2773,19 +2773,9 @@ static int device_cgroup_rule_parse(struct device_item *device, const char *key, ...@@ -2773,19 +2773,9 @@ static int device_cgroup_rule_parse(struct device_item *device, const char *key,
device->type = 'a'; device->type = 'a';
device->major = -1; device->major = -1;
device->minor = -1; device->minor = -1;
if (device->allow) /* allow all devices */
device->global_rule = LXC_BPF_DEVICE_CGROUP_DENYLIST;
else /* deny all devices */
device->global_rule = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
device->allow = -1;
return 0; return 0;
} }
/* local rule */
device->global_rule = LXC_BPF_DEVICE_CGROUP_LOCAL_RULE;
switch (*val) { switch (*val) {
case 'a': case 'a':
__fallthrough; __fallthrough;
...@@ -2968,7 +2958,6 @@ static int device_cgroup_rule_parse_devpath(struct device_item *device, ...@@ -2968,7 +2958,6 @@ static int device_cgroup_rule_parse_devpath(struct device_item *device,
device->major = MAJOR(sb.st_rdev); device->major = MAJOR(sb.st_rdev);
device->minor = MINOR(sb.st_rdev); device->minor = MINOR(sb.st_rdev);
device->allow = 1; device->allow = 1;
device->global_rule = LXC_BPF_DEVICE_CGROUP_LOCAL_RULE;
return 0; return 0;
} }
...@@ -3106,9 +3095,10 @@ static int bpf_device_cgroup_prepare(struct cgroup_ops *ops, ...@@ -3106,9 +3095,10 @@ static int bpf_device_cgroup_prepare(struct cgroup_ops *ops,
if (ret < 0) if (ret < 0)
return log_error_errno(-1, EINVAL, "Failed to parse device string %s=%s", key, val); return log_error_errno(-1, EINVAL, "Failed to parse device string %s=%s", key, val);
ret = bpf_list_add_device(&conf->devices, &device_item); ret = bpf_list_add_device(&conf->bpf_devices, &device_item);
if (ret < 0) if (ret < 0)
return -1; return -1;
return 0; return 0;
} }
...@@ -3180,10 +3170,11 @@ __cgfsng_ops static bool cgfsng_devices_activate(struct cgroup_ops *ops, struct ...@@ -3180,10 +3170,11 @@ __cgfsng_ops static bool cgfsng_devices_activate(struct cgroup_ops *ops, struct
unified = ops->unified; unified = ops->unified;
if (!unified || !unified->bpf_device_controller || if (!unified || !unified->bpf_device_controller ||
!unified->container_full_path || lxc_list_empty(&conf->devices)) !unified->container_full_path ||
lxc_list_empty(&(conf->bpf_devices).device_item))
return true; return true;
return bpf_cgroup_devices_attach(ops, &conf->devices); return bpf_cgroup_devices_attach(ops, &conf->bpf_devices);
} }
static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cgroup) static bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cgroup)
......
...@@ -211,12 +211,6 @@ int bpf_program_append_device(struct bpf_program *prog, struct device_item *devi ...@@ -211,12 +211,6 @@ int bpf_program_append_device(struct bpf_program *prog, struct device_item *devi
if (!prog || !device) if (!prog || !device)
return ret_set_errno(-1, EINVAL); return ret_set_errno(-1, EINVAL);
/* This is a global rule so no need to append anything. */
if (device->global_rule > LXC_BPF_DEVICE_CGROUP_LOCAL_RULE) {
prog->device_list_type = device->global_rule;
return 0;
}
ret = bpf_access_mask(device->access, &access_mask); ret = bpf_access_mask(device->access, &access_mask);
if (ret < 0) if (ret < 0)
return log_error_errno(ret, -ret, "Invalid access mask specified %s", device->access); return log_error_errno(ret, -ret, "Invalid access mask specified %s", device->access);
...@@ -296,10 +290,10 @@ int bpf_program_finalize(struct bpf_program *prog) ...@@ -296,10 +290,10 @@ int bpf_program_finalize(struct bpf_program *prog)
if (!prog) if (!prog)
return ret_set_errno(-1, EINVAL); return ret_set_errno(-1, EINVAL);
TRACE("Implementing %s bpf device cgroup program", TRACE("Device bpf program %s all devices by default",
prog->device_list_type == LXC_BPF_DEVICE_CGROUP_DENYLIST prog->device_list_type == LXC_BPF_DEVICE_CGROUP_ALLOWLIST
? "denylist" ? "blocks"
: "allowlist"); : "allows");
ins[0] = BPF_MOV64_IMM(BPF_REG_0, prog->device_list_type); ins[0] = BPF_MOV64_IMM(BPF_REG_0, prog->device_list_type);
ins[1] = BPF_EXIT_INSN(); ins[1] = BPF_EXIT_INSN();
...@@ -436,31 +430,35 @@ void bpf_device_program_free(struct cgroup_ops *ops) ...@@ -436,31 +430,35 @@ void bpf_device_program_free(struct cgroup_ops *ops)
} }
} }
int bpf_list_add_device(struct lxc_list *devices, struct device_item *device) int bpf_list_add_device(struct bpf_devices *bpf_devices,
struct device_item *device)
{ {
__do_free struct lxc_list *list_elem = NULL; __do_free struct lxc_list *list_elem = NULL;
__do_free struct device_item *new_device = NULL; __do_free struct device_item *new_device = NULL;
struct lxc_list *it; struct lxc_list *it;
if (!devices || !device) if (!bpf_devices || !device)
return ret_errno(EINVAL); return ret_errno(EINVAL);
lxc_list_for_each(it, devices) { /* Check whether this determines the list type. */
struct device_item *cur = it->elem; if (device->type == 'a' &&
device->major < 0 &&
if (cur->global_rule > LXC_BPF_DEVICE_CGROUP_LOCAL_RULE && device->minor < 0 &&
device->global_rule > LXC_BPF_DEVICE_CGROUP_LOCAL_RULE) { is_empty_string(device->access)) {
TRACE("Switched from %s to %s", if (device->allow) {
cur->global_rule == LXC_BPF_DEVICE_CGROUP_ALLOWLIST bpf_devices->list_type = LXC_BPF_DEVICE_CGROUP_DENYLIST;
? "allowlist" TRACE("Device cgroup will allow (\"denylist\") all devices by default");
: "denylist", } else {
device->global_rule == LXC_BPF_DEVICE_CGROUP_ALLOWLIST bpf_devices->list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
? "allowlist" TRACE("Device cgroup will deny (\"allowlist\") all devices by default");
: "denylist");
cur->global_rule = device->global_rule;
return 1;
} }
return 0;
}
lxc_list_for_each(it, &bpf_devices->device_item) {
struct device_item *cur = it->elem;
if (cur->type != device->type) if (cur->type != device->type)
continue; continue;
if (cur->major != device->major) if (cur->major != device->major)
...@@ -476,27 +474,26 @@ int bpf_list_add_device(struct lxc_list *devices, struct device_item *device) ...@@ -476,27 +474,26 @@ int bpf_list_add_device(struct lxc_list *devices, struct device_item *device)
*/ */
if (cur->allow != device->allow) { if (cur->allow != device->allow) {
cur->allow = device->allow; cur->allow = device->allow;
return log_trace(0, "Switched existing rule of bpf device program: type %c, major %d, minor %d, access %s, allow %d, global_rule %d", TRACE("Switched existing rule: type %c, major %d, minor %d, access %s, allow %d",
cur->type, cur->major, cur->minor, cur->type, cur->major, cur->minor, cur->access, cur->allow);
cur->access, cur->allow, } else {
cur->global_rule); TRACE("Reusing existing rule: type %c, major %d, minor %d, access %s, allow %d",
cur->type, cur->major, cur->minor, cur->access, cur->allow);
} }
return log_trace(1, "Reusing existing rule of bpf device program: type %c, major %d, minor %d, access %s, allow %d, global_rule %d", return 0;
cur->type, cur->major, cur->minor, cur->access,
cur->allow, cur->global_rule);
} }
list_elem = malloc(sizeof(*list_elem)); list_elem = malloc(sizeof(*list_elem));
if (!list_elem) if (!list_elem)
return log_error_errno(-1, ENOMEM, "Failed to allocate new device list"); return syserrno_set(ENOMEM, "Failed to allocate new device list");
new_device = memdup(device, sizeof(struct device_item)); new_device = memdup(device, sizeof(struct device_item));
if (!new_device) if (!new_device)
return log_error_errno(-1, ENOMEM, "Failed to allocate new device item"); return syserrno_set(ENOMEM, "Failed to allocate new device item");
lxc_list_add_elem(list_elem, move_ptr(new_device)); lxc_list_add_elem(list_elem, move_ptr(new_device));
lxc_list_add_tail(devices, move_ptr(list_elem)); lxc_list_add_tail(&bpf_devices->device_item, move_ptr(list_elem));
return 0; return 0;
} }
...@@ -533,7 +530,20 @@ bool bpf_devices_cgroup_supported(void) ...@@ -533,7 +530,20 @@ bool bpf_devices_cgroup_supported(void)
return log_trace(true, "The bpf device cgroup is supported"); return log_trace(true, "The bpf device cgroup is supported");
} }
static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices) static inline bool bpf_device_add(const struct bpf_program *prog,
struct device_item *device)
{
/* We're blocking all devices so skip individual deny rules. */
if (bpf_device_block_all(prog) && !device->allow)
return false;
/* We're allowing all devices so skip individual allow rules. */
if (!bpf_device_block_all(prog) && device->allow)
return false;
return true;
}
static struct bpf_program *__bpf_cgroup_devices(struct bpf_devices *bpf_devices)
{ {
__do_bpf_program_free struct bpf_program *prog = NULL; __do_bpf_program_free struct bpf_program *prog = NULL;
int ret; int ret;
...@@ -547,11 +557,11 @@ static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices) ...@@ -547,11 +557,11 @@ static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices)
if (ret) if (ret)
return syserrno(NULL, "Failed to initialize bpf program"); return syserrno(NULL, "Failed to initialize bpf program");
bpf_device_set_type(prog, devices); prog->device_list_type = bpf_devices->list_type;
TRACE("Device bpf %s all devices by default", TRACE("Device cgroup %s all devices by default",
bpf_device_block_all(prog) ? "blocks" : "allows"); bpf_device_block_all(prog) ? "blocks" : "allows");
lxc_list_for_each(it, devices) { lxc_list_for_each(it, &bpf_devices->device_item) {
struct device_item *cur = it->elem; struct device_item *cur = it->elem;
if (!bpf_device_add(prog, cur)) { if (!bpf_device_add(prog, cur)) {
...@@ -565,7 +575,7 @@ static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices) ...@@ -565,7 +575,7 @@ static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices)
return syserrno(NULL, "Failed adding rule: type %c, major %d, minor %d, access %s, allow %d", return syserrno(NULL, "Failed adding rule: type %c, major %d, minor %d, access %s, allow %d",
cur->type, cur->major, cur->minor, cur->access, cur->allow); cur->type, cur->major, cur->minor, cur->access, cur->allow);
TRACE("Added rule to bpf device program: type %c, major %d, minor %d, access %s, allow %d", TRACE("Added rule: type %c, major %d, minor %d, access %s, allow %d",
cur->type, cur->major, cur->minor, cur->access, cur->allow); cur->type, cur->major, cur->minor, cur->access, cur->allow);
} }
...@@ -576,12 +586,13 @@ static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices) ...@@ -576,12 +586,13 @@ static struct bpf_program *__bpf_cgroup_devices(struct lxc_list *devices)
return move_ptr(prog); return move_ptr(prog);
} }
bool bpf_cgroup_devices_attach(struct cgroup_ops *ops, struct lxc_list *devices) bool bpf_cgroup_devices_attach(struct cgroup_ops *ops,
struct bpf_devices *bpf_devices)
{ {
__do_bpf_program_free struct bpf_program *prog = NULL; __do_bpf_program_free struct bpf_program *prog = NULL;
int ret; int ret;
prog = __bpf_cgroup_devices(devices); prog = __bpf_cgroup_devices(bpf_devices);
if (!prog) if (!prog)
return syserrno(false, "Failed to create bpf program"); return syserrno(false, "Failed to create bpf program");
...@@ -597,8 +608,8 @@ bool bpf_cgroup_devices_attach(struct cgroup_ops *ops, struct lxc_list *devices) ...@@ -597,8 +608,8 @@ bool bpf_cgroup_devices_attach(struct cgroup_ops *ops, struct lxc_list *devices)
} }
bool bpf_cgroup_devices_update(struct cgroup_ops *ops, bool bpf_cgroup_devices_update(struct cgroup_ops *ops,
struct device_item *new, struct bpf_devices *bpf_devices,
struct lxc_list *devices) struct device_item *new)
{ {
__do_bpf_program_free struct bpf_program *prog = NULL; __do_bpf_program_free struct bpf_program *prog = NULL;
static int can_use_bpf_replace = -1; static int can_use_bpf_replace = -1;
...@@ -615,16 +626,16 @@ bool bpf_cgroup_devices_update(struct cgroup_ops *ops, ...@@ -615,16 +626,16 @@ bool bpf_cgroup_devices_update(struct cgroup_ops *ops,
if (ops->unified->cgfd_limit < 0) if (ops->unified->cgfd_limit < 0)
return ret_set_errno(false, EBADF); return ret_set_errno(false, EBADF);
ret = bpf_list_add_device(devices, new); ret = bpf_list_add_device(bpf_devices, new);
if (ret < 0) if (ret < 0)
return false; return false;
/* No previous device program attached. */ /* No previous device program attached. */
prog_old = ops->cgroup2_devices; prog_old = ops->cgroup2_devices;
if (!prog_old) if (!prog_old)
return bpf_cgroup_devices_attach(ops, devices); return bpf_cgroup_devices_attach(ops, bpf_devices);
prog = __bpf_cgroup_devices(devices); prog = __bpf_cgroup_devices(bpf_devices);
if (!prog) if (!prog)
return syserrno(false, "Failed to create bpf program"); return syserrno(false, "Failed to create bpf program");
......
...@@ -54,35 +54,6 @@ static inline bool bpf_device_block_all(const struct bpf_program *prog) ...@@ -54,35 +54,6 @@ static inline bool bpf_device_block_all(const struct bpf_program *prog)
return prog->device_list_type == LXC_BPF_DEVICE_CGROUP_ALLOWLIST; return prog->device_list_type == LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
} }
static inline bool bpf_device_add(const struct bpf_program *prog,
struct device_item *device)
{
if (device->global_rule > LXC_BPF_DEVICE_CGROUP_LOCAL_RULE)
return false;
/* We're blocking all devices so skip individual deny rules. */
if (bpf_device_block_all(prog) && !device->allow)
return false;
/* We're allowing all devices so skip individual allow rules. */
if (!bpf_device_block_all(prog) && device->allow)
return false;
return true;
}
static inline void bpf_device_set_type(struct bpf_program *prog,
struct lxc_list *devices)
{
struct lxc_list *it;
lxc_list_for_each (it, devices) {
struct device_item *cur = it->elem;
if (cur->global_rule > LXC_BPF_DEVICE_CGROUP_LOCAL_RULE)
prog->device_list_type = cur->global_rule;
}
}
__hidden extern struct bpf_program *bpf_program_new(__u32 prog_type); __hidden extern struct bpf_program *bpf_program_new(__u32 prog_type);
__hidden extern int bpf_program_init(struct bpf_program *prog); __hidden extern int bpf_program_init(struct bpf_program *prog);
__hidden extern int bpf_program_append_device(struct bpf_program *prog, struct device_item *device); __hidden extern int bpf_program_append_device(struct bpf_program *prog, struct device_item *device);
...@@ -91,13 +62,13 @@ __hidden extern int bpf_program_cgroup_detach(struct bpf_program *prog); ...@@ -91,13 +62,13 @@ __hidden extern int bpf_program_cgroup_detach(struct bpf_program *prog);
__hidden extern void bpf_device_program_free(struct cgroup_ops *ops); __hidden extern void bpf_device_program_free(struct cgroup_ops *ops);
__hidden extern bool bpf_devices_cgroup_supported(void); __hidden extern bool bpf_devices_cgroup_supported(void);
__hidden extern int bpf_list_add_device(struct lxc_list *devices, __hidden extern int bpf_list_add_device(struct bpf_devices *bpf_devices,
struct device_item *device); struct device_item *device);
__hidden extern bool bpf_cgroup_devices_attach(struct cgroup_ops *ops, __hidden extern bool bpf_cgroup_devices_attach(struct cgroup_ops *ops,
struct lxc_list *devices); struct bpf_devices *bpf_devices);
__hidden extern bool bpf_cgroup_devices_update(struct cgroup_ops *ops, __hidden extern bool bpf_cgroup_devices_update(struct cgroup_ops *ops,
struct device_item *new, struct bpf_devices *bpf_devices,
struct lxc_list *devices); struct device_item *device);
static inline void bpf_program_free(struct bpf_program *prog) static inline void bpf_program_free(struct bpf_program *prog)
{ {
......
...@@ -1195,7 +1195,6 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re ...@@ -1195,7 +1195,6 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re
{ {
int ret; int ret;
struct lxc_cmd_rsp rsp = {}; struct lxc_cmd_rsp rsp = {};
struct device_item *device;
struct lxc_conf *conf; struct lxc_conf *conf;
if (req->datalen <= 0) if (req->datalen <= 0)
...@@ -1207,9 +1206,10 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re ...@@ -1207,9 +1206,10 @@ static int lxc_cmd_add_bpf_device_cgroup_callback(int fd, struct lxc_cmd_req *re
if (!req->data) if (!req->data)
return LXC_CMD_REAP_CLIENT_FD; return LXC_CMD_REAP_CLIENT_FD;
device = (struct device_item *)req->data;
conf = handler->conf; conf = handler->conf;
if (!bpf_cgroup_devices_update(handler->cgroup_ops, device, &conf->devices)) if (!bpf_cgroup_devices_update(handler->cgroup_ops,
&conf->bpf_devices,
(struct device_item *)req->data))
rsp.ret = -1; rsp.ret = -1;
else else
rsp.ret = 0; rsp.ret = 0;
......
...@@ -2678,7 +2678,9 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -2678,7 +2678,9 @@ struct lxc_conf *lxc_conf_init(void)
new->logfd = -1; new->logfd = -1;
lxc_list_init(&new->cgroup); lxc_list_init(&new->cgroup);
lxc_list_init(&new->cgroup2); lxc_list_init(&new->cgroup2);
lxc_list_init(&new->devices); /* Block ("allowlist") all devices by default. */
new->bpf_devices.list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
lxc_list_init(&(new->bpf_devices).device_item);
lxc_list_init(&new->network); lxc_list_init(&new->network);
lxc_list_init(&new->mount_list); lxc_list_init(&new->mount_list);
lxc_list_init(&new->caps); lxc_list_init(&new->caps);
...@@ -3710,13 +3712,15 @@ int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version) ...@@ -3710,13 +3712,15 @@ int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version)
static void lxc_clear_devices(struct lxc_conf *conf) static void lxc_clear_devices(struct lxc_conf *conf)
{ {
struct lxc_list *list = &conf->devices; struct lxc_list *list = &(conf->bpf_devices).device_item;
struct lxc_list *it, *next; struct lxc_list *it, *next;
lxc_list_for_each_safe(it, list, next) { lxc_list_for_each_safe(it, list, next) {
lxc_list_del(it); lxc_list_del(it);
free(it); free(it);
} }
lxc_list_init(&(conf->bpf_devices).device_item);
} }
int lxc_clear_limits(struct lxc_conf *c, const char *key) int lxc_clear_limits(struct lxc_conf *c, const char *key)
......
...@@ -270,7 +270,6 @@ struct lxc_state_client { ...@@ -270,7 +270,6 @@ struct lxc_state_client {
}; };
typedef enum lxc_bpf_devices_rule_t { typedef enum lxc_bpf_devices_rule_t {
LXC_BPF_DEVICE_CGROUP_LOCAL_RULE = -1,
LXC_BPF_DEVICE_CGROUP_ALLOWLIST = 0, LXC_BPF_DEVICE_CGROUP_ALLOWLIST = 0,
LXC_BPF_DEVICE_CGROUP_DENYLIST = 1, LXC_BPF_DEVICE_CGROUP_DENYLIST = 1,
} lxc_bpf_devices_rule_t; } lxc_bpf_devices_rule_t;
...@@ -281,12 +280,11 @@ struct device_item { ...@@ -281,12 +280,11 @@ struct device_item {
int minor; int minor;
char access[4]; char access[4];
int allow; int allow;
/* };
* LXC_BPF_DEVICE_CGROUP_LOCAL_RULE -> no global rule
* LXC_BPF_DEVICE_CGROUP_ALLOWLIST -> allowlist (deny all) struct bpf_devices {
* LXC_BPF_DEVICE_CGROUP_DENYLIST -> denylist (allow all) lxc_bpf_devices_rule_t list_type;
*/ struct lxc_list device_item;
int global_rule;
}; };
struct timens_offsets { struct timens_offsets {
...@@ -310,8 +308,7 @@ struct lxc_conf { ...@@ -310,8 +308,7 @@ struct lxc_conf {
struct { struct {
struct lxc_list cgroup; struct lxc_list cgroup;
struct lxc_list cgroup2; struct lxc_list cgroup2;
/* This should be reimplemented as a hashmap. */ struct bpf_devices bpf_devices;
struct lxc_list devices;
}; };
struct { struct {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment