Skip to content

Commit ee20a0a

Browse files
Xu KuohaiKernel Patches Daemon
Xu Kuohai
authored and
Kernel Patches Daemon
committed
bpf, arm64: Support up to 12 function arguments
Currently ARM64 bpf trampoline supports up to 8 function arguments. According to the statistics from commit 473e315 ("bpf, x86: allow function arguments up to 12 for TRACING"), there are about 200 functions accept 9 to 12 arguments, so adding support for up to 12 function arguments. Due to bpf only supporting function arguments up to 16 bytes, according to AAPCS64, starting from the first argument, each argument is first attempted to be loaded to 1 or 2 smallest registers from x0-x7, if there are no enough registers to hold the entire argument, then all remaining arguments starting from this one are pushed to the stack for passing. There are some non-trivial cases for which it is not possible to correctly read arguments from/write arguments to the stack: for example struct variables may have custom packing/alignment attributes that are invisible in BTF info. Such cases are denied for now to make sure not to read incorrect values. Signed-off-by: Xu Kuohai <[email protected]> Co-developed-by: Alexis Lothoré (eBPF Foundation) <[email protected]> Signed-off-by: Alexis Lothoré (eBPF Foundation) <[email protected]>
1 parent f4bae1b commit ee20a0a

File tree

1 file changed

+180
-54
lines changed

1 file changed

+180
-54
lines changed

arch/arm64/net/bpf_jit_comp.c

Lines changed: 180 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,7 +2064,7 @@ bool bpf_jit_supports_subprog_tailcalls(void)
20642064
}
20652065

20662066
static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
2067-
int args_off, int retval_off, int run_ctx_off,
2067+
int bargs_off, int retval_off, int run_ctx_off,
20682068
bool save_ret)
20692069
{
20702070
__le32 *branch;
@@ -2106,7 +2106,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
21062106
branch = ctx->image + ctx->idx;
21072107
emit(A64_NOP, ctx);
21082108

2109-
emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx);
2109+
emit(A64_ADD_I(1, A64_R(0), A64_SP, bargs_off), ctx);
21102110
if (!p->jited)
21112111
emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx);
21122112

@@ -2131,7 +2131,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
21312131
}
21322132

21332133
static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
2134-
int args_off, int retval_off, int run_ctx_off,
2134+
int bargs_off, int retval_off, int run_ctx_off,
21352135
__le32 **branches)
21362136
{
21372137
int i;
@@ -2141,7 +2141,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
21412141
*/
21422142
emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx);
21432143
for (i = 0; i < tl->nr_links; i++) {
2144-
invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off,
2144+
invoke_bpf_prog(ctx, tl->links[i], bargs_off, retval_off,
21452145
run_ctx_off, true);
21462146
/* if (*(u64 *)(sp + retval_off) != 0)
21472147
* goto do_fexit;
@@ -2155,23 +2155,134 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
21552155
}
21562156
}
21572157

2158-
static void save_args(struct jit_ctx *ctx, int args_off, int nregs)
2158+
struct arg_aux {
2159+
/* how many args are passed through registers, the rest of the args are
2160+
* passed through stack
2161+
*/
2162+
int args_in_regs;
2163+
/* how many registers are used to pass arguments */
2164+
int regs_for_args;
2165+
/* how much stack is used for additional args passed to bpf program
2166+
* that did not fit in original function registers
2167+
**/
2168+
int bstack_for_args;
2169+
/* home much stack is used for additional args passed to the
2170+
* original function when called from trampoline (this one needs
2171+
* arguments to be properly aligned)
2172+
*/
2173+
int ostack_for_args;
2174+
};
2175+
2176+
static int calc_arg_aux(const struct btf_func_model *m,
2177+
struct arg_aux *a)
21592178
{
2160-
int i;
2179+
int stack_slots, nregs, slots, i;
2180+
2181+
/* verifier ensures m->nr_args <= MAX_BPF_FUNC_ARGS */
2182+
for (i = 0, nregs = 0; i < m->nr_args; i++) {
2183+
slots = (m->arg_size[i] + 7) / 8;
2184+
if (nregs + slots <= 8) /* passed through register ? */
2185+
nregs += slots;
2186+
else
2187+
break;
2188+
}
2189+
2190+
a->args_in_regs = i;
2191+
a->regs_for_args = nregs;
2192+
a->ostack_for_args = 0;
2193+
2194+
/* the rest arguments are passed through stack */
2195+
for (a->ostack_for_args = 0, a->bstack_for_args = 0;
2196+
i < m->nr_args; i++) {
2197+
/* We can not know for sure about exact alignment needs for
2198+
* struct passed on stack, so deny those
2199+
*/
2200+
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
2201+
return -EOPNOTSUPP;
2202+
stack_slots = (m->arg_size[i] + 7) / 8;
2203+
/* AAPCS 64 C.14: arguments passed on stack must be aligned to
2204+
* max(8, arg_natural_alignment)
2205+
*/
2206+
a->bstack_for_args += stack_slots * 8;
2207+
a->ostack_for_args = round_up(a->ostack_for_args + stack_slots * 8, 8);
2208+
}
21612209

2162-
for (i = 0; i < nregs; i++) {
2163-
emit(A64_STR64I(i, A64_SP, args_off), ctx);
2164-
args_off += 8;
2210+
return 0;
2211+
}
2212+
2213+
static void clear_garbage(struct jit_ctx *ctx, int reg, int effective_bytes)
2214+
{
2215+
if (effective_bytes) {
2216+
int garbage_bits = 64 - 8 * effective_bytes;
2217+
#ifdef CONFIG_CPU_BIG_ENDIAN
2218+
/* garbage bits are at the right end */
2219+
emit(A64_LSR(1, reg, reg, garbage_bits), ctx);
2220+
emit(A64_LSL(1, reg, reg, garbage_bits), ctx);
2221+
#else
2222+
/* garbage bits are at the left end */
2223+
emit(A64_LSL(1, reg, reg, garbage_bits), ctx);
2224+
emit(A64_LSR(1, reg, reg, garbage_bits), ctx);
2225+
#endif
21652226
}
21662227
}
21672228

2168-
static void restore_args(struct jit_ctx *ctx, int args_off, int nregs)
2229+
static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
2230+
const struct btf_func_model *m,
2231+
const struct arg_aux *a,
2232+
bool for_call_origin)
21692233
{
21702234
int i;
2235+
int reg;
2236+
int doff;
2237+
int soff;
2238+
int slots;
2239+
u8 tmp = bpf2a64[TMP_REG_1];
2240+
2241+
/* store arguments to the stack for the bpf program, or restore
2242+
* arguments from stack for the original function
2243+
*/
2244+
for (reg = 0; reg < a->regs_for_args; reg++) {
2245+
emit(for_call_origin ?
2246+
A64_LDR64I(reg, A64_SP, bargs_off) :
2247+
A64_STR64I(reg, A64_SP, bargs_off),
2248+
ctx);
2249+
bargs_off += 8;
2250+
}
2251+
2252+
soff = 32; /* on stack arguments start from FP + 32 */
2253+
doff = (for_call_origin ? oargs_off : bargs_off);
2254+
2255+
/* save on stack arguments */
2256+
for (i = a->args_in_regs; i < m->nr_args; i++) {
2257+
slots = (m->arg_size[i] + 7) / 8;
2258+
/* AAPCS C.14: additional arguments on stack must be
2259+
* aligned on max(8, arg_natural_alignment)
2260+
*/
2261+
soff = round_up(soff, 8);
2262+
if (for_call_origin)
2263+
doff = round_up(doff, 8);
2264+
/* verifier ensures arg_size <= 16, so slots equals 1 or 2 */
2265+
while (slots-- > 0) {
2266+
emit(A64_LDR64I(tmp, A64_FP, soff), ctx);
2267+
/* if there is unused space in the last slot, clear
2268+
* the garbage contained in the space.
2269+
*/
2270+
if (slots == 0 && !for_call_origin)
2271+
clear_garbage(ctx, tmp, m->arg_size[i] % 8);
2272+
emit(A64_STR64I(tmp, A64_SP, doff), ctx);
2273+
soff += 8;
2274+
doff += 8;
2275+
}
2276+
}
2277+
}
2278+
2279+
static void restore_args(struct jit_ctx *ctx, int bargs_off, int nregs)
2280+
{
2281+
int reg;
21712282

2172-
for (i = 0; i < nregs; i++) {
2173-
emit(A64_LDR64I(i, A64_SP, args_off), ctx);
2174-
args_off += 8;
2283+
for (reg = 0; reg < nregs; reg++) {
2284+
emit(A64_LDR64I(reg, A64_SP, bargs_off), ctx);
2285+
bargs_off += 8;
21752286
}
21762287
}
21772288

@@ -2194,17 +2305,21 @@ static bool is_struct_ops_tramp(const struct bpf_tramp_links *fentry_links)
21942305
*/
21952306
static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
21962307
struct bpf_tramp_links *tlinks, void *func_addr,
2197-
int nregs, u32 flags)
2308+
const struct btf_func_model *m,
2309+
const struct arg_aux *a,
2310+
u32 flags)
21982311
{
21992312
int i;
22002313
int stack_size;
22012314
int retaddr_off;
22022315
int regs_off;
22032316
int retval_off;
2204-
int args_off;
2205-
int nregs_off;
2317+
int bargs_off;
2318+
int nfuncargs_off;
22062319
int ip_off;
22072320
int run_ctx_off;
2321+
int oargs_off;
2322+
int nfuncargs;
22082323
struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];
22092324
struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
22102325
struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
@@ -2213,31 +2328,38 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
22132328
bool is_struct_ops = is_struct_ops_tramp(fentry);
22142329

22152330
/* trampoline stack layout:
2216-
* [ parent ip ]
2217-
* [ FP ]
2218-
* SP + retaddr_off [ self ip ]
2219-
* [ FP ]
2331+
* [ parent ip ]
2332+
* [ FP ]
2333+
* SP + retaddr_off [ self ip ]
2334+
* [ FP ]
22202335
*
2221-
* [ padding ] align SP to multiples of 16
2336+
* [ padding ] align SP to multiples of 16
22222337
*
2223-
* [ x20 ] callee saved reg x20
2224-
* SP + regs_off [ x19 ] callee saved reg x19
2338+
* [ x20 ] callee saved reg x20
2339+
* SP + regs_off [ x19 ] callee saved reg x19
22252340
*
2226-
* SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or
2227-
* BPF_TRAMP_F_RET_FENTRY_RET
2341+
* SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or
2342+
* BPF_TRAMP_F_RET_FENTRY_RET
2343+
* [ arg reg N ]
2344+
* [ ... ]
2345+
* SP + bargs_off [ arg reg 1 ] for bpf
22282346
*
2229-
* [ arg reg N ]
2230-
* [ ... ]
2231-
* SP + args_off [ arg reg 1 ]
2347+
* SP + nfuncargs_off [ arg regs count ]
22322348
*
2233-
* SP + nregs_off [ arg regs count ]
2349+
* SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
22342350
*
2235-
* SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
2351+
* SP + run_ctx_off [ bpf_tramp_run_ctx ]
22362352
*
2237-
* SP + run_ctx_off [ bpf_tramp_run_ctx ]
2353+
* [ stack arg N ]
2354+
* [ ... ]
2355+
* SP + oargs_off [ stack arg 1 ] for original func
22382356
*/
22392357

22402358
stack_size = 0;
2359+
oargs_off = stack_size;
2360+
if (flags & BPF_TRAMP_F_CALL_ORIG)
2361+
stack_size += a->ostack_for_args;
2362+
22412363
run_ctx_off = stack_size;
22422364
/* room for bpf_tramp_run_ctx */
22432365
stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8);
@@ -2247,13 +2369,14 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
22472369
if (flags & BPF_TRAMP_F_IP_ARG)
22482370
stack_size += 8;
22492371

2250-
nregs_off = stack_size;
2372+
nfuncargs_off = stack_size;
22512373
/* room for args count */
22522374
stack_size += 8;
22532375

2254-
args_off = stack_size;
2376+
bargs_off = stack_size;
22552377
/* room for args */
2256-
stack_size += nregs * 8;
2378+
nfuncargs = a->regs_for_args + a->bstack_for_args / 8;
2379+
stack_size += 8 * nfuncargs;
22572380

22582381
/* room for return value */
22592382
retval_off = stack_size;
@@ -2300,11 +2423,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
23002423
}
23012424

23022425
/* save arg regs count*/
2303-
emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx);
2304-
emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx);
2426+
emit(A64_MOVZ(1, A64_R(10), nfuncargs, 0), ctx);
2427+
emit(A64_STR64I(A64_R(10), A64_SP, nfuncargs_off), ctx);
23052428

2306-
/* save arg regs */
2307-
save_args(ctx, args_off, nregs);
2429+
/* save args for bpf */
2430+
save_args(ctx, bargs_off, oargs_off, m, a, false);
23082431

23092432
/* save callee saved registers */
23102433
emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx);
@@ -2320,7 +2443,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
23202443
}
23212444

23222445
for (i = 0; i < fentry->nr_links; i++)
2323-
invoke_bpf_prog(ctx, fentry->links[i], args_off,
2446+
invoke_bpf_prog(ctx, fentry->links[i], bargs_off,
23242447
retval_off, run_ctx_off,
23252448
flags & BPF_TRAMP_F_RET_FENTRY_RET);
23262449

@@ -2330,12 +2453,13 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
23302453
if (!branches)
23312454
return -ENOMEM;
23322455

2333-
invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off,
2456+
invoke_bpf_mod_ret(ctx, fmod_ret, bargs_off, retval_off,
23342457
run_ctx_off, branches);
23352458
}
23362459

23372460
if (flags & BPF_TRAMP_F_CALL_ORIG) {
2338-
restore_args(ctx, args_off, nregs);
2461+
/* save args for original func */
2462+
save_args(ctx, bargs_off, oargs_off, m, a, true);
23392463
/* call original func */
23402464
emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx);
23412465
emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx);
@@ -2354,7 +2478,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
23542478
}
23552479

23562480
for (i = 0; i < fexit->nr_links; i++)
2357-
invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off,
2481+
invoke_bpf_prog(ctx, fexit->links[i], bargs_off, retval_off,
23582482
run_ctx_off, false);
23592483

23602484
if (flags & BPF_TRAMP_F_CALL_ORIG) {
@@ -2368,7 +2492,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
23682492
}
23692493

23702494
if (flags & BPF_TRAMP_F_RESTORE_REGS)
2371-
restore_args(ctx, args_off, nregs);
2495+
restore_args(ctx, bargs_off, a->regs_for_args);
23722496

23732497
/* restore callee saved register x19 and x20 */
23742498
emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx);
@@ -2428,14 +2552,16 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
24282552
.idx = 0,
24292553
};
24302554
struct bpf_tramp_image im;
2555+
struct arg_aux aaux;
24312556
int nregs, ret;
24322557

24332558
nregs = btf_func_model_nregs(m);
2434-
/* the first 8 registers are used for arguments */
2435-
if (nregs > 8)
2436-
return -ENOTSUPP;
24372559

2438-
ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags);
2560+
ret = calc_arg_aux(m, &aaux);
2561+
if (ret < 0)
2562+
return ret;
2563+
2564+
ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, m, &aaux, flags);
24392565
if (ret < 0)
24402566
return ret;
24412567

@@ -2462,9 +2588,10 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image,
24622588
u32 flags, struct bpf_tramp_links *tlinks,
24632589
void *func_addr)
24642590
{
2465-
int ret, nregs;
2466-
void *image, *tmp;
24672591
u32 size = ro_image_end - ro_image;
2592+
struct arg_aux aaux;
2593+
void *image, *tmp;
2594+
int ret;
24682595

24692596
/* image doesn't need to be in module memory range, so we can
24702597
* use kvmalloc.
@@ -2480,13 +2607,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image,
24802607
.write = true,
24812608
};
24822609

2483-
nregs = btf_func_model_nregs(m);
2484-
/* the first 8 registers are used for arguments */
2485-
if (nregs > 8)
2486-
return -ENOTSUPP;
24872610

24882611
jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image));
2489-
ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags);
2612+
ret = calc_arg_aux(m, &aaux);
2613+
if (ret)
2614+
goto out;
2615+
ret = prepare_trampoline(&ctx, im, tlinks, func_addr, m, &aaux, flags);
24902616

24912617
if (ret > 0 && validate_code(&ctx) < 0) {
24922618
ret = -EINVAL;

0 commit comments

Comments
 (0)