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
65 changes: 65 additions & 0 deletions Documentation/bpf/kfuncs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,71 @@ is also covered by this recovery. A kfunc handed an arena pointer may
therefore access up to ``GUARD_SZ / 2`` past it without bounds-checking
against the arena. Larger accesses must verify the range explicitly.

2.9 kfunc Return Values
-----------------------

A kfunc may return a scalar, a pointer, or a small struct or union by
value. A scalar or pointer of up to 8 bytes is returned in R0, as usual.

A struct or union returned by value must be composed only of scalars
(recursively), where a scalar is an integer or an enum; arrays of scalars are
allowed as members. Its bytes are handed back to the program as the raw
contents of R0 (and R2), so a pointer field would be laundered into a scalar
and escape the verifier's pointer provenance and reference tracking. A struct
or union with a pointer member is therefore rejected at load time, and so is
one with a floating-point member, which the ABI may not return in R0:R2 at
all.

A kfunc may also return a value larger than 8 bytes and up to 16 bytes -- a
scalar-only struct or union, or an ``__int128``. Such a value is returned
in the register pair R0:R2, matching the convention LLVM uses for the BPF
target: the first 8 bytes in R0 and the second 8 bytes in R2. A struct or
union of 8 bytes or less is returned in R0 alone.

::

struct bpf_pair { __u64 a, b; }; /* 16 bytes */

__bpf_kfunc struct bpf_pair bpf_kfunc_get_pair(void)
{
struct bpf_pair p = { .a = 1, .b = 2 };

return p; /* p.a in R0, p.b in R2 */
}

Returning a value in the R0:R2 pair requires the JIT to place the second
half of the return value into R2, which not every architecture supports
right now. A kfunc with a return value larger than 8 bytes is therefore
rejected at load time on a JIT that does not advertise this capability (see
``bpf_jit_supports_kfunc_ret_reg_pair()``), and such a program is never run
by the interpreter. A return value larger than 16 bytes is not supported.

The same R0:R2 convention applies to a BPF subprogram, global or static, that
returns an ``__int128`` or a struct or union larger than 8 bytes. It is only
used when the program is JITed, since the interpreter propagates only R0 out of
a subprogram: without a JIT the return value stays in R0 alone, and a caller
reading R2 is rejected for reading an uninitialized register. A global
subprogram is verified in isolation, so its by-value struct or union return is
restricted to scalars just like a kfunc's; a static subprogram is verified
inline and has no such restriction. The main program is not covered: its return
value is the program's exit code, read out of R0 alone, so a declared upper
half is never looked at.

A global subprogram must leave a scalar in *every* register of the pair, so
both halves of the returned value have to be assigned. Leaving the upper half
uninitialized is not merely untidy: the compiler is then free to leave R2
holding whatever it happened to hold, which for a subprogram taking a pointer
argument is typically that pointer. Handing the caller an unknown scalar built
from a pointer is a leak, so the verifier rejects it with::

At subprogram exit the register R2 is not a scalar value (...)

Initialize the whole return value, for example ``struct pair p = {};``, to
avoid this. A static subprogram is exempt from the scalar-only rule: it is
verified inline, so an unassigned R2 is simply passed back to the caller as
uninitialized and only a caller that reads it fails. A stack pointer left in
R2 is still rejected there, just as one in R0 is.

.. _BPF_kfunc_lifecycle_expectations:

3. kfunc lifecycle expectations
Expand Down
5 changes: 5 additions & 0 deletions arch/arm64/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2388,6 +2388,11 @@ bool bpf_jit_supports_kfunc_call(void)
return true;
}

bool bpf_jit_supports_kfunc_ret_reg_pair(void)
{
return true;
}

bool bpf_jit_supports_stack_args(void)
{
return true;
Expand Down
5 changes: 5 additions & 0 deletions arch/riscv/net/bpf_jit_comp64.c
Original file line number Diff line number Diff line change
Expand Up @@ -2121,6 +2121,11 @@ bool bpf_jit_supports_kfunc_call(void)
return true;
}

bool bpf_jit_supports_kfunc_ret_reg_pair(void)
{
return true;
}

bool bpf_jit_supports_ptr_xchg(void)
{
return true;
Expand Down
27 changes: 20 additions & 7 deletions arch/x86/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1689,17 +1689,12 @@ static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
* arena NULL is offset 0. Return the number of emitted bytes.
*/
static int emit_kfunc_arena_args(struct bpf_prog *bpf_prog,
const struct bpf_insn *insn, u8 **pprog)
const struct btf_func_model *fm, u8 **pprog)
{
const struct btf_func_model *fm;
u8 *prog = *pprog;
u8 *start = prog;
int i;

fm = bpf_jit_find_kfunc_model(bpf_prog, insn);
if (!fm)
return -EINVAL;

for (i = 0; i < min_t(int, fm->nr_args, MAX_BPF_FUNC_REG_ARGS); i++) {
u8 flags = fm->arg_flags[i];
u32 reg = BPF_REG_1 + i;
Expand Down Expand Up @@ -2644,6 +2639,8 @@ st: insn_off = insn->off;

/* call */
case BPF_JMP | BPF_CALL: {
const struct btf_func_model *fm = NULL;

func = (u8 *) __bpf_call_base + imm32;
if (src_reg == BPF_PSEUDO_CALL && tail_call_reachable) {
LOAD_TAIL_CALL_CNT_PTR(stack_depth);
Expand All @@ -2652,7 +2649,10 @@ st: insn_off = insn->off;
if (!imm32)
return -EINVAL;
if (src_reg == BPF_PSEUDO_KFUNC_CALL) {
err = emit_kfunc_arena_args(bpf_prog, insn, &prog);
fm = bpf_jit_find_kfunc_model(bpf_prog, insn);
if (!fm)
return -EINVAL;
err = emit_kfunc_arena_args(bpf_prog, fm, &prog);
if (err < 0)
return err;
ip += err;
Expand All @@ -2666,6 +2666,14 @@ st: insn_off = insn->off;
return -EINVAL;
if (priv_frame_ptr)
pop_r9(&prog);
/*
* A kfunc returning more than 8 bytes hands the second
* half back in RDX (the native ABI's second return reg),
* but BPF expects it in R0:R2. BPF R0 is RAX (no move
* needed), while BPF R2 is RSI, so copy RDX into RSI.
*/
if (fm && fm->ret_size > 8)
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_3);
break;
}

Expand Down Expand Up @@ -4156,6 +4164,11 @@ bool bpf_jit_supports_kfunc_call(void)
return true;
}

bool bpf_jit_supports_kfunc_ret_reg_pair(void)
{
return true;
}

bool bpf_jit_supports_stack_args(void)
{
return true;
Expand Down
9 changes: 9 additions & 0 deletions include/linux/bpf_verifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,8 @@ struct bpf_subprog_info {
bool is_async_cb: 1;
bool is_exception_cb: 1;
bool args_cached: 1;
/* true if the return value is passed in the R0:R2 register pair */
bool ret_reg_pair: 1;
/* true if bpf_fastcall stack region is used by functions that can't be inlined */
bool keep_fastcall_stack: 1;
bool changes_pkt_data: 1;
Expand Down Expand Up @@ -1055,6 +1057,11 @@ static inline struct bpf_subprog_info *subprog_info(struct bpf_verifier_env *env
return &env->subprog_info[subprog];
}

static inline bool bpf_ret_reg_pair(struct bpf_verifier_env *env, int subprog)
{
return subprog_info(env, subprog)->ret_reg_pair;
}

struct bpf_call_summary {
u8 num_params;
bool is_void;
Expand Down Expand Up @@ -1460,6 +1467,8 @@ int bpf_jmp_offset(struct bpf_insn *insn);
struct bpf_iarray *bpf_insn_successors(struct bpf_verifier_env *env, u32 idx);
void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog);
bool btf_type_is_scalar_struct(struct bpf_verifier_env *env, const struct btf *btf,
const struct btf_type *t, int rec);

int bpf_find_subprog(struct bpf_verifier_env *env, int off);
bool bpf_is_throw_kfunc(struct bpf_insn *insn);
Expand Down
1 change: 1 addition & 0 deletions include/linux/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,7 @@ bool bpf_jit_inlines_helper_call(s32 imm);
bool bpf_jit_supports_subprog_tailcalls(void);
bool bpf_jit_supports_percpu_insn(void);
bool bpf_jit_supports_kfunc_call(void);
bool bpf_jit_supports_kfunc_ret_reg_pair(void);
bool bpf_jit_supports_stack_args(void);
bool bpf_jit_supports_arena_args(void);
bool bpf_jit_supports_far_kfunc_call(void);
Expand Down
46 changes: 30 additions & 16 deletions kernel/bpf/backtrack.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,10 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
*/
verifier_bug_if(idx + 1 != subseq_idx, env,
"extra insn from subprog");
/* global subprog always sets R0 */
bt_clear_reg(bt, BPF_REG_0);
/* and if it does not set R2, main pass would catch it */
bt_clear_reg(bt, BPF_REG_2);
/* r1-r5 are invalidated after subprog call,
* so for global func call it shouldn't be set
* anymore
Expand All @@ -432,8 +436,6 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
bt_reg_mask(bt));
return -EFAULT;
}
/* global subprog always sets R0 */
bt_clear_reg(bt, BPF_REG_0);
return 0;
} else {
/* static subprog call instruction, which
Expand Down Expand Up @@ -506,6 +508,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
return -ENOTSUPP;
/* regular helper call sets R0 */
bt_clear_reg(bt, BPF_REG_0);
/* kfunc might also set R2 */
bt_clear_reg(bt, BPF_REG_2);
if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
/* if backtracking was looking for registers R1-R5
* they should have been found already.
Expand All @@ -520,7 +524,25 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
return -EFAULT;
}
} else if (opcode == BPF_EXIT) {
bool r0_precise;
bool from_subprog_call, r0_precise, r2_precise;

/* BPF_EXIT in subprog or callback always returns
* right after the call instruction, so by checking
* whether the instruction at subseq_idx-1 is subprog
* call or not we can distinguish actual exit from
* *subprog* from exit from *callback*. In the former
* case, we need to propagate the precision of the
* return registers, if necessary. In the latter we
* never do that.
*/
from_subprog_call = subseq_idx - 1 >= 0 &&
bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1]);

/* Sample the return registers before the callback
* handling below clears R1-R5.
*/
r0_precise = from_subprog_call && bt_is_reg_set(bt, BPF_REG_0);
r2_precise = from_subprog_call && bt_is_reg_set(bt, BPF_REG_2);

/* Backtracking to a nested function call, 'idx' is a part of
* the inner frame 'subseq_idx' is a part of the outer frame.
Expand All @@ -533,30 +555,22 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
if (subseq_idx >= 0 && bpf_calls_callback(env, subseq_idx))
for (i = BPF_REG_1; i <= BPF_REG_5; i++)
bt_clear_reg(bt, i);

bt_clear_reg(bt, BPF_REG_0);
bt_clear_reg(bt, BPF_REG_2);
if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
verifier_bug(env, "backtracking exit unexpected regs %x",
bt_reg_mask(bt));
return -EFAULT;
}

/* BPF_EXIT in subprog or callback always returns
* right after the call instruction, so by checking
* whether the instruction at subseq_idx-1 is subprog
* call or not we can distinguish actual exit from
* *subprog* from exit from *callback*. In the former
* case, we need to propagate r0 precision, if
* necessary. In the former we never do that.
*/
r0_precise = subseq_idx - 1 >= 0 &&
bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1]) &&
bt_is_reg_set(bt, BPF_REG_0);

bt_clear_reg(bt, BPF_REG_0);
if (bt_subprog_enter(bt))
return -EFAULT;

if (r0_precise)
bt_set_reg(bt, BPF_REG_0);
if (r2_precise)
bt_set_reg(bt, BPF_REG_2);
/* r6-r9 and stack slots will stay set in caller frame
* bitmasks until we return back from callee(s)
*/
Expand Down
29 changes: 24 additions & 5 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7591,7 +7591,7 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
return -EINVAL;
}
ret = __get_type_size(btf, func->type, &t);
if (ret < 0 || btf_type_is_struct(t)) {
if (ret < 0 || ret > 16) {
bpf_log(log,
"The function %s return type %s is unsupported.\n",
tname, btf_type_str(t));
Expand Down Expand Up @@ -7684,6 +7684,12 @@ static int btf_check_func_type_match(struct bpf_verifier_log *log,
btf_type_str(t2), fn2);
return -EINVAL;
}
if (btf_type_has_size(t1) && (t1->size > 8 || t2->size > 8)) {
bpf_log(log,
"Return type of %s() has size %u while %s() has size %u, and a size above 8 bytes cannot be replaced\n",
fn1, t1->size, fn2, t2->size);
return -EINVAL;
}

for (i = 0; i < nargs1; i++) {
t1 = btf_type_skip_modifiers(btf1, args1[i].type, NULL);
Expand Down Expand Up @@ -7964,7 +7970,7 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env,

/* Check whether the type is a valid return type. */
static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *btf,
const struct btf_type *t, int subprog)
const struct btf_type *t, int subprog, bool is_global)
{
u32 tags = 0;
int err;
Expand All @@ -7987,6 +7993,19 @@ static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *bt
if (btf_type_is_void(t) || btf_type_is_int(t) || btf_is_any_enum(t))
return 0;

if (btf_type_is_struct(t) && t->size <= 16) {
/*
* A global function's caller models the return as an opaque
* scalar pair, so it may only return scalars by value. A local
* function is verified inline, so a pointer field stays tracked
* and needs no such restriction.
*/
bool local_func = subprog && !is_global;

if (local_func || btf_type_is_scalar_struct(env, btf, t, 0))
return 0;
}

return -EOPNOTSUPP;
}

Expand Down Expand Up @@ -8074,12 +8093,12 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
return -EINVAL;
}

err = btf_validate_return_type(env, btf, t, subprog);
err = btf_validate_return_type(env, btf, t, subprog, is_global);
if (err) {
if (is_global) {
bpf_log(log,
"Global function %s() return value not void or scalar. "
"Only those are supported.\n",
"Global function %s() has unsupported return type. "
"Only void, scalar, or a scalar-only struct/union up to 16 bytes is supported.\n",
tname);
}
return err;
Expand Down
5 changes: 5 additions & 0 deletions kernel/bpf/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3287,6 +3287,11 @@ bool __weak bpf_jit_supports_kfunc_call(void)
return false;
}

bool __weak bpf_jit_supports_kfunc_ret_reg_pair(void)
{
return false;
}

bool __weak bpf_jit_supports_stack_args(void)
{
return false;
Expand Down
Loading
Loading