Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -29,4 +29,5 @@ obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
|
||||
endif
|
||||
ifeq ($(CONFIG_BPF_JIT),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
|
||||
obj-${CONFIG_BPF_LSM} += bpf_lsm.o
|
||||
endif
|
||||
|
54
kernel/bpf/bpf_lsm.c
Normal file
54
kernel/bpf/bpf_lsm.c
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/filter.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/bpf_lsm.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/bpf_verifier.h>
|
||||
|
||||
/* For every LSM hook that allows attachment of BPF programs, declare a nop
|
||||
* function where a BPF program can be attached.
|
||||
*/
|
||||
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
|
||||
noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
|
||||
{ \
|
||||
return DEFAULT; \
|
||||
}
|
||||
|
||||
#include <linux/lsm_hook_defs.h>
|
||||
#undef LSM_HOOK
|
||||
|
||||
#define BPF_LSM_SYM_PREFX "bpf_lsm_"
|
||||
|
||||
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
|
||||
const struct bpf_prog *prog)
|
||||
{
|
||||
if (!prog->gpl_compatible) {
|
||||
bpf_log(vlog,
|
||||
"LSM programs must have a GPL compatible license\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp(BPF_LSM_SYM_PREFX, prog->aux->attach_func_name,
|
||||
sizeof(BPF_LSM_SYM_PREFX) - 1)) {
|
||||
bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
|
||||
prog->aux->attach_btf_id, prog->aux->attach_func_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct bpf_prog_ops lsm_prog_ops = {
|
||||
};
|
||||
|
||||
const struct bpf_verifier_ops lsm_verifier_ops = {
|
||||
.get_func_proto = bpf_tracing_func_proto,
|
||||
.is_valid_access = btf_ctx_access,
|
||||
};
|
@@ -3477,8 +3477,8 @@ errout:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
extern char __weak _binary__btf_vmlinux_bin_start[];
|
||||
extern char __weak _binary__btf_vmlinux_bin_end[];
|
||||
extern char __weak __start_BTF[];
|
||||
extern char __weak __stop_BTF[];
|
||||
extern struct btf *btf_vmlinux;
|
||||
|
||||
#define BPF_MAP_TYPE(_id, _ops)
|
||||
@@ -3605,9 +3605,8 @@ struct btf *btf_parse_vmlinux(void)
|
||||
}
|
||||
env->btf = btf;
|
||||
|
||||
btf->data = _binary__btf_vmlinux_bin_start;
|
||||
btf->data_size = _binary__btf_vmlinux_bin_end -
|
||||
_binary__btf_vmlinux_bin_start;
|
||||
btf->data = __start_BTF;
|
||||
btf->data_size = __stop_BTF - __start_BTF;
|
||||
|
||||
err = btf_parse_hdr(env);
|
||||
if (err)
|
||||
@@ -3710,12 +3709,34 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||
nr_args--;
|
||||
}
|
||||
|
||||
if (arg > nr_args) {
|
||||
bpf_log(log, "func '%s' doesn't have %d-th argument\n",
|
||||
tname, arg + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg == nr_args) {
|
||||
if (prog->expected_attach_type == BPF_TRACE_FEXIT) {
|
||||
switch (prog->expected_attach_type) {
|
||||
case BPF_LSM_MAC:
|
||||
case BPF_TRACE_FEXIT:
|
||||
/* When LSM programs are attached to void LSM hooks
|
||||
* they use FEXIT trampolines and when attached to
|
||||
* int LSM hooks, they use MODIFY_RETURN trampolines.
|
||||
*
|
||||
* While the LSM programs are BPF_MODIFY_RETURN-like
|
||||
* the check:
|
||||
*
|
||||
* if (ret_type != 'int')
|
||||
* return -EINVAL;
|
||||
*
|
||||
* is _not_ done here. This is still safe as LSM hooks
|
||||
* have only void and int return types.
|
||||
*/
|
||||
if (!t)
|
||||
return true;
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
} else if (prog->expected_attach_type == BPF_MODIFY_RETURN) {
|
||||
break;
|
||||
case BPF_MODIFY_RETURN:
|
||||
/* For now the BPF_MODIFY_RETURN can only be attached to
|
||||
* functions that return an int.
|
||||
*/
|
||||
@@ -3729,17 +3750,19 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||
btf_kind_str[BTF_INFO_KIND(t->info)]);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bpf_log(log, "func '%s' doesn't have %d-th argument\n",
|
||||
tname, arg + 1);
|
||||
return false;
|
||||
}
|
||||
} else if (arg >= nr_args) {
|
||||
bpf_log(log, "func '%s' doesn't have %d-th argument\n",
|
||||
tname, arg + 1);
|
||||
return false;
|
||||
} else {
|
||||
if (!t)
|
||||
/* Default prog with 5 args */
|
||||
return true;
|
||||
t = btf_type_by_id(btf, args[arg].type);
|
||||
}
|
||||
|
||||
/* skip modifiers */
|
||||
while (btf_type_is_modifier(t))
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
|
@@ -28,6 +28,69 @@ void cgroup_bpf_offline(struct cgroup *cgrp)
|
||||
percpu_ref_kill(&cgrp->bpf.refcnt);
|
||||
}
|
||||
|
||||
static void bpf_cgroup_storages_free(struct bpf_cgroup_storage *storages[])
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
|
||||
for_each_cgroup_storage_type(stype)
|
||||
bpf_cgroup_storage_free(storages[stype]);
|
||||
}
|
||||
|
||||
static int bpf_cgroup_storages_alloc(struct bpf_cgroup_storage *storages[],
|
||||
struct bpf_prog *prog)
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
storages[stype] = bpf_cgroup_storage_alloc(prog, stype);
|
||||
if (IS_ERR(storages[stype])) {
|
||||
storages[stype] = NULL;
|
||||
bpf_cgroup_storages_free(storages);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_cgroup_storages_assign(struct bpf_cgroup_storage *dst[],
|
||||
struct bpf_cgroup_storage *src[])
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
|
||||
for_each_cgroup_storage_type(stype)
|
||||
dst[stype] = src[stype];
|
||||
}
|
||||
|
||||
static void bpf_cgroup_storages_link(struct bpf_cgroup_storage *storages[],
|
||||
struct cgroup* cgrp,
|
||||
enum bpf_attach_type attach_type)
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
|
||||
for_each_cgroup_storage_type(stype)
|
||||
bpf_cgroup_storage_link(storages[stype], cgrp, attach_type);
|
||||
}
|
||||
|
||||
static void bpf_cgroup_storages_unlink(struct bpf_cgroup_storage *storages[])
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
|
||||
for_each_cgroup_storage_type(stype)
|
||||
bpf_cgroup_storage_unlink(storages[stype]);
|
||||
}
|
||||
|
||||
/* Called when bpf_cgroup_link is auto-detached from dying cgroup.
|
||||
* It drops cgroup and bpf_prog refcounts, and marks bpf_link as defunct. It
|
||||
* doesn't free link memory, which will eventually be done by bpf_link's
|
||||
* release() callback, when its last FD is closed.
|
||||
*/
|
||||
static void bpf_cgroup_link_auto_detach(struct bpf_cgroup_link *link)
|
||||
{
|
||||
cgroup_put(link->cgroup);
|
||||
link->cgroup = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cgroup_bpf_release() - put references of all bpf programs and
|
||||
* release all cgroup bpf data
|
||||
@@ -37,7 +100,6 @@ static void cgroup_bpf_release(struct work_struct *work)
|
||||
{
|
||||
struct cgroup *p, *cgrp = container_of(work, struct cgroup,
|
||||
bpf.release_work);
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
struct bpf_prog_array *old_array;
|
||||
unsigned int type;
|
||||
|
||||
@@ -49,11 +111,12 @@ static void cgroup_bpf_release(struct work_struct *work)
|
||||
|
||||
list_for_each_entry_safe(pl, tmp, progs, node) {
|
||||
list_del(&pl->node);
|
||||
bpf_prog_put(pl->prog);
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
bpf_cgroup_storage_unlink(pl->storage[stype]);
|
||||
bpf_cgroup_storage_free(pl->storage[stype]);
|
||||
}
|
||||
if (pl->prog)
|
||||
bpf_prog_put(pl->prog);
|
||||
if (pl->link)
|
||||
bpf_cgroup_link_auto_detach(pl->link);
|
||||
bpf_cgroup_storages_unlink(pl->storage);
|
||||
bpf_cgroup_storages_free(pl->storage);
|
||||
kfree(pl);
|
||||
static_branch_dec(&cgroup_bpf_enabled_key);
|
||||
}
|
||||
@@ -85,6 +148,18 @@ static void cgroup_bpf_release_fn(struct percpu_ref *ref)
|
||||
queue_work(system_wq, &cgrp->bpf.release_work);
|
||||
}
|
||||
|
||||
/* Get underlying bpf_prog of bpf_prog_list entry, regardless if it's through
|
||||
* link or direct prog.
|
||||
*/
|
||||
static struct bpf_prog *prog_list_prog(struct bpf_prog_list *pl)
|
||||
{
|
||||
if (pl->prog)
|
||||
return pl->prog;
|
||||
if (pl->link)
|
||||
return pl->link->link.prog;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* count number of elements in the list.
|
||||
* it's slow but the list cannot be long
|
||||
*/
|
||||
@@ -94,7 +169,7 @@ static u32 prog_list_length(struct list_head *head)
|
||||
u32 cnt = 0;
|
||||
|
||||
list_for_each_entry(pl, head, node) {
|
||||
if (!pl->prog)
|
||||
if (!prog_list_prog(pl))
|
||||
continue;
|
||||
cnt++;
|
||||
}
|
||||
@@ -138,7 +213,7 @@ static int compute_effective_progs(struct cgroup *cgrp,
|
||||
enum bpf_attach_type type,
|
||||
struct bpf_prog_array **array)
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
struct bpf_prog_array_item *item;
|
||||
struct bpf_prog_array *progs;
|
||||
struct bpf_prog_list *pl;
|
||||
struct cgroup *p = cgrp;
|
||||
@@ -163,13 +238,13 @@ static int compute_effective_progs(struct cgroup *cgrp,
|
||||
continue;
|
||||
|
||||
list_for_each_entry(pl, &p->bpf.progs[type], node) {
|
||||
if (!pl->prog)
|
||||
if (!prog_list_prog(pl))
|
||||
continue;
|
||||
|
||||
progs->items[cnt].prog = pl->prog;
|
||||
for_each_cgroup_storage_type(stype)
|
||||
progs->items[cnt].cgroup_storage[stype] =
|
||||
pl->storage[stype];
|
||||
item = &progs->items[cnt];
|
||||
item->prog = prog_list_prog(pl);
|
||||
bpf_cgroup_storages_assign(item->cgroup_storage,
|
||||
pl->storage);
|
||||
cnt++;
|
||||
}
|
||||
} while ((p = cgroup_parent(p)));
|
||||
@@ -287,19 +362,60 @@ cleanup:
|
||||
|
||||
#define BPF_CGROUP_MAX_PROGS 64
|
||||
|
||||
static struct bpf_prog_list *find_attach_entry(struct list_head *progs,
|
||||
struct bpf_prog *prog,
|
||||
struct bpf_cgroup_link *link,
|
||||
struct bpf_prog *replace_prog,
|
||||
bool allow_multi)
|
||||
{
|
||||
struct bpf_prog_list *pl;
|
||||
|
||||
/* single-attach case */
|
||||
if (!allow_multi) {
|
||||
if (list_empty(progs))
|
||||
return NULL;
|
||||
return list_first_entry(progs, typeof(*pl), node);
|
||||
}
|
||||
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (prog && pl->prog == prog)
|
||||
/* disallow attaching the same prog twice */
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (link && pl->link == link)
|
||||
/* disallow attaching the same link twice */
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* direct prog multi-attach w/ replacement case */
|
||||
if (replace_prog) {
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (pl->prog == replace_prog)
|
||||
/* a match found */
|
||||
return pl;
|
||||
}
|
||||
/* prog to replace not found for cgroup */
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* __cgroup_bpf_attach() - Attach the program to a cgroup, and
|
||||
* __cgroup_bpf_attach() - Attach the program or the link to a cgroup, and
|
||||
* propagate the change to descendants
|
||||
* @cgrp: The cgroup which descendants to traverse
|
||||
* @prog: A program to attach
|
||||
* @link: A link to attach
|
||||
* @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set
|
||||
* @type: Type of attach operation
|
||||
* @flags: Option flags
|
||||
*
|
||||
* Exactly one of @prog or @link can be non-null.
|
||||
* Must be called with cgroup_mutex held.
|
||||
*/
|
||||
int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
struct bpf_prog *replace_prog,
|
||||
int __cgroup_bpf_attach(struct cgroup *cgrp,
|
||||
struct bpf_prog *prog, struct bpf_prog *replace_prog,
|
||||
struct bpf_cgroup_link *link,
|
||||
enum bpf_attach_type type, u32 flags)
|
||||
{
|
||||
u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI));
|
||||
@@ -307,14 +423,19 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
struct bpf_prog *old_prog = NULL;
|
||||
struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
|
||||
struct bpf_cgroup_storage *old_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
|
||||
struct bpf_prog_list *pl, *replace_pl = NULL;
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
struct bpf_prog_list *pl;
|
||||
int err;
|
||||
|
||||
if (((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) ||
|
||||
((flags & BPF_F_REPLACE) && !(flags & BPF_F_ALLOW_MULTI)))
|
||||
/* invalid combination */
|
||||
return -EINVAL;
|
||||
if (link && (prog || replace_prog))
|
||||
/* only either link or prog/replace_prog can be specified */
|
||||
return -EINVAL;
|
||||
if (!!replace_prog != !!(flags & BPF_F_REPLACE))
|
||||
/* replace_prog implies BPF_F_REPLACE, and vice versa */
|
||||
return -EINVAL;
|
||||
|
||||
if (!hierarchy_allows_attach(cgrp, type))
|
||||
return -EPERM;
|
||||
@@ -329,140 +450,203 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
if (prog_list_length(progs) >= BPF_CGROUP_MAX_PROGS)
|
||||
return -E2BIG;
|
||||
|
||||
if (flags & BPF_F_ALLOW_MULTI) {
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (pl->prog == prog)
|
||||
/* disallow attaching the same prog twice */
|
||||
return -EINVAL;
|
||||
if (pl->prog == replace_prog)
|
||||
replace_pl = pl;
|
||||
}
|
||||
if ((flags & BPF_F_REPLACE) && !replace_pl)
|
||||
/* prog to replace not found for cgroup */
|
||||
return -ENOENT;
|
||||
} else if (!list_empty(progs)) {
|
||||
replace_pl = list_first_entry(progs, typeof(*pl), node);
|
||||
}
|
||||
pl = find_attach_entry(progs, prog, link, replace_prog,
|
||||
flags & BPF_F_ALLOW_MULTI);
|
||||
if (IS_ERR(pl))
|
||||
return PTR_ERR(pl);
|
||||
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
storage[stype] = bpf_cgroup_storage_alloc(prog, stype);
|
||||
if (IS_ERR(storage[stype])) {
|
||||
storage[stype] = NULL;
|
||||
for_each_cgroup_storage_type(stype)
|
||||
bpf_cgroup_storage_free(storage[stype]);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (bpf_cgroup_storages_alloc(storage, prog ? : link->link.prog))
|
||||
return -ENOMEM;
|
||||
|
||||
if (replace_pl) {
|
||||
pl = replace_pl;
|
||||
if (pl) {
|
||||
old_prog = pl->prog;
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
old_storage[stype] = pl->storage[stype];
|
||||
bpf_cgroup_storage_unlink(old_storage[stype]);
|
||||
}
|
||||
bpf_cgroup_storages_unlink(pl->storage);
|
||||
bpf_cgroup_storages_assign(old_storage, pl->storage);
|
||||
} else {
|
||||
pl = kmalloc(sizeof(*pl), GFP_KERNEL);
|
||||
if (!pl) {
|
||||
for_each_cgroup_storage_type(stype)
|
||||
bpf_cgroup_storage_free(storage[stype]);
|
||||
bpf_cgroup_storages_free(storage);
|
||||
return -ENOMEM;
|
||||
}
|
||||
list_add_tail(&pl->node, progs);
|
||||
}
|
||||
|
||||
pl->prog = prog;
|
||||
for_each_cgroup_storage_type(stype)
|
||||
pl->storage[stype] = storage[stype];
|
||||
|
||||
pl->link = link;
|
||||
bpf_cgroup_storages_assign(pl->storage, storage);
|
||||
cgrp->bpf.flags[type] = saved_flags;
|
||||
|
||||
err = update_effective_progs(cgrp, type);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
static_branch_inc(&cgroup_bpf_enabled_key);
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
if (!old_storage[stype])
|
||||
continue;
|
||||
bpf_cgroup_storage_free(old_storage[stype]);
|
||||
}
|
||||
if (old_prog) {
|
||||
bpf_cgroup_storages_free(old_storage);
|
||||
if (old_prog)
|
||||
bpf_prog_put(old_prog);
|
||||
static_branch_dec(&cgroup_bpf_enabled_key);
|
||||
}
|
||||
for_each_cgroup_storage_type(stype)
|
||||
bpf_cgroup_storage_link(storage[stype], cgrp, type);
|
||||
else
|
||||
static_branch_inc(&cgroup_bpf_enabled_key);
|
||||
bpf_cgroup_storages_link(pl->storage, cgrp, type);
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
/* and cleanup the prog list */
|
||||
pl->prog = old_prog;
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
bpf_cgroup_storage_free(pl->storage[stype]);
|
||||
pl->storage[stype] = old_storage[stype];
|
||||
bpf_cgroup_storage_link(old_storage[stype], cgrp, type);
|
||||
if (old_prog) {
|
||||
pl->prog = old_prog;
|
||||
pl->link = NULL;
|
||||
}
|
||||
if (!replace_pl) {
|
||||
bpf_cgroup_storages_free(pl->storage);
|
||||
bpf_cgroup_storages_assign(pl->storage, old_storage);
|
||||
bpf_cgroup_storages_link(pl->storage, cgrp, type);
|
||||
if (!old_prog) {
|
||||
list_del(&pl->node);
|
||||
kfree(pl);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Swap updated BPF program for given link in effective program arrays across
|
||||
* all descendant cgroups. This function is guaranteed to succeed.
|
||||
*/
|
||||
static void replace_effective_prog(struct cgroup *cgrp,
|
||||
enum bpf_attach_type type,
|
||||
struct bpf_cgroup_link *link)
|
||||
{
|
||||
struct bpf_prog_array_item *item;
|
||||
struct cgroup_subsys_state *css;
|
||||
struct bpf_prog_array *progs;
|
||||
struct bpf_prog_list *pl;
|
||||
struct list_head *head;
|
||||
struct cgroup *cg;
|
||||
int pos;
|
||||
|
||||
css_for_each_descendant_pre(css, &cgrp->self) {
|
||||
struct cgroup *desc = container_of(css, struct cgroup, self);
|
||||
|
||||
if (percpu_ref_is_zero(&desc->bpf.refcnt))
|
||||
continue;
|
||||
|
||||
/* find position of link in effective progs array */
|
||||
for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) {
|
||||
if (pos && !(cg->bpf.flags[type] & BPF_F_ALLOW_MULTI))
|
||||
continue;
|
||||
|
||||
head = &cg->bpf.progs[type];
|
||||
list_for_each_entry(pl, head, node) {
|
||||
if (!prog_list_prog(pl))
|
||||
continue;
|
||||
if (pl->link == link)
|
||||
goto found;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
found:
|
||||
BUG_ON(!cg);
|
||||
progs = rcu_dereference_protected(
|
||||
desc->bpf.effective[type],
|
||||
lockdep_is_held(&cgroup_mutex));
|
||||
item = &progs->items[pos];
|
||||
WRITE_ONCE(item->prog, link->link.prog);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __cgroup_bpf_detach() - Detach the program from a cgroup, and
|
||||
* propagate the change to descendants
|
||||
* __cgroup_bpf_replace() - Replace link's program and propagate the change
|
||||
* to descendants
|
||||
* @cgrp: The cgroup which descendants to traverse
|
||||
* @prog: A program to detach or NULL
|
||||
* @type: Type of detach operation
|
||||
* @link: A link for which to replace BPF program
|
||||
* @type: Type of attach operation
|
||||
*
|
||||
* Must be called with cgroup_mutex held.
|
||||
*/
|
||||
int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
enum bpf_attach_type type)
|
||||
int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
|
||||
struct bpf_prog *new_prog)
|
||||
{
|
||||
struct list_head *progs = &cgrp->bpf.progs[type];
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
u32 flags = cgrp->bpf.flags[type];
|
||||
struct bpf_prog *old_prog = NULL;
|
||||
struct list_head *progs = &cgrp->bpf.progs[link->type];
|
||||
struct bpf_prog *old_prog;
|
||||
struct bpf_prog_list *pl;
|
||||
int err;
|
||||
bool found = false;
|
||||
|
||||
if (flags & BPF_F_ALLOW_MULTI) {
|
||||
if (!prog)
|
||||
/* to detach MULTI prog the user has to specify valid FD
|
||||
* of the program to be detached
|
||||
*/
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (list_empty(progs))
|
||||
/* report error when trying to detach and nothing is attached */
|
||||
return -ENOENT;
|
||||
}
|
||||
if (link->link.prog->type != new_prog->type)
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & BPF_F_ALLOW_MULTI) {
|
||||
/* find the prog and detach it */
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (pl->prog != prog)
|
||||
continue;
|
||||
old_prog = prog;
|
||||
/* mark it deleted, so it's ignored while
|
||||
* recomputing effective
|
||||
*/
|
||||
pl->prog = NULL;
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (pl->link == link) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!old_prog)
|
||||
return -ENOENT;
|
||||
} else {
|
||||
/* to maintain backward compatibility NONE and OVERRIDE cgroups
|
||||
* allow detaching with invalid FD (prog==NULL)
|
||||
*/
|
||||
pl = list_first_entry(progs, typeof(*pl), node);
|
||||
old_prog = pl->prog;
|
||||
pl->prog = NULL;
|
||||
}
|
||||
if (!found)
|
||||
return -ENOENT;
|
||||
|
||||
old_prog = xchg(&link->link.prog, new_prog);
|
||||
replace_effective_prog(cgrp, link->type, link);
|
||||
bpf_prog_put(old_prog);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bpf_prog_list *find_detach_entry(struct list_head *progs,
|
||||
struct bpf_prog *prog,
|
||||
struct bpf_cgroup_link *link,
|
||||
bool allow_multi)
|
||||
{
|
||||
struct bpf_prog_list *pl;
|
||||
|
||||
if (!allow_multi) {
|
||||
if (list_empty(progs))
|
||||
/* report error when trying to detach and nothing is attached */
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* to maintain backward compatibility NONE and OVERRIDE cgroups
|
||||
* allow detaching with invalid FD (prog==NULL) in legacy mode
|
||||
*/
|
||||
return list_first_entry(progs, typeof(*pl), node);
|
||||
}
|
||||
|
||||
if (!prog && !link)
|
||||
/* to detach MULTI prog the user has to specify valid FD
|
||||
* of the program or link to be detached
|
||||
*/
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* find the prog or link and detach it */
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (pl->prog == prog && pl->link == link)
|
||||
return pl;
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* __cgroup_bpf_detach() - Detach the program or link from a cgroup, and
|
||||
* propagate the change to descendants
|
||||
* @cgrp: The cgroup which descendants to traverse
|
||||
* @prog: A program to detach or NULL
|
||||
* @prog: A link to detach or NULL
|
||||
* @type: Type of detach operation
|
||||
*
|
||||
* At most one of @prog or @link can be non-NULL.
|
||||
* Must be called with cgroup_mutex held.
|
||||
*/
|
||||
int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
struct bpf_cgroup_link *link, enum bpf_attach_type type)
|
||||
{
|
||||
struct list_head *progs = &cgrp->bpf.progs[type];
|
||||
u32 flags = cgrp->bpf.flags[type];
|
||||
struct bpf_prog_list *pl;
|
||||
struct bpf_prog *old_prog;
|
||||
int err;
|
||||
|
||||
if (prog && link)
|
||||
/* only one of prog or link can be specified */
|
||||
return -EINVAL;
|
||||
|
||||
pl = find_detach_entry(progs, prog, link, flags & BPF_F_ALLOW_MULTI);
|
||||
if (IS_ERR(pl))
|
||||
return PTR_ERR(pl);
|
||||
|
||||
/* mark it deleted, so it's ignored while recomputing effective */
|
||||
old_prog = pl->prog;
|
||||
pl->prog = NULL;
|
||||
pl->link = NULL;
|
||||
|
||||
err = update_effective_progs(cgrp, type);
|
||||
if (err)
|
||||
@@ -470,22 +654,21 @@ int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
|
||||
/* now can actually delete it from this cgroup list */
|
||||
list_del(&pl->node);
|
||||
for_each_cgroup_storage_type(stype) {
|
||||
bpf_cgroup_storage_unlink(pl->storage[stype]);
|
||||
bpf_cgroup_storage_free(pl->storage[stype]);
|
||||
}
|
||||
bpf_cgroup_storages_unlink(pl->storage);
|
||||
bpf_cgroup_storages_free(pl->storage);
|
||||
kfree(pl);
|
||||
if (list_empty(progs))
|
||||
/* last program was detached, reset flags to zero */
|
||||
cgrp->bpf.flags[type] = 0;
|
||||
|
||||
bpf_prog_put(old_prog);
|
||||
if (old_prog)
|
||||
bpf_prog_put(old_prog);
|
||||
static_branch_dec(&cgroup_bpf_enabled_key);
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
/* and restore back old_prog */
|
||||
/* restore back prog or link */
|
||||
pl->prog = old_prog;
|
||||
pl->link = link;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -498,6 +681,7 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
struct list_head *progs = &cgrp->bpf.progs[type];
|
||||
u32 flags = cgrp->bpf.flags[type];
|
||||
struct bpf_prog_array *effective;
|
||||
struct bpf_prog *prog;
|
||||
int cnt, ret = 0, i;
|
||||
|
||||
effective = rcu_dereference_protected(cgrp->bpf.effective[type],
|
||||
@@ -528,7 +712,8 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
id = pl->prog->aux->id;
|
||||
prog = prog_list_prog(pl);
|
||||
id = prog->aux->id;
|
||||
if (copy_to_user(prog_ids + i, &id, sizeof(id)))
|
||||
return -EFAULT;
|
||||
if (++i == cnt)
|
||||
@@ -558,8 +743,8 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr,
|
||||
}
|
||||
}
|
||||
|
||||
ret = cgroup_bpf_attach(cgrp, prog, replace_prog, attr->attach_type,
|
||||
attr->attach_flags);
|
||||
ret = cgroup_bpf_attach(cgrp, prog, replace_prog, NULL,
|
||||
attr->attach_type, attr->attach_flags);
|
||||
|
||||
if (replace_prog)
|
||||
bpf_prog_put(replace_prog);
|
||||
@@ -581,7 +766,7 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
|
||||
if (IS_ERR(prog))
|
||||
prog = NULL;
|
||||
|
||||
ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type, 0);
|
||||
ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type);
|
||||
if (prog)
|
||||
bpf_prog_put(prog);
|
||||
|
||||
@@ -589,6 +774,90 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bpf_cgroup_link_release(struct bpf_link *link)
|
||||
{
|
||||
struct bpf_cgroup_link *cg_link =
|
||||
container_of(link, struct bpf_cgroup_link, link);
|
||||
|
||||
/* link might have been auto-detached by dying cgroup already,
|
||||
* in that case our work is done here
|
||||
*/
|
||||
if (!cg_link->cgroup)
|
||||
return;
|
||||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
|
||||
/* re-check cgroup under lock again */
|
||||
if (!cg_link->cgroup) {
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link,
|
||||
cg_link->type));
|
||||
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
cgroup_put(cg_link->cgroup);
|
||||
}
|
||||
|
||||
static void bpf_cgroup_link_dealloc(struct bpf_link *link)
|
||||
{
|
||||
struct bpf_cgroup_link *cg_link =
|
||||
container_of(link, struct bpf_cgroup_link, link);
|
||||
|
||||
kfree(cg_link);
|
||||
}
|
||||
|
||||
const struct bpf_link_ops bpf_cgroup_link_lops = {
|
||||
.release = bpf_cgroup_link_release,
|
||||
.dealloc = bpf_cgroup_link_dealloc,
|
||||
};
|
||||
|
||||
int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_cgroup_link *link;
|
||||
struct file *link_file;
|
||||
struct cgroup *cgrp;
|
||||
int err, link_fd;
|
||||
|
||||
if (attr->link_create.flags)
|
||||
return -EINVAL;
|
||||
|
||||
cgrp = cgroup_get_from_fd(attr->link_create.target_fd);
|
||||
if (IS_ERR(cgrp))
|
||||
return PTR_ERR(cgrp);
|
||||
|
||||
link = kzalloc(sizeof(*link), GFP_USER);
|
||||
if (!link) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_cgroup;
|
||||
}
|
||||
bpf_link_init(&link->link, &bpf_cgroup_link_lops, prog);
|
||||
link->cgroup = cgrp;
|
||||
link->type = attr->link_create.attach_type;
|
||||
|
||||
link_file = bpf_link_new_file(&link->link, &link_fd);
|
||||
if (IS_ERR(link_file)) {
|
||||
kfree(link);
|
||||
err = PTR_ERR(link_file);
|
||||
goto out_put_cgroup;
|
||||
}
|
||||
|
||||
err = cgroup_bpf_attach(cgrp, NULL, NULL, link, link->type,
|
||||
BPF_F_ALLOW_MULTI);
|
||||
if (err) {
|
||||
bpf_link_cleanup(&link->link, link_file, link_fd);
|
||||
goto out_put_cgroup;
|
||||
}
|
||||
|
||||
fd_install(link_fd, link_file);
|
||||
return link_fd;
|
||||
|
||||
out_put_cgroup:
|
||||
cgroup_put(cgrp);
|
||||
return err;
|
||||
}
|
||||
|
||||
int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
|
@@ -2156,6 +2156,7 @@ const struct bpf_func_proto bpf_get_current_pid_tgid_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_comm_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_cgroup_id_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_ancestor_cgroup_id_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_local_storage_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto __weak;
|
||||
|
||||
|
@@ -340,6 +340,24 @@ const struct bpf_func_proto bpf_get_current_cgroup_id_proto = {
|
||||
.ret_type = RET_INTEGER,
|
||||
};
|
||||
|
||||
BPF_CALL_1(bpf_get_current_ancestor_cgroup_id, int, ancestor_level)
|
||||
{
|
||||
struct cgroup *cgrp = task_dfl_cgroup(current);
|
||||
struct cgroup *ancestor;
|
||||
|
||||
ancestor = cgroup_ancestor(cgrp, ancestor_level);
|
||||
if (!ancestor)
|
||||
return 0;
|
||||
return cgroup_id(ancestor);
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_get_current_ancestor_cgroup_id_proto = {
|
||||
.func = bpf_get_current_ancestor_cgroup_id,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
DECLARE_PER_CPU(struct bpf_cgroup_storage*,
|
||||
bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/audit.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/bpf_lsm.h>
|
||||
|
||||
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
|
||||
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
|
||||
@@ -1942,6 +1943,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
|
||||
|
||||
switch (prog_type) {
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
case BPF_PROG_TYPE_LSM:
|
||||
case BPF_PROG_TYPE_STRUCT_OPS:
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
break;
|
||||
@@ -2181,13 +2183,6 @@ static int bpf_obj_get(const union bpf_attr *attr)
|
||||
attr->file_flags);
|
||||
}
|
||||
|
||||
struct bpf_link {
|
||||
atomic64_t refcnt;
|
||||
const struct bpf_link_ops *ops;
|
||||
struct bpf_prog *prog;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
|
||||
struct bpf_prog *prog)
|
||||
{
|
||||
@@ -2201,8 +2196,8 @@ void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
|
||||
* anon_inode's release() call. This helper manages marking bpf_link as
|
||||
* defunct, releases anon_inode file and puts reserved FD.
|
||||
*/
|
||||
static void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
|
||||
int link_fd)
|
||||
void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
|
||||
int link_fd)
|
||||
{
|
||||
link->prog = NULL;
|
||||
fput(link_file);
|
||||
@@ -2260,7 +2255,6 @@ static int bpf_link_release(struct inode *inode, struct file *filp)
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static const struct bpf_link_ops bpf_raw_tp_lops;
|
||||
static const struct bpf_link_ops bpf_tracing_link_lops;
|
||||
static const struct bpf_link_ops bpf_xdp_link_lops;
|
||||
|
||||
static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
|
||||
{
|
||||
@@ -2273,6 +2267,10 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
|
||||
link_type = "raw_tracepoint";
|
||||
else if (link->ops == &bpf_tracing_link_lops)
|
||||
link_type = "tracing";
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
else if (link->ops == &bpf_cgroup_link_lops)
|
||||
link_type = "cgroup";
|
||||
#endif
|
||||
else
|
||||
link_type = "unknown";
|
||||
|
||||
@@ -2375,10 +2373,28 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
struct file *link_file;
|
||||
int link_fd, err;
|
||||
|
||||
if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
|
||||
prog->expected_attach_type != BPF_TRACE_FEXIT &&
|
||||
prog->expected_attach_type != BPF_MODIFY_RETURN &&
|
||||
prog->type != BPF_PROG_TYPE_EXT) {
|
||||
switch (prog->type) {
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
|
||||
prog->expected_attach_type != BPF_TRACE_FEXIT &&
|
||||
prog->expected_attach_type != BPF_MODIFY_RETURN) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
break;
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
if (prog->expected_attach_type != 0) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
break;
|
||||
case BPF_PROG_TYPE_LSM:
|
||||
if (prog->expected_attach_type != BPF_LSM_MAC) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
@@ -2457,16 +2473,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
|
||||
prog->type != BPF_PROG_TYPE_TRACING &&
|
||||
prog->type != BPF_PROG_TYPE_EXT &&
|
||||
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
if (prog->type == BPF_PROG_TYPE_TRACING ||
|
||||
prog->type == BPF_PROG_TYPE_EXT) {
|
||||
switch (prog->type) {
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
case BPF_PROG_TYPE_LSM:
|
||||
if (attr->raw_tracepoint.name) {
|
||||
/* The attach point for this category of programs
|
||||
* should be specified via btf_id during program load.
|
||||
@@ -2474,11 +2484,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
if (prog->expected_attach_type == BPF_TRACE_RAW_TP)
|
||||
if (prog->type == BPF_PROG_TYPE_TRACING &&
|
||||
prog->expected_attach_type == BPF_TRACE_RAW_TP) {
|
||||
tp_name = prog->aux->attach_func_name;
|
||||
else
|
||||
return bpf_tracing_prog_attach(prog);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
return bpf_tracing_prog_attach(prog);
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT:
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
|
||||
if (strncpy_from_user(buf,
|
||||
u64_to_user_ptr(attr->raw_tracepoint.name),
|
||||
sizeof(buf) - 1) < 0) {
|
||||
@@ -2487,6 +2500,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
}
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
tp_name = buf;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
btp = bpf_get_raw_tracepoint(tp_name);
|
||||
@@ -2543,6 +2560,50 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
|
||||
}
|
||||
}
|
||||
|
||||
static enum bpf_prog_type
|
||||
attach_type_to_prog_type(enum bpf_attach_type attach_type)
|
||||
{
|
||||
switch (attach_type) {
|
||||
case BPF_CGROUP_INET_INGRESS:
|
||||
case BPF_CGROUP_INET_EGRESS:
|
||||
return BPF_PROG_TYPE_CGROUP_SKB;
|
||||
break;
|
||||
case BPF_CGROUP_INET_SOCK_CREATE:
|
||||
case BPF_CGROUP_INET4_POST_BIND:
|
||||
case BPF_CGROUP_INET6_POST_BIND:
|
||||
return BPF_PROG_TYPE_CGROUP_SOCK;
|
||||
case BPF_CGROUP_INET4_BIND:
|
||||
case BPF_CGROUP_INET6_BIND:
|
||||
case BPF_CGROUP_INET4_CONNECT:
|
||||
case BPF_CGROUP_INET6_CONNECT:
|
||||
case BPF_CGROUP_UDP4_SENDMSG:
|
||||
case BPF_CGROUP_UDP6_SENDMSG:
|
||||
case BPF_CGROUP_UDP4_RECVMSG:
|
||||
case BPF_CGROUP_UDP6_RECVMSG:
|
||||
return BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
|
||||
case BPF_CGROUP_SOCK_OPS:
|
||||
return BPF_PROG_TYPE_SOCK_OPS;
|
||||
case BPF_CGROUP_DEVICE:
|
||||
return BPF_PROG_TYPE_CGROUP_DEVICE;
|
||||
case BPF_SK_MSG_VERDICT:
|
||||
return BPF_PROG_TYPE_SK_MSG;
|
||||
case BPF_SK_SKB_STREAM_PARSER:
|
||||
case BPF_SK_SKB_STREAM_VERDICT:
|
||||
return BPF_PROG_TYPE_SK_SKB;
|
||||
case BPF_LIRC_MODE2:
|
||||
return BPF_PROG_TYPE_LIRC_MODE2;
|
||||
case BPF_FLOW_DISSECTOR:
|
||||
return BPF_PROG_TYPE_FLOW_DISSECTOR;
|
||||
case BPF_CGROUP_SYSCTL:
|
||||
return BPF_PROG_TYPE_CGROUP_SYSCTL;
|
||||
case BPF_CGROUP_GETSOCKOPT:
|
||||
case BPF_CGROUP_SETSOCKOPT:
|
||||
return BPF_PROG_TYPE_CGROUP_SOCKOPT;
|
||||
default:
|
||||
return BPF_PROG_TYPE_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
#define BPF_PROG_ATTACH_LAST_FIELD replace_bpf_fd
|
||||
|
||||
#define BPF_F_ATTACH_MASK \
|
||||
@@ -2563,55 +2624,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
|
||||
if (attr->attach_flags & ~BPF_F_ATTACH_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
switch (attr->attach_type) {
|
||||
case BPF_CGROUP_INET_INGRESS:
|
||||
case BPF_CGROUP_INET_EGRESS:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SKB;
|
||||
break;
|
||||
case BPF_CGROUP_INET_SOCK_CREATE:
|
||||
case BPF_CGROUP_INET4_POST_BIND:
|
||||
case BPF_CGROUP_INET6_POST_BIND:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SOCK;
|
||||
break;
|
||||
case BPF_CGROUP_INET4_BIND:
|
||||
case BPF_CGROUP_INET6_BIND:
|
||||
case BPF_CGROUP_INET4_CONNECT:
|
||||
case BPF_CGROUP_INET6_CONNECT:
|
||||
case BPF_CGROUP_UDP4_SENDMSG:
|
||||
case BPF_CGROUP_UDP6_SENDMSG:
|
||||
case BPF_CGROUP_UDP4_RECVMSG:
|
||||
case BPF_CGROUP_UDP6_RECVMSG:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
|
||||
break;
|
||||
case BPF_CGROUP_SOCK_OPS:
|
||||
ptype = BPF_PROG_TYPE_SOCK_OPS;
|
||||
break;
|
||||
case BPF_CGROUP_DEVICE:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
|
||||
break;
|
||||
case BPF_SK_MSG_VERDICT:
|
||||
ptype = BPF_PROG_TYPE_SK_MSG;
|
||||
break;
|
||||
case BPF_SK_SKB_STREAM_PARSER:
|
||||
case BPF_SK_SKB_STREAM_VERDICT:
|
||||
ptype = BPF_PROG_TYPE_SK_SKB;
|
||||
break;
|
||||
case BPF_LIRC_MODE2:
|
||||
ptype = BPF_PROG_TYPE_LIRC_MODE2;
|
||||
break;
|
||||
case BPF_FLOW_DISSECTOR:
|
||||
ptype = BPF_PROG_TYPE_FLOW_DISSECTOR;
|
||||
break;
|
||||
case BPF_CGROUP_SYSCTL:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SYSCTL;
|
||||
break;
|
||||
case BPF_CGROUP_GETSOCKOPT:
|
||||
case BPF_CGROUP_SETSOCKOPT:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SOCKOPT;
|
||||
break;
|
||||
default:
|
||||
ptype = attach_type_to_prog_type(attr->attach_type);
|
||||
if (ptype == BPF_PROG_TYPE_UNSPEC)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype);
|
||||
if (IS_ERR(prog))
|
||||
@@ -2633,8 +2648,17 @@ static int bpf_prog_attach(const union bpf_attr *attr)
|
||||
case BPF_PROG_TYPE_FLOW_DISSECTOR:
|
||||
ret = skb_flow_dissector_bpf_prog_attach(attr, prog);
|
||||
break;
|
||||
default:
|
||||
case BPF_PROG_TYPE_CGROUP_DEVICE:
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
case BPF_PROG_TYPE_SOCK_OPS:
|
||||
ret = cgroup_bpf_prog_attach(attr, ptype, prog);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
@@ -2654,53 +2678,27 @@ static int bpf_prog_detach(const union bpf_attr *attr)
|
||||
if (CHECK_ATTR(BPF_PROG_DETACH))
|
||||
return -EINVAL;
|
||||
|
||||
switch (attr->attach_type) {
|
||||
case BPF_CGROUP_INET_INGRESS:
|
||||
case BPF_CGROUP_INET_EGRESS:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SKB;
|
||||
break;
|
||||
case BPF_CGROUP_INET_SOCK_CREATE:
|
||||
case BPF_CGROUP_INET4_POST_BIND:
|
||||
case BPF_CGROUP_INET6_POST_BIND:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SOCK;
|
||||
break;
|
||||
case BPF_CGROUP_INET4_BIND:
|
||||
case BPF_CGROUP_INET6_BIND:
|
||||
case BPF_CGROUP_INET4_CONNECT:
|
||||
case BPF_CGROUP_INET6_CONNECT:
|
||||
case BPF_CGROUP_UDP4_SENDMSG:
|
||||
case BPF_CGROUP_UDP6_SENDMSG:
|
||||
case BPF_CGROUP_UDP4_RECVMSG:
|
||||
case BPF_CGROUP_UDP6_RECVMSG:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
|
||||
break;
|
||||
case BPF_CGROUP_SOCK_OPS:
|
||||
ptype = BPF_PROG_TYPE_SOCK_OPS;
|
||||
break;
|
||||
case BPF_CGROUP_DEVICE:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
|
||||
break;
|
||||
case BPF_SK_MSG_VERDICT:
|
||||
ptype = attach_type_to_prog_type(attr->attach_type);
|
||||
|
||||
switch (ptype) {
|
||||
case BPF_PROG_TYPE_SK_MSG:
|
||||
case BPF_PROG_TYPE_SK_SKB:
|
||||
return sock_map_get_from_fd(attr, NULL);
|
||||
case BPF_SK_SKB_STREAM_PARSER:
|
||||
case BPF_SK_SKB_STREAM_VERDICT:
|
||||
return sock_map_get_from_fd(attr, NULL);
|
||||
case BPF_LIRC_MODE2:
|
||||
case BPF_PROG_TYPE_LIRC_MODE2:
|
||||
return lirc_prog_detach(attr);
|
||||
case BPF_FLOW_DISSECTOR:
|
||||
case BPF_PROG_TYPE_FLOW_DISSECTOR:
|
||||
return skb_flow_dissector_bpf_prog_detach(attr);
|
||||
case BPF_CGROUP_SYSCTL:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SYSCTL;
|
||||
break;
|
||||
case BPF_CGROUP_GETSOCKOPT:
|
||||
case BPF_CGROUP_SETSOCKOPT:
|
||||
ptype = BPF_PROG_TYPE_CGROUP_SOCKOPT;
|
||||
break;
|
||||
case BPF_PROG_TYPE_CGROUP_DEVICE:
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
case BPF_PROG_TYPE_SOCK_OPS:
|
||||
return cgroup_bpf_prog_detach(attr, ptype);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cgroup_bpf_prog_detach(attr, ptype);
|
||||
}
|
||||
|
||||
#define BPF_PROG_QUERY_LAST_FIELD query.prog_cnt
|
||||
@@ -2734,7 +2732,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
|
||||
case BPF_CGROUP_SYSCTL:
|
||||
case BPF_CGROUP_GETSOCKOPT:
|
||||
case BPF_CGROUP_SETSOCKOPT:
|
||||
break;
|
||||
return cgroup_bpf_prog_query(attr, uattr);
|
||||
case BPF_LIRC_MODE2:
|
||||
return lirc_prog_query(attr, uattr);
|
||||
case BPF_FLOW_DISSECTOR:
|
||||
@@ -2742,8 +2740,6 @@ static int bpf_prog_query(const union bpf_attr *attr,
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cgroup_bpf_prog_query(attr, uattr);
|
||||
}
|
||||
|
||||
#define BPF_PROG_TEST_RUN_LAST_FIELD test.ctx_out
|
||||
@@ -3564,6 +3560,104 @@ err_put:
|
||||
return err;
|
||||
}
|
||||
|
||||
#define BPF_LINK_CREATE_LAST_FIELD link_create.flags
|
||||
static int link_create(union bpf_attr *attr)
|
||||
{
|
||||
enum bpf_prog_type ptype;
|
||||
struct bpf_prog *prog;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (CHECK_ATTR(BPF_LINK_CREATE))
|
||||
return -EINVAL;
|
||||
|
||||
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
|
||||
if (ptype == BPF_PROG_TYPE_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
prog = bpf_prog_get_type(attr->link_create.prog_fd, ptype);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
ret = bpf_prog_attach_check_attach_type(prog,
|
||||
attr->link_create.attach_type);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
switch (ptype) {
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
case BPF_PROG_TYPE_SOCK_OPS:
|
||||
case BPF_PROG_TYPE_CGROUP_DEVICE:
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
ret = cgroup_bpf_link_attach(attr, prog);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
err_out:
|
||||
if (ret < 0)
|
||||
bpf_prog_put(prog);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define BPF_LINK_UPDATE_LAST_FIELD link_update.old_prog_fd
|
||||
|
||||
static int link_update(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_prog *old_prog = NULL, *new_prog;
|
||||
struct bpf_link *link;
|
||||
u32 flags;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (CHECK_ATTR(BPF_LINK_UPDATE))
|
||||
return -EINVAL;
|
||||
|
||||
flags = attr->link_update.flags;
|
||||
if (flags & ~BPF_F_REPLACE)
|
||||
return -EINVAL;
|
||||
|
||||
link = bpf_link_get_from_fd(attr->link_update.link_fd);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
|
||||
new_prog = bpf_prog_get(attr->link_update.new_prog_fd);
|
||||
if (IS_ERR(new_prog))
|
||||
return PTR_ERR(new_prog);
|
||||
|
||||
if (flags & BPF_F_REPLACE) {
|
||||
old_prog = bpf_prog_get(attr->link_update.old_prog_fd);
|
||||
if (IS_ERR(old_prog)) {
|
||||
ret = PTR_ERR(old_prog);
|
||||
old_prog = NULL;
|
||||
goto out_put_progs;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
if (link->ops == &bpf_cgroup_link_lops) {
|
||||
ret = cgroup_bpf_replace(link, old_prog, new_prog);
|
||||
goto out_put_progs;
|
||||
}
|
||||
#endif
|
||||
ret = -EINVAL;
|
||||
|
||||
out_put_progs:
|
||||
if (old_prog)
|
||||
bpf_prog_put(old_prog);
|
||||
if (ret)
|
||||
bpf_prog_put(new_prog);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
@@ -3675,6 +3769,12 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
||||
case BPF_MAP_DELETE_BATCH:
|
||||
err = bpf_map_do_batch(&attr, uattr, BPF_MAP_DELETE_BATCH);
|
||||
break;
|
||||
case BPF_LINK_CREATE:
|
||||
err = link_create(&attr);
|
||||
break;
|
||||
case BPF_LINK_UPDATE:
|
||||
err = link_update(&attr);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
|
@@ -9,15 +9,15 @@
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
/* See scripts/link-vmlinux.sh, gen_btf() func for details */
|
||||
extern char __weak _binary__btf_vmlinux_bin_start[];
|
||||
extern char __weak _binary__btf_vmlinux_bin_end[];
|
||||
extern char __weak __start_BTF[];
|
||||
extern char __weak __stop_BTF[];
|
||||
|
||||
static ssize_t
|
||||
btf_vmlinux_read(struct file *file, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t len)
|
||||
{
|
||||
memcpy(buf, _binary__btf_vmlinux_bin_start + off, len);
|
||||
memcpy(buf, __start_BTF + off, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -30,15 +30,14 @@ static struct kobject *btf_kobj;
|
||||
|
||||
static int __init btf_vmlinux_init(void)
|
||||
{
|
||||
if (!_binary__btf_vmlinux_bin_start)
|
||||
if (!__start_BTF)
|
||||
return 0;
|
||||
|
||||
btf_kobj = kobject_create_and_add("btf", kernel_kobj);
|
||||
if (!btf_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
bin_attr_btf_vmlinux.size = _binary__btf_vmlinux_bin_end -
|
||||
_binary__btf_vmlinux_bin_start;
|
||||
bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF;
|
||||
|
||||
return sysfs_create_bin_file(btf_kobj, &bin_attr_btf_vmlinux);
|
||||
}
|
||||
|
@@ -194,3 +194,18 @@ int tnum_sbin(char *str, size_t size, struct tnum a)
|
||||
str[min(size - 1, (size_t)64)] = 0;
|
||||
return 64;
|
||||
}
|
||||
|
||||
struct tnum tnum_subreg(struct tnum a)
|
||||
{
|
||||
return tnum_cast(a, 4);
|
||||
}
|
||||
|
||||
struct tnum tnum_clear_subreg(struct tnum a)
|
||||
{
|
||||
return tnum_lshift(tnum_rshift(a, 32), 32);
|
||||
}
|
||||
|
||||
struct tnum tnum_const_subreg(struct tnum a, u32 value)
|
||||
{
|
||||
return tnum_or(tnum_clear_subreg(a), tnum_const(value));
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/btf.h>
|
||||
|
||||
/* dummy _ops. The verifier will operate on target program's ops. */
|
||||
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
|
||||
@@ -233,15 +234,23 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t)
|
||||
static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
|
||||
{
|
||||
switch (t) {
|
||||
switch (prog->expected_attach_type) {
|
||||
case BPF_TRACE_FENTRY:
|
||||
return BPF_TRAMP_FENTRY;
|
||||
case BPF_MODIFY_RETURN:
|
||||
return BPF_TRAMP_MODIFY_RETURN;
|
||||
case BPF_TRACE_FEXIT:
|
||||
return BPF_TRAMP_FEXIT;
|
||||
case BPF_LSM_MAC:
|
||||
if (!prog->aux->attach_func_proto->type)
|
||||
/* The function returns void, we cannot modify its
|
||||
* return value.
|
||||
*/
|
||||
return BPF_TRAMP_FEXIT;
|
||||
else
|
||||
return BPF_TRAMP_MODIFY_RETURN;
|
||||
default:
|
||||
return BPF_TRAMP_REPLACE;
|
||||
}
|
||||
@@ -255,7 +264,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
|
||||
int cnt;
|
||||
|
||||
tr = prog->aux->trampoline;
|
||||
kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
|
||||
kind = bpf_attach_type_to_tramp(prog);
|
||||
mutex_lock(&tr->mutex);
|
||||
if (tr->extension_prog) {
|
||||
/* cannot attach fentry/fexit if extension prog is attached.
|
||||
@@ -305,7 +314,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
|
||||
int err;
|
||||
|
||||
tr = prog->aux->trampoline;
|
||||
kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
|
||||
kind = bpf_attach_type_to_tramp(prog);
|
||||
mutex_lock(&tr->mutex);
|
||||
if (kind == BPF_TRAMP_REPLACE) {
|
||||
WARN_ON_ONCE(!tr->extension_prog);
|
||||
|
文件差異過大導致無法顯示
Load Diff
Reference in New Issue
Block a user