History

bpf: Add per-program recursion prevention mechanism

Since both sleepable and non-sleepable programs execute under migrate_disable add recursion prevention mechanism to both types of programs when they're executed via bpf trampoline.

Alexei Starovoitov committed 5 years ago

Commit ca06f551 parent f2dd3b37 files · +50 −11Browse files

Unified · Split

7 files
  1. arch/x86/net/bpf_jit_comp.c +15 −0
  2. include/linux/bpf.h +3 −3
  3. include/linux/filter.h +1 −0
  4. kernel/bpf/core.c +8 −0
  5. kernel/bpf/trampoline.c +19 −4
  6. tools/testing/selftests/bpf/prog_tests/fexit_stress.c +2 −2
  7. tools/testing/selftests/bpf/prog_tests/trampoline_count.c +2 −2
arch/x86/net/bpf_jit_comp.c+15−0
@@ -1740,8 +1740,11 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, · 1739 unmodified lines
17401740 struct bpf_prog *p, int stack_size, bool mod_ret)
17411741 {
17421742 u8 *prog = *pprog;
1743+ u8 *jmp_insn;
17431744 int cnt = 0;
17441745
1746+ /* arg1: mov rdi, progs[i] */
1747+ emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p);
17451748 if (emit_call(&prog,
17461749 p->aux->sleepable ? __bpf_prog_enter_sleepable :
17471750 __bpf_prog_enter, prog))
@@ -1749,6 +1752,14 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, · 1 unmodified line
17491752 /* remember prog start time returned by __bpf_prog_enter */
17501753 emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0);
17511754
1755+ /* if (__bpf_prog_enter*(prog) == 0)
1756+ * goto skip_exec_of_prog;
1757+ */
1758+ EMIT3(0x48, 0x85, 0xC0); /* test rax,rax */
1759+ /* emit 2 nops that will be replaced with JE insn */
1760+ jmp_insn = prog;
1761+ emit_nops(&prog, 2);
1762+
17521763 /* arg1: lea rdi, [rbp - stack_size] */
17531764 EMIT4(0x48, 0x8D, 0x7D, -stack_size);
17541765 /* arg2: progs[i]->insnsi for interpreter */
@@ -1767,6 +1778,10 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, · 12 unmodified lines
17671778 if (mod_ret)
17681779 emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
17691780
1781+ /* replace 2 nops with JE insn, since jmp target is known */
1782+ jmp_insn[0] = X86_JE;
1783+ jmp_insn[1] = prog - jmp_insn - 2;
1784+
17701785 /* arg1: mov rdi, progs[i] */
17711786 emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p);
17721787 /* arg2: mov rsi, rbx <- start time in nsec */
include/linux/bpf.h+3−3
@@ -529,7 +529,7 @@ struct btf_func_model { · 528 unmodified lines
529529 /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
530530 * bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2
531531 */
532−#define BPF_MAX_TRAMP_PROGS 40
532+#define BPF_MAX_TRAMP_PROGS 38
533533
534534 struct bpf_tramp_progs {
535535 struct bpf_prog *progs[BPF_MAX_TRAMP_PROGS];
@@ -561,9 +561,9 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, · 25 unmodified lines
561561 struct bpf_tramp_progs *tprogs,
562562 void *orig_call);
563563 /* these two functions are called from generated trampoline */
564−u64 notrace __bpf_prog_enter(void);
564+u64 notrace __bpf_prog_enter(struct bpf_prog *prog);
565565 void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start);
566−u64 notrace __bpf_prog_enter_sleepable(void);
566+u64 notrace __bpf_prog_enter_sleepable(struct bpf_prog *prog);
567567 void notrace __bpf_prog_exit_sleepable(struct bpf_prog *prog, u64 start);
568568
569569 struct bpf_ksym {
include/linux/filter.h+1−0
@@ -565,6 +565,7 @@ struct bpf_prog { · 564 unmodified lines
565565 u32 jited_len; /* Size of jited insns in bytes */
566566 u8 tag[BPF_TAG_SIZE];
567567 struct bpf_prog_stats __percpu *stats;
568+ int __percpu *active;
568569 unsigned int (*bpf_func)(const void *ctx,
569570 const struct bpf_insn *insn);
570571 struct bpf_prog_aux *aux; /* Auxiliary fields */
kernel/bpf/core.c+8−0
@@ -91,6 +91,12 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag · 90 unmodified lines
9191 vfree(fp);
9292 return NULL;
9393 }
94+ fp->active = alloc_percpu_gfp(int, GFP_KERNEL_ACCOUNT | gfp_extra_flags);
95+ if (!fp->active) {
96+ vfree(fp);
97+ kfree(aux);
98+ return NULL;
99+ }
94100
95101 fp->pages = size / PAGE_SIZE;
96102 fp->aux = aux;
@@ -116,6 +122,7 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags) · 19 unmodified lines
116122
117123 prog->stats = alloc_percpu_gfp(struct bpf_prog_stats, gfp_flags);
118124 if (!prog->stats) {
125+ free_percpu(prog->active);
119126 kfree(prog->aux);
120127 vfree(prog);
121128 return NULL;
@@ -253,6 +260,7 @@ void __bpf_prog_free(struct bpf_prog *fp) · 131 unmodified lines
253260 kfree(fp->aux);
254261 }
255262 free_percpu(fp->stats);
263+ free_percpu(fp->active);
256264 vfree(fp);
257265 }
258266
kernel/bpf/trampoline.c+19−4
@@ -381,13 +381,16 @@ out: · 380 unmodified lines
381381 mutex_unlock(&trampoline_mutex);
382382 }
383383
384−#define NO_START_TIME 0
384+#define NO_START_TIME 1
385385 static u64 notrace bpf_prog_start_time(void)
386386 {
387387 u64 start = NO_START_TIME;
388388
389− if (static_branch_unlikely(&bpf_stats_enabled_key))
389+ if (static_branch_unlikely(&bpf_stats_enabled_key)) {
390390 start = sched_clock();
391+ if (unlikely(!start))
392+ start = NO_START_TIME;
393+ }
391394 return start;
392395 }
393396
@@ -397,12 +400,20 @@ static u64 notrace bpf_prog_start_time(void) · 3 unmodified lines
397400 * call __bpf_prog_enter
398401 * call prog->bpf_func
399402 * call __bpf_prog_exit
403+ *
404+ * __bpf_prog_enter returns:
405+ * 0 - skip execution of the bpf prog
406+ * 1 - execute bpf prog
407+ * [2..MAX_U64] - excute bpf prog and record execution time.
408+ * This is start time.
400409 */
401−u64 notrace __bpf_prog_enter(void)
410+u64 notrace __bpf_prog_enter(struct bpf_prog *prog)
402411 __acquires(RCU)
403412 {
404413 rcu_read_lock();
405414 migrate_disable();
415+ if (unlikely(__this_cpu_inc_return(*(prog->active)) != 1))
416+ return 0;
406417 return bpf_prog_start_time();
407418 }
408419
@@ -430,21 +441,25 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start) · 21 unmodified lines
430441 __releases(RCU)
431442 {
432443 update_prog_stats(prog, start);
444+ __this_cpu_dec(*(prog->active));
433445 migrate_enable();
434446 rcu_read_unlock();
435447 }
436448
437−u64 notrace __bpf_prog_enter_sleepable(void)
449+u64 notrace __bpf_prog_enter_sleepable(struct bpf_prog *prog)
438450 {
439451 rcu_read_lock_trace();
440452 migrate_disable();
441453 might_fault();
454+ if (unlikely(__this_cpu_inc_return(*(prog->active)) != 1))
455+ return 0;
442456 return bpf_prog_start_time();
443457 }
444458
445459 void notrace __bpf_prog_exit_sleepable(struct bpf_prog *prog, u64 start)
446460 {
447461 update_prog_stats(prog, start);
462+ __this_cpu_dec(*(prog->active));
448463 migrate_enable();
449464 rcu_read_unlock_trace();
450465 }
tools/testing/selftests/bpf/prog_tests/fexit_stress.c+2−2
@@ -2,8 +2,8 @@ · 1 unmodified line
22 /* Copyright (c) 2019 Facebook */
33 #include <test_progs.h>
44
5−/* x86-64 fits 55 JITed and 43 interpreted progs into half page */
6−#define CNT 40
5+/* that's kernel internal BPF_MAX_TRAMP_PROGS define */
6+#define CNT 38
77
88 void test_fexit_stress(void)
99 {
tools/testing/selftests/bpf/prog_tests/trampoline_count.c+2−2
@@ -4,7 +4,7 @@ · 3 unmodified lines
44 #include <sys/prctl.h>
55 #include <test_progs.h>
66
7−#define MAX_TRAMP_PROGS 40
7+#define MAX_TRAMP_PROGS 38
88
99 struct inst {
1010 struct bpf_object *obj;
@@ -52,7 +52,7 @@ void test_trampoline_count(void) · 41 unmodified lines
5252 struct bpf_link *link;
5353 char comm[16] = {};
5454
55− /* attach 'allowed' 40 trampoline programs */
55+ /* attach 'allowed' trampoline programs */
5656 for (i = 0; i < MAX_TRAMP_PROGS; i++) {
5757 obj = bpf_object__open_file(object, NULL);
5858 if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj))) {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t