Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion Documentation/bpf/kfuncs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Or::
...
}

2.2.6 __prog Annotation
2.2.6 __prog Annotation (deprecated, use __magic instead)
---------------------------
This annotation is used to indicate that the argument needs to be fixed up to
the bpf_prog_aux of the caller BPF program. Any value passed into this argument
Expand All @@ -177,6 +177,37 @@ An example is given below::
...
}

.. _magic_annotation:

2.2.7 __magic Annotation
---------------------------
This annotation is used in kfuncs with KF_MAGIC_ARGS flag to indicate the
arguments that are omitted in the BPF signature of the kfunc. The actual
values for __magic arguments are set by the verifier at load time, and
depend on the argument type.

Currently only ``struct bpf_prog_aux *`` type is supported.

Example declaration:

.. code-block:: c

__bpf_kfunc int bpf_wq_set_callback(struct bpf_wq *wq,
int (callback_fn)(void *map, int *key, void *value),
unsigned int flags,
struct bpf_prog_aux *aux__magic)
{
...
}

Example usage:

.. code-block:: c

/* note the last argument is omitted */
if (bpf_wq_set_callback(wq, callback_fn, 0))
return -1;

.. _BPF_kfunc_nodef:

2.3 Using an existing kernel function
Expand Down Expand Up @@ -374,6 +405,22 @@ encouraged to make their use-cases known as early as possible, and participate
in upstream discussions regarding whether to keep, change, deprecate, or remove
those kfuncs if and when such discussions occur.

2.4.10 KF_MAGIC_ARGS flag
------------------------------------

The KF_MAGIC_ARGS flag is used to indicate that the BPF signature of the kfunc
is different from it's kernel signature, and the values for arguments annotated
with __magic suffix are provided at load time by the verifier.

A kfunc with KF_MAGIC_ARGS flag therefore has two types in BTF: one function
matching the kernel declaration (with _impl suffix by convention), and another
matching the intended BPF API.

Verifier allows calls to both _impl and non-_impl versions of a magic kfunc.

Note that only arguments of particular types can be __magic.
See :ref:`magic_annotation`.

2.5 Registering the kfuncs
--------------------------

Expand Down
7 changes: 5 additions & 2 deletions include/linux/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
#define KF_ARENA_RET (1 << 13) /* kfunc returns an arena pointer */
#define KF_ARENA_ARG1 (1 << 14) /* kfunc takes an arena pointer as its first argument */
#define KF_ARENA_ARG2 (1 << 15) /* kfunc takes an arena pointer as its second argument */
#define KF_MAGIC_ARGS (1 << 16) /* kfunc signature is different from its BPF signature */

/*
* Tag marking a kernel function as a kfunc. This is meant to minimize the
Expand Down Expand Up @@ -575,8 +576,10 @@ const char *btf_name_by_offset(const struct btf *btf, u32 offset);
const char *btf_str_by_offset(const struct btf *btf, u32 offset);
struct btf *btf_parse_vmlinux(void);
struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
u32 *btf_kfunc_id_set_contains(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog);
u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
u32 *btf_kfunc_flags_if_allowed(const struct btf *btf,
u32 kfunc_btf_id,
const struct bpf_prog *prog);
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog);
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
Expand Down
10 changes: 10 additions & 0 deletions include/linux/btf_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ asm( \
__BTF_ID_LIST(name, local) \
extern u32 name[];

/*
* The BTF_ID_LIST_END macro may be used to denote an end
* of a BTF_ID_LIST. This enables calculation of the list
* size with BTF_ID_LIST_SIZE.
*/
#define BTF_ID_LIST_END(name) \
BTF_ID_LIST(name##__end)
#define BTF_ID_LIST_SIZE(name) \
(name##__end - name)

#define BTF_ID_LIST_GLOBAL(name, n) \
__BTF_ID_LIST(name, globl)

Expand Down
70 changes: 53 additions & 17 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8640,24 +8640,17 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook,
return ret;
}

static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
enum btf_kfunc_hook hook,
u32 kfunc_btf_id,
const struct bpf_prog *prog)
static u32 *btf_kfunc_id_set_contains(const struct btf *btf,
enum btf_kfunc_hook hook,
u32 kfunc_btf_id)
{
struct btf_kfunc_hook_filter *hook_filter;
struct btf_id_set8 *set;
u32 *id, i;
u32 *id;

if (hook >= BTF_KFUNC_HOOK_MAX)
return NULL;
if (!btf->kfunc_set_tab)
return NULL;
hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
for (i = 0; i < hook_filter->nr_filters; i++) {
if (hook_filter->filters[i](prog, kfunc_btf_id))
return NULL;
}
set = btf->kfunc_set_tab->sets[hook];
if (!set)
return NULL;
Expand All @@ -8668,6 +8661,28 @@ static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
return id + 1;
}

static bool btf_kfunc_is_allowed(const struct btf *btf,
enum btf_kfunc_hook hook,
u32 kfunc_btf_id,
const struct bpf_prog *prog)
{
struct btf_kfunc_hook_filter *hook_filter;
int i;

if (hook >= BTF_KFUNC_HOOK_MAX)
return false;
if (!btf->kfunc_set_tab)
return false;

hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
for (i = 0; i < hook_filter->nr_filters; i++) {
if (hook_filter->filters[i](prog, kfunc_btf_id))
return false;
}

return true;
}

static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
{
switch (prog_type) {
Expand Down Expand Up @@ -8721,26 +8736,47 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
* keeping the reference for the duration of the call provides the necessary
* protection for looking up a well-formed btf->kfunc_set_tab.
*/
u32 *btf_kfunc_id_set_contains(const struct btf *btf,
u32 kfunc_btf_id,
const struct bpf_prog *prog)
u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog)
{
enum bpf_prog_type prog_type = resolve_prog_type(prog);
enum btf_kfunc_hook hook;
u32 *kfunc_flags;

kfunc_flags = __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog);
kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
if (kfunc_flags)
return kfunc_flags;

hook = bpf_prog_type_to_kfunc_hook(prog_type);
return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id, prog);
return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
}

u32 *btf_kfunc_flags_if_allowed(const struct btf *btf,
u32 kfunc_btf_id,
const struct bpf_prog *prog)
{
enum bpf_prog_type prog_type = resolve_prog_type(prog);
enum btf_kfunc_hook hook;
u32 *kfunc_flags;

kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
if (kfunc_flags && btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog))
return kfunc_flags;

hook = bpf_prog_type_to_kfunc_hook(prog_type);
kfunc_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
if (kfunc_flags && btf_kfunc_is_allowed(btf, hook, kfunc_btf_id, prog))
return kfunc_flags;

return NULL;
}

u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog)
{
return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog);
if (!btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog))
return NULL;

return btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
}

static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,
Expand Down
31 changes: 15 additions & 16 deletions kernel/bpf/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -3119,18 +3119,17 @@ __bpf_kfunc int bpf_wq_start(struct bpf_wq *wq, unsigned int flags)
return 0;
}

__bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq,
int (callback_fn)(void *map, int *key, void *value),
unsigned int flags,
void *aux__prog)
__bpf_kfunc int bpf_wq_set_callback(struct bpf_wq *wq,
int (callback_fn)(void *map, int *key, void *value),
unsigned int flags,
struct bpf_prog_aux *aux__magic)
{
struct bpf_prog_aux *aux = (struct bpf_prog_aux *)aux__prog;
struct bpf_async_kern *async = (struct bpf_async_kern *)wq;

if (flags)
return -EINVAL;

return __bpf_async_set_callback(async, callback_fn, aux, flags, BPF_ASYNC_TYPE_WQ);
return __bpf_async_set_callback(async, callback_fn, aux__magic, flags, BPF_ASYNC_TYPE_WQ);
}

__bpf_kfunc void bpf_preempt_disable(void)
Expand Down Expand Up @@ -4279,15 +4278,15 @@ static int bpf_task_work_schedule(struct task_struct *task, struct bpf_task_work
* @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
* @map__map: bpf_map that embeds struct bpf_task_work in the values
* @callback: pointer to BPF subprogram to call
* @aux__prog: user should pass NULL
* @aux__magic: pointer to bpf_prog_aux of the caller BPF program, set by the verifier
*
* Return: 0 if task work has been scheduled successfully, negative error code otherwise
*/
__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
void *map__map, bpf_task_work_callback_t callback,
void *aux__prog)
struct bpf_prog_aux *aux__magic)
{
return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
return bpf_task_work_schedule(task, tw, map__map, callback, aux__magic, TWA_SIGNAL);
}

/**
Expand All @@ -4296,15 +4295,15 @@ __bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct b
* @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
* @map__map: bpf_map that embeds struct bpf_task_work in the values
* @callback: pointer to BPF subprogram to call
* @aux__prog: user should pass NULL
* @aux__magic: pointer to bpf_prog_aux of the caller BPF program, set by the verifier
*
* Return: 0 if task work has been scheduled successfully, negative error code otherwise
*/
__bpf_kfunc int bpf_task_work_schedule_resume(struct task_struct *task, struct bpf_task_work *tw,
void *map__map, bpf_task_work_callback_t callback,
void *aux__prog)
struct bpf_prog_aux *aux__magic)
{
return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_RESUME);
return bpf_task_work_schedule(task, tw, map__map, callback, aux__magic, TWA_RESUME);
}

static int make_file_dynptr(struct file *file, u32 flags, bool may_sleep,
Expand Down Expand Up @@ -4483,7 +4482,7 @@ BTF_ID_FLAGS(func, bpf_dynptr_memset)
BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
#endif
BTF_ID_FLAGS(func, bpf_wq_init)
BTF_ID_FLAGS(func, bpf_wq_set_callback_impl)
BTF_ID_FLAGS(func, bpf_wq_set_callback, KF_MAGIC_ARGS)
BTF_ID_FLAGS(func, bpf_wq_start)
BTF_ID_FLAGS(func, bpf_preempt_disable)
BTF_ID_FLAGS(func, bpf_preempt_enable)
Expand Down Expand Up @@ -4529,9 +4528,9 @@ BTF_ID_FLAGS(func, bpf_strncasestr);
#if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
#endif
BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_MAGIC_ARGS | KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_MAGIC_ARGS | KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_MAGIC_ARGS | KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_dynptr_from_file, KF_TRUSTED_ARGS)
BTF_ID_FLAGS(func, bpf_dynptr_file_discard)
BTF_KFUNCS_END(common_btf_ids)
Expand Down
9 changes: 6 additions & 3 deletions kernel/bpf/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,22 @@ __bpf_kfunc_start_defs();
* Avoid using enum bpf_stream_id so that kfunc users don't have to pull in the
* enum in headers.
*/
__bpf_kfunc int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args, u32 len__sz, void *aux__prog)
__bpf_kfunc int bpf_stream_vprintk(int stream_id,
const char *fmt__str,
const void *args,
u32 len__sz,
struct bpf_prog_aux *aux__magic)
{
struct bpf_bprintf_data data = {
.get_bin_args = true,
.get_buf = true,
};
struct bpf_prog_aux *aux = aux__prog;
u32 fmt_size = strlen(fmt__str) + 1;
struct bpf_stream *stream;
u32 data_len = len__sz;
int ret, num_args;

stream = bpf_stream_get(stream_id, aux);
stream = bpf_stream_get(stream_id, aux__magic);
if (!stream)
return -ENOENT;

Expand Down
Loading
Loading