Skip to content

Commit 2cd3e37

Browse files
Peter ZijlstraAlexei Starovoitov
authored andcommitted
x86/cfi,bpf: Fix bpf_struct_ops CFI
BPF struct_ops uses __arch_prepare_bpf_trampoline() to write trampolines for indirect function calls. These tramplines much have matching CFI. In order to obtain the correct CFI hash for the various methods, add a matching structure that contains stub functions, the compiler will generate correct CFI which we can pilfer for the trampolines. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent e72d88d commit 2cd3e37

File tree

7 files changed

+191
-32
lines changed

7 files changed

+191
-32
lines changed

arch/x86/include/asm/cfi.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,19 @@ static inline int cfi_get_offset(void)
123123
}
124124
#define cfi_get_offset cfi_get_offset
125125

126+
extern u32 cfi_get_func_hash(void *func);
127+
126128
#else
127129
static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
128130
{
129131
return BUG_TRAP_TYPE_NONE;
130132
}
131133
#define cfi_bpf_hash 0U
132134
#define cfi_bpf_subprog_hash 0U
135+
static inline u32 cfi_get_func_hash(void *func)
136+
{
137+
return 0;
138+
}
133139
#endif /* CONFIG_CFI_CLANG */
134140

135141
#endif /* _ASM_X86_CFI_H */

arch/x86/kernel/alternative.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,28 @@ asm (
883883
" .size cfi_bpf_subprog_hash, 4 \n"
884884
" .popsection \n"
885885
);
886+
887+
u32 cfi_get_func_hash(void *func)
888+
{
889+
u32 hash;
890+
891+
func -= cfi_get_offset();
892+
switch (cfi_mode) {
893+
case CFI_FINEIBT:
894+
func += 7;
895+
break;
896+
case CFI_KCFI:
897+
func += 1;
898+
break;
899+
default:
900+
return 0;
901+
}
902+
903+
if (get_kernel_nofault(hash, func))
904+
return 0;
905+
906+
return hash;
907+
}
886908
#endif
887909

888910
#ifdef CONFIG_FINEIBT

arch/x86/net/bpf_jit_comp.c

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,8 @@ static void pop_callee_regs(u8 **pprog, bool *callee_regs_used)
312312
* in arch/x86/kernel/alternative.c
313313
*/
314314

315-
static void emit_fineibt(u8 **pprog, bool is_subprog)
315+
static void emit_fineibt(u8 **pprog, u32 hash)
316316
{
317-
u32 hash = is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash;
318317
u8 *prog = *pprog;
319318

320319
EMIT_ENDBR();
@@ -327,9 +326,8 @@ static void emit_fineibt(u8 **pprog, bool is_subprog)
327326
*pprog = prog;
328327
}
329328

330-
static void emit_kcfi(u8 **pprog, bool is_subprog)
329+
static void emit_kcfi(u8 **pprog, u32 hash)
331330
{
332-
u32 hash = is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash;
333331
u8 *prog = *pprog;
334332

335333
EMIT1_off32(0xb8, hash); /* movl $hash, %eax */
@@ -351,17 +349,17 @@ static void emit_kcfi(u8 **pprog, bool is_subprog)
351349
*pprog = prog;
352350
}
353351

354-
static void emit_cfi(u8 **pprog, bool is_subprog)
352+
static void emit_cfi(u8 **pprog, u32 hash)
355353
{
356354
u8 *prog = *pprog;
357355

358356
switch (cfi_mode) {
359357
case CFI_FINEIBT:
360-
emit_fineibt(&prog, is_subprog);
358+
emit_fineibt(&prog, hash);
361359
break;
362360

363361
case CFI_KCFI:
364-
emit_kcfi(&prog, is_subprog);
362+
emit_kcfi(&prog, hash);
365363
break;
366364

367365
default:
@@ -383,7 +381,7 @@ static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf,
383381
{
384382
u8 *prog = *pprog;
385383

386-
emit_cfi(&prog, is_subprog);
384+
emit_cfi(&prog, is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash);
387385
/* BPF trampoline can be made to work without these nops,
388386
* but let's waste 5 bytes for now and optimize later
389387
*/
@@ -2510,10 +2508,19 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
25102508
u8 *prog;
25112509
bool save_ret;
25122510

2511+
/*
2512+
* F_INDIRECT is only compatible with F_RET_FENTRY_RET, it is
2513+
* explicitly incompatible with F_CALL_ORIG | F_SKIP_FRAME | F_IP_ARG
2514+
* because @func_addr.
2515+
*/
2516+
WARN_ON_ONCE((flags & BPF_TRAMP_F_INDIRECT) &&
2517+
(flags & ~(BPF_TRAMP_F_INDIRECT | BPF_TRAMP_F_RET_FENTRY_RET)));
2518+
25132519
/* extra registers for struct arguments */
2514-
for (i = 0; i < m->nr_args; i++)
2520+
for (i = 0; i < m->nr_args; i++) {
25152521
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
25162522
nr_regs += (m->arg_size[i] + 7) / 8 - 1;
2523+
}
25172524

25182525
/* x86-64 supports up to MAX_BPF_FUNC_ARGS arguments. 1-6
25192526
* are passed through regs, the remains are through stack.
@@ -2596,20 +2603,27 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
25962603

25972604
prog = rw_image;
25982605

2599-
EMIT_ENDBR();
2600-
/*
2601-
* This is the direct-call trampoline, as such it needs accounting
2602-
* for the __fentry__ call.
2603-
*/
2604-
x86_call_depth_emit_accounting(&prog, NULL);
2606+
if (flags & BPF_TRAMP_F_INDIRECT) {
2607+
/*
2608+
* Indirect call for bpf_struct_ops
2609+
*/
2610+
emit_cfi(&prog, cfi_get_func_hash(func_addr));
2611+
} else {
2612+
/*
2613+
* Direct-call fentry stub, as such it needs accounting for the
2614+
* __fentry__ call.
2615+
*/
2616+
x86_call_depth_emit_accounting(&prog, NULL);
2617+
}
26052618
EMIT1(0x55); /* push rbp */
26062619
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
2607-
if (!is_imm8(stack_size))
2620+
if (!is_imm8(stack_size)) {
26082621
/* sub rsp, stack_size */
26092622
EMIT3_off32(0x48, 0x81, 0xEC, stack_size);
2610-
else
2623+
} else {
26112624
/* sub rsp, stack_size */
26122625
EMIT4(0x48, 0x83, 0xEC, stack_size);
2626+
}
26132627
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
26142628
EMIT1(0x50); /* push rax */
26152629
/* mov QWORD PTR [rbp - rbx_off], rbx */
@@ -2643,10 +2657,11 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
26432657
}
26442658
}
26452659

2646-
if (fentry->nr_links)
2660+
if (fentry->nr_links) {
26472661
if (invoke_bpf(m, &prog, fentry, regs_off, run_ctx_off,
26482662
flags & BPF_TRAMP_F_RET_FENTRY_RET, image, rw_image))
26492663
return -EINVAL;
2664+
}
26502665

26512666
if (fmod_ret->nr_links) {
26522667
branches = kcalloc(fmod_ret->nr_links, sizeof(u8 *),
@@ -2665,11 +2680,12 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
26652680
restore_regs(m, &prog, regs_off);
26662681
save_args(m, &prog, arg_stack_off, true);
26672682

2668-
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
2683+
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) {
26692684
/* Before calling the original function, restore the
26702685
* tail_call_cnt from stack to rax.
26712686
*/
26722687
RESTORE_TAIL_CALL_CNT(stack_size);
2688+
}
26732689

26742690
if (flags & BPF_TRAMP_F_ORIG_STACK) {
26752691
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, 8);
@@ -2698,17 +2714,19 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
26982714
/* Update the branches saved in invoke_bpf_mod_ret with the
26992715
* aligned address of do_fexit.
27002716
*/
2701-
for (i = 0; i < fmod_ret->nr_links; i++)
2717+
for (i = 0; i < fmod_ret->nr_links; i++) {
27022718
emit_cond_near_jump(&branches[i], image + (prog - (u8 *)rw_image),
27032719
image + (branches[i] - (u8 *)rw_image), X86_JNE);
2720+
}
27042721
}
27052722

2706-
if (fexit->nr_links)
2723+
if (fexit->nr_links) {
27072724
if (invoke_bpf(m, &prog, fexit, regs_off, run_ctx_off,
27082725
false, image, rw_image)) {
27092726
ret = -EINVAL;
27102727
goto cleanup;
27112728
}
2729+
}
27122730

27132731
if (flags & BPF_TRAMP_F_RESTORE_REGS)
27142732
restore_regs(m, &prog, regs_off);
@@ -2725,21 +2743,23 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
27252743
ret = -EINVAL;
27262744
goto cleanup;
27272745
}
2728-
} else if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
2746+
} else if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) {
27292747
/* Before running the original function, restore the
27302748
* tail_call_cnt from stack to rax.
27312749
*/
27322750
RESTORE_TAIL_CALL_CNT(stack_size);
2751+
}
27332752

27342753
/* restore return value of orig_call or fentry prog back into RAX */
27352754
if (save_ret)
27362755
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
27372756

27382757
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, -rbx_off);
27392758
EMIT1(0xC9); /* leave */
2740-
if (flags & BPF_TRAMP_F_SKIP_FRAME)
2759+
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
27412760
/* skip our return address and return to parent */
27422761
EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */
2762+
}
27432763
emit_return(&prog, image + (prog - (u8 *)rw_image));
27442764
/* Make sure the trampoline generation logic doesn't overflow */
27452765
if (WARN_ON_ONCE(prog > (u8 *)rw_image_end - BPF_INSN_SAFETY)) {

include/linux/bpf.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,17 @@ struct btf_func_model {
10601060
*/
10611061
#define BPF_TRAMP_F_TAIL_CALL_CTX BIT(7)
10621062

1063+
/*
1064+
* Indicate the trampoline should be suitable to receive indirect calls;
1065+
* without this indirectly calling the generated code can result in #UD/#CP,
1066+
* depending on the CFI options.
1067+
*
1068+
* Used by bpf_struct_ops.
1069+
*
1070+
* Incompatible with FENTRY usage, overloads @func_addr argument.
1071+
*/
1072+
#define BPF_TRAMP_F_INDIRECT BIT(8)
1073+
10631074
/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
10641075
* bytes on x86.
10651076
*/
@@ -1697,6 +1708,7 @@ struct bpf_struct_ops {
16971708
struct btf_func_model func_models[BPF_STRUCT_OPS_MAX_NR_MEMBERS];
16981709
u32 type_id;
16991710
u32 value_id;
1711+
void *cfi_stubs;
17001712
};
17011713

17021714
#if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL)
@@ -1710,6 +1722,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
17101722
int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks,
17111723
struct bpf_tramp_link *link,
17121724
const struct btf_func_model *model,
1725+
void *stub_func,
17131726
void *image, void *image_end);
17141727
static inline bool bpf_try_module_get(const void *data, struct module *owner)
17151728
{

kernel/bpf/bpf_struct_ops.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -352,25 +352,24 @@ const struct bpf_link_ops bpf_struct_ops_link_lops = {
352352
int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks,
353353
struct bpf_tramp_link *link,
354354
const struct btf_func_model *model,
355-
void *image, void *image_end)
355+
void *stub_func, void *image, void *image_end)
356356
{
357-
u32 flags;
357+
u32 flags = BPF_TRAMP_F_INDIRECT;
358358
int size;
359359

360360
tlinks[BPF_TRAMP_FENTRY].links[0] = link;
361361
tlinks[BPF_TRAMP_FENTRY].nr_links = 1;
362-
/* BPF_TRAMP_F_RET_FENTRY_RET is only used by bpf_struct_ops,
363-
* and it must be used alone.
364-
*/
365-
flags = model->ret_size > 0 ? BPF_TRAMP_F_RET_FENTRY_RET : 0;
362+
363+
if (model->ret_size > 0)
364+
flags |= BPF_TRAMP_F_RET_FENTRY_RET;
366365

367366
size = arch_bpf_trampoline_size(model, flags, tlinks, NULL);
368367
if (size < 0)
369368
return size;
370369
if (size > (unsigned long)image_end - (unsigned long)image)
371370
return -E2BIG;
372371
return arch_prepare_bpf_trampoline(NULL, image, image_end,
373-
model, flags, tlinks, NULL);
372+
model, flags, tlinks, stub_func);
374373
}
375374

376375
static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
@@ -504,11 +503,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
504503

505504
err = bpf_struct_ops_prepare_trampoline(tlinks, link,
506505
&st_ops->func_models[i],
506+
*(void **)(st_ops->cfi_stubs + moff),
507507
image, image_end);
508508
if (err < 0)
509509
goto reset_unlock;
510510

511-
*(void **)(kdata + moff) = image;
511+
*(void **)(kdata + moff) = image + cfi_get_offset();
512512
image += err;
513513

514514
/* put prog_id to udata */

net/bpf/bpf_dummy_struct_ops.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ extern struct bpf_struct_ops bpf_bpf_dummy_ops;
1212
/* A common type for test_N with return value in bpf_dummy_ops */
1313
typedef int (*dummy_ops_test_ret_fn)(struct bpf_dummy_ops_state *state, ...);
1414

15+
static int dummy_ops_test_ret_function(struct bpf_dummy_ops_state *state, ...)
16+
{
17+
return 0;
18+
}
19+
1520
struct bpf_dummy_ops_test_args {
1621
u64 args[MAX_BPF_FUNC_ARGS];
1722
struct bpf_dummy_ops_state state;
@@ -62,7 +67,7 @@ static int dummy_ops_copy_args(struct bpf_dummy_ops_test_args *args)
6267

6368
static int dummy_ops_call_op(void *image, struct bpf_dummy_ops_test_args *args)
6469
{
65-
dummy_ops_test_ret_fn test = (void *)image;
70+
dummy_ops_test_ret_fn test = (void *)image + cfi_get_offset();
6671
struct bpf_dummy_ops_state *state = NULL;
6772

6873
/* state needs to be NULL if args[0] is 0 */
@@ -119,6 +124,7 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
119124
op_idx = prog->expected_attach_type;
120125
err = bpf_struct_ops_prepare_trampoline(tlinks, link,
121126
&st_ops->func_models[op_idx],
127+
&dummy_ops_test_ret_function,
122128
image, image + PAGE_SIZE);
123129
if (err < 0)
124130
goto out;
@@ -219,6 +225,28 @@ static void bpf_dummy_unreg(void *kdata)
219225
{
220226
}
221227

228+
static int bpf_dummy_test_1(struct bpf_dummy_ops_state *cb)
229+
{
230+
return 0;
231+
}
232+
233+
static int bpf_dummy_test_2(struct bpf_dummy_ops_state *cb, int a1, unsigned short a2,
234+
char a3, unsigned long a4)
235+
{
236+
return 0;
237+
}
238+
239+
static int bpf_dummy_test_sleepable(struct bpf_dummy_ops_state *cb)
240+
{
241+
return 0;
242+
}
243+
244+
static struct bpf_dummy_ops __bpf_bpf_dummy_ops = {
245+
.test_1 = bpf_dummy_test_1,
246+
.test_2 = bpf_dummy_test_2,
247+
.test_sleepable = bpf_dummy_test_sleepable,
248+
};
249+
222250
struct bpf_struct_ops bpf_bpf_dummy_ops = {
223251
.verifier_ops = &bpf_dummy_verifier_ops,
224252
.init = bpf_dummy_init,
@@ -227,4 +255,5 @@ struct bpf_struct_ops bpf_bpf_dummy_ops = {
227255
.reg = bpf_dummy_reg,
228256
.unreg = bpf_dummy_unreg,
229257
.name = "bpf_dummy_ops",
258+
.cfi_stubs = &__bpf_bpf_dummy_ops,
230259
};

0 commit comments

Comments
 (0)