Skip to content

Commit d114bb9

Browse files
committed
Merge branch 'add-arena-argument-support-to-kfuncs-and-struct_ops'
Kumar Kartikeya Dwivedi says: ==================== Add arena argument support to kfuncs and struct_ops This is a continuation of patches in [0], with mostly minor changes and reordering. The motivation is covered in that link. A major change is moving to two tags (__arena and __arena__nullable) and moving the changes to JIT to emit more optimized sequences. Please see commit logs for details. [0]: https://lore.kernel.org/bpf/20260713024414.3759854-1-tj@kernel.org Changelog: ---------- v4 -> v5 v4: https://lore.kernel.org/bpf/20260805210427.3218326-1-memxor@gmail.com * Remove the redundant patch-8 capability comment and duplicate nullable kfunc test coverage. (Eduard) * Introduce the final bpf_tramp_arena_base() interface directly with function-model argument flags, avoiding temporary slot bitmaps and arena_nullable state; simplify struct_ops pointer validation. (Eduard) * Simplify kfunc arena nullability classification by using the common nullable path for both arena suffixes while leaving the function model to distinguish JIT NULL preservation. (Amery) * Keep bpf_prog_has_arena_ctx_arg() in bpf_verifier.h from its introduction so trampoline and verifier users share one inline definition, avoiding BPF_JIT/BPF_SYSCALL link dependencies. (Eduard, BPF CI Bot) * Reject both tracing and extension attachments to struct_ops programs with arena context arguments, and add fentry, fexit, and freplace rejection tests. (Eduard, Sashiko) v3 -> v4 v3: https://lore.kernel.org/bpf/20260803125115.2264733-1-memxor@gmail.com * Rename __arena_nullable to __arena__nullable and prioritize the composite suffix over __nullable during argument classification. (Sashiko, Eduard) * Resolve instructions before collecting subprograms and kfuncs so kfunc prototype validation can use associated arena state. * Move the arena kfunc and JIT-sequence test entry points into prog_tests/verifier.c. (Eduard) * Match the generated L0 target and call in nullable JIT assertions. (Eduard) * Route arena kfunc validation through the common argument-checking path. (Amery) * Reuse btf_func_model argument flags for struct_ops arena arguments instead of maintaining separate trampoline slot metadata. (Eduard) * Check the generic-trampoline arena argument invariant at link time and warn once on violations. (Eduard) * Reject tracing attachments to struct_ops programs with arena context arguments whose indirect trampolines convert the pointers. (Sashiko) v2 -> v3 v2: https://lore.kernel.org/bpf/20260726013105.3689867-1-memxor@gmail.com * Rebase onto current bpf-next to resolve conflicts. v1 -> v2 v1: https://lore.kernel.org/bpf/20260715220052.1590783-1-memxor@gmail.com * Fix documentation to only mention x86 for now. (Sashiko) * Move arg bitmap from insn_aux_data to kfunc descriptor. (Eduard) ==================== Link: https://patch.msgid.link/20260808003938.3486067-1-memxor@gmail.com Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
2 parents 8b365b3 + 4976cce commit d114bb9

21 files changed

Lines changed: 1123 additions & 59 deletions

Documentation/bpf/kfuncs.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,43 @@ An example is given below::
278278
...
279279
}
280280

281+
2.3.8 __arena and __arena__nullable Annotations
282+
-----------------------------------------------
283+
284+
Both annotations indicate that the pointer argument points into the
285+
calling program's arena. The JIT rebases the value at the call site so
286+
the kfunc receives a directly dereferenceable kernel address, subject to
287+
the access rules described in :ref:`BPF_kfunc_arena_access` (at most
288+
``GUARD_SZ / 2``, 32 KiB, past the pointer in a single unchecked access).
289+
290+
With ``__arena`` the rebase is unconditional and the argument is never
291+
NULL: a value whose lower 32 bits are zero arrives as the arena base
292+
address (arena offset 0). The kfunc must not check the argument for NULL.
293+
With ``__arena__nullable`` such a value arrives as NULL instead and the
294+
kfunc must check before dereferencing.
295+
296+
An example is given below::
297+
298+
__bpf_kfunc int bpf_process_item(struct item *item__arena)
299+
{
300+
...
301+
}
302+
303+
Calling such a kfunc requires the program to use an arena map and a JIT with
304+
arena argument support (currently x86-64); verification fails otherwise. The
305+
program can pass any value without compromising the kernel. A value that does
306+
not point into the arena is a program bug.
307+
308+
The suffixes have the same meaning on the arguments of struct_ops stub
309+
functions, with the conversion running in the opposite direction. The
310+
kernel caller passes the kernel arena address and the trampoline converts
311+
it while saving the arguments, so the callback receives an arena pointer
312+
it can dereference directly. With ``__arena`` the kernel caller must not
313+
pass NULL. With ``__arena__nullable`` a NULL kernel pointer arrives as NULL.
314+
However, there is no obligation to prove to the verifier that such a pointer is
315+
non-NULL before use, in-line with existing semantics of arena pointers used in
316+
a program (or obtained from any other source).
317+
281318
.. _BPF_kfunc_nodef:
282319

283320
2.4 Using an existing kernel function
@@ -522,6 +559,8 @@ In order to accommodate such requirements, the verifier will enforce strict
522559
PTR_TO_BTF_ID type matching if two types have the exact same name, with one
523560
being suffixed with ``___init``.
524561

562+
.. _BPF_kfunc_arena_access:
563+
525564
2.8 Accessing arena memory through kfunc arguments
526565
--------------------------------------------------
527566

arch/x86/net/bpf_jit_comp.c

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,50 @@ static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
16781678
return 0;
16791679
}
16801680

1681+
/*
1682+
* Rebase the __arena args of a kfunc call to arena kernel addresses,
1683+
* rN = kern_vm_start + (u32)rN, with R12 holding kern_vm_start. A nullable
1684+
* arg preserves NULL by skipping the add, tested on the truncated value as
1685+
* arena NULL is offset 0. Return the number of emitted bytes.
1686+
*/
1687+
static int emit_kfunc_arena_args(struct bpf_prog *bpf_prog,
1688+
const struct bpf_insn *insn, u8 **pprog)
1689+
{
1690+
const struct btf_func_model *fm;
1691+
u8 *prog = *pprog;
1692+
u8 *start = prog;
1693+
int i;
1694+
1695+
fm = bpf_jit_find_kfunc_model(bpf_prog, insn);
1696+
if (!fm)
1697+
return -EINVAL;
1698+
1699+
for (i = 0; i < min_t(int, fm->nr_args, MAX_BPF_FUNC_REG_ARGS); i++) {
1700+
u8 flags = fm->arg_flags[i];
1701+
u32 reg = BPF_REG_1 + i;
1702+
1703+
if (!(flags & BTF_FMODEL_ARENA_ARG))
1704+
continue;
1705+
if (WARN_ON_ONCE(!bpf_prog->aux->arena))
1706+
return -EINVAL;
1707+
1708+
/* mov eN, eN: truncate and clear the upper 32 bits */
1709+
emit_mov_reg(&prog, false, reg, reg);
1710+
if (flags & BTF_FMODEL_NULLABLE_ARG) {
1711+
/* test eN, eN; jz over the 3-byte add */
1712+
maybe_emit_mod(&prog, reg, reg, false);
1713+
EMIT2(0x85, add_2reg(0xC0, reg, reg));
1714+
EMIT2(X86_JE, 3);
1715+
}
1716+
/* add rN, r12 */
1717+
maybe_emit_mod(&prog, reg, X86_REG_R12, true);
1718+
EMIT2(0x01, add_2reg(0xC0, reg, X86_REG_R12));
1719+
}
1720+
1721+
*pprog = prog;
1722+
return prog - start;
1723+
}
1724+
16811725
static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *addrs, u8 *image,
16821726
u8 *rw_image, int oldproglen, struct jit_context *ctx, bool jmp_padding)
16831727
{
@@ -2588,6 +2632,12 @@ st: insn_off = insn->off;
25882632
}
25892633
if (!imm32)
25902634
return -EINVAL;
2635+
if (src_reg == BPF_PSEUDO_KFUNC_CALL) {
2636+
err = emit_kfunc_arena_args(bpf_prog, insn, &prog);
2637+
if (err < 0)
2638+
return err;
2639+
ip += err;
2640+
}
25912641
if (priv_frame_ptr) {
25922642
push_r9(&prog);
25932643
ip += 2;
@@ -2998,11 +3048,39 @@ static int get_nr_used_regs(const struct btf_func_model *m)
29983048
return nr_used_regs;
29993049
}
30003050

3051+
/*
3052+
* Convert an arena kernel address into the arena pointer form on its way
3053+
* into the BPF ctx, rax = (u32)(src - kern_vm_start). A nullable arg
3054+
* preserves NULL, tested on the full 64-bit kernel pointer. The 32-bit
3055+
* subtraction both truncates and clears the upper half, so the stored
3056+
* value satisfies the JIT invariant for arena pointer registers.
3057+
*/
3058+
static void emit_arena_arg_conv(u8 **pprog, u32 src_reg, bool nullable, u32 base_lo)
3059+
{
3060+
u8 *prog = *pprog;
3061+
3062+
if (nullable) {
3063+
if (src_reg != BPF_REG_0)
3064+
emit_mov_reg(&prog, true, BPF_REG_0, src_reg);
3065+
/* test rax, rax; jz over the 5-byte sub */
3066+
EMIT3(0x48, 0x85, 0xC0);
3067+
EMIT2(X86_JE, 5);
3068+
} else if (src_reg != BPF_REG_0) {
3069+
emit_mov_reg(&prog, false, BPF_REG_0, src_reg);
3070+
}
3071+
/* sub eax, base_lo */
3072+
EMIT1_off32(0x2D, base_lo);
3073+
3074+
*pprog = prog;
3075+
}
3076+
30013077
static void save_args(const struct btf_func_model *m, u8 **prog,
3002-
int stack_size, bool for_call_origin, u32 flags)
3078+
int stack_size, bool for_call_origin, u32 flags,
3079+
u64 arena_base)
30033080
{
30043081
int arg_regs, first_off = 0, nr_regs = 0, nr_stack_slots = 0;
30053082
bool use_jmp = bpf_trampoline_use_jmp(flags);
3083+
int stack_args_off = (use_jmp || (flags & BPF_TRAMP_F_INDIRECT)) ? 16 : 24;
30063084
int i, j;
30073085

30083086
/* Store function arguments to stack.
@@ -3011,6 +3089,9 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
30113089
* mov QWORD PTR [rbp-0x8],rsi
30123090
*/
30133091
for (i = 0; i < min_t(int, m->nr_args, MAX_BPF_FUNC_ARGS); i++) {
3092+
bool arena_arg = arena_base && (m->arg_flags[i] & BTF_FMODEL_ARENA_ARG);
3093+
bool nullable = m->arg_flags[i] & BTF_FMODEL_NULLABLE_ARG;
3094+
30143095
arg_regs = (m->arg_size[i] + 7) / 8;
30153096

30163097
/* According to the research of Yonghong, struct members
@@ -3034,16 +3115,19 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
30343115
/* copy function arguments from origin stack frame
30353116
* into current stack frame.
30363117
*
3037-
* The starting address of the arguments on-stack
3038-
* is:
3039-
* rbp + 8(push rbp) +
3040-
* 8(return addr of origin call) +
3041-
* 8(return addr of the caller)
3042-
* which means: rbp + 24
3118+
* The arguments on-stack start above the saved rbp
3119+
* and the return addresses: two return addresses
3120+
* (origin call and caller) when the trampoline is
3121+
* entered through the fentry call, so rbp + 24, and
3122+
* a single one when it is entered with a jmp or
3123+
* called indirectly, so rbp + 16.
30433124
*/
30443125
for (j = 0; j < arg_regs; j++) {
30453126
emit_ldx(prog, BPF_DW, BPF_REG_0, BPF_REG_FP,
3046-
nr_stack_slots * 8 + 16 + (!use_jmp) * 8);
3127+
nr_stack_slots * 8 + stack_args_off);
3128+
if (arena_arg)
3129+
emit_arena_arg_conv(prog, BPF_REG_0, nullable,
3130+
(u32)arena_base);
30473131
emit_stx(prog, BPF_DW, BPF_REG_FP, BPF_REG_0,
30483132
-stack_size);
30493133

@@ -3064,9 +3148,13 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
30643148

30653149
/* copy the arguments from regs into stack */
30663150
for (j = 0; j < arg_regs; j++) {
3067-
emit_stx(prog, BPF_DW, BPF_REG_FP,
3068-
nr_regs == 5 ? X86_REG_R9 : BPF_REG_1 + nr_regs,
3069-
-stack_size);
3151+
u32 src = nr_regs == 5 ? X86_REG_R9 : BPF_REG_1 + nr_regs;
3152+
3153+
if (arena_arg) {
3154+
emit_arena_arg_conv(prog, src, nullable, (u32)arena_base);
3155+
src = BPF_REG_0;
3156+
}
3157+
emit_stx(prog, BPF_DW, BPF_REG_FP, src, -stack_size);
30703158
stack_size -= 8;
30713159
nr_regs++;
30723160
}
@@ -3362,6 +3450,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
33623450
void *orig_call = func_addr;
33633451
int cookie_off, cookie_cnt;
33643452
u8 **branches = NULL;
3453+
u64 arena_base;
33653454
u64 func_meta;
33663455
u8 *prog;
33673456
bool save_ret;
@@ -3374,6 +3463,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
33743463
WARN_ON_ONCE((flags & BPF_TRAMP_F_INDIRECT) &&
33753464
(flags & ~(BPF_TRAMP_F_INDIRECT | BPF_TRAMP_F_RET_FENTRY_RET)));
33763465

3466+
arena_base = bpf_tramp_arena_base(m, tnodes, flags);
3467+
33773468
for (i = 0; i < m->nr_args; i++)
33783469
nr_regs += (m->arg_size[i] + 7) / 8 - 1;
33793470

@@ -3508,7 +3599,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
35083599
emit_store_stack_imm64(&prog, BPF_REG_0, -ip_off, (long)func_addr);
35093600
}
35103601

3511-
save_args(m, &prog, regs_off, false, flags);
3602+
save_args(m, &prog, regs_off, false, flags, arena_base);
35123603

35133604
if (flags & BPF_TRAMP_F_CALL_ORIG) {
35143605
/* arg1: mov rdi, im */
@@ -3550,7 +3641,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
35503641

35513642
if (flags & BPF_TRAMP_F_CALL_ORIG) {
35523643
restore_regs(m, &prog, regs_off);
3553-
save_args(m, &prog, arg_stack_off, true, flags);
3644+
save_args(m, &prog, arg_stack_off, true, flags, 0);
35543645

35553646
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) {
35563647
/* Before calling the original function, load the
@@ -4051,6 +4142,11 @@ bool bpf_jit_supports_stack_args(void)
40514142
return true;
40524143
}
40534144

4145+
bool bpf_jit_supports_arena_args(void)
4146+
{
4147+
return true;
4148+
}
4149+
40544150
void *bpf_arch_text_copy(void *dst, void *src, size_t len)
40554151
{
40564152
if (text_poke_copy(dst, src, len) == NULL)

include/linux/bpf.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,12 @@ struct bpf_prog_offload {
11951195
/* The argument is signed. */
11961196
#define BTF_FMODEL_SIGNED_ARG BIT(1)
11971197

1198+
/* The argument is an arena pointer. */
1199+
#define BTF_FMODEL_ARENA_ARG BIT(2)
1200+
1201+
/* The argument is nullable. */
1202+
#define BTF_FMODEL_NULLABLE_ARG BIT(3)
1203+
11981204
struct btf_func_model {
11991205
u8 ret_size;
12001206
u8 ret_flags;
@@ -1268,6 +1274,15 @@ struct bpf_tramp_nodes {
12681274
int nr_nodes;
12691275
};
12701276

1277+
/*
1278+
* The arena base against which a struct_ops trampoline converts the
1279+
* arguments marked with BTF_FMODEL_ARENA_ARG while saving them into the BPF
1280+
* ctx, ctx[arg] = (u32)(kaddr - kern_vm_start). Zero when the trampoline
1281+
* converts nothing.
1282+
*/
1283+
u64 bpf_tramp_arena_base(const struct btf_func_model *m,
1284+
struct bpf_tramp_nodes *tnodes, u32 flags);
1285+
12711286
struct bpf_tramp_run_ctx;
12721287

12731288
/* Different use cases for BPF trampoline:

include/linux/bpf_verifier.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,8 +1172,8 @@ static inline void bpf_trampoline_unpack_key(u64 key, u32 *obj_id, u32 *btf_id)
11721172
*btf_id = key & 0x7FFFFFFF;
11731173
}
11741174

1175-
int bpf_check_btf_info_early(struct bpf_verifier_env *env,
1176-
const union bpf_attr *attr, bpfptr_t uattr);
1175+
int bpf_prepare_btf_info(struct bpf_verifier_env *env,
1176+
const union bpf_attr *attr, bpfptr_t uattr);
11771177
int bpf_check_btf_info(struct bpf_verifier_env *env,
11781178
const union bpf_attr *attr, bpfptr_t uattr);
11791179

@@ -1297,6 +1297,16 @@ static inline u32 type_flag(u32 type)
12971297
return type & ~BPF_BASE_TYPE_MASK;
12981298
}
12991299

1300+
static inline bool bpf_prog_has_arena_ctx_arg(const struct bpf_prog *prog)
1301+
{
1302+
int i;
1303+
1304+
for (i = 0; i < prog->aux->ctx_arg_info_size; i++)
1305+
if (base_type(prog->aux->ctx_arg_info[i].reg_type) == PTR_TO_ARENA)
1306+
return true;
1307+
return false;
1308+
}
1309+
13001310
static inline enum bpf_prog_type resolve_prog_type(const struct bpf_prog *prog)
13011311
{
13021312
return (prog->type == BPF_PROG_TYPE_EXT && prog->aux->saved_dst_prog_type) ?

include/linux/filter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,7 @@ bool bpf_jit_supports_subprog_tailcalls(void);
12141214
bool bpf_jit_supports_percpu_insn(void);
12151215
bool bpf_jit_supports_kfunc_call(void);
12161216
bool bpf_jit_supports_stack_args(void);
1217+
bool bpf_jit_supports_arena_args(void);
12171218
bool bpf_jit_supports_far_kfunc_call(void);
12181219
bool bpf_jit_supports_exceptions(void);
12191220
bool bpf_jit_supports_ptr_xchg(void);

0 commit comments

Comments
 (0)