diff options
author | Alexei Starovoitov <ast@kernel.org> | 2019-03-12 19:16:44 -0700 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2019-03-19 14:09:55 -0700 |
commit | 1bc6bf983183a2727a39d250941da09f6fb48372 (patch) | |
tree | fc68e9255e10221a931032bc007ce86907bdb736 | |
parent | c55c8edafa91139419ed011f7d036274ce96be0b (diff) | |
download | bpf-verif_scale.tar.gz |
bpf: increase program instruction limitverif_scale
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r-- | include/linux/bpf.h | 2 | ||||
-rw-r--r-- | include/linux/bpf_verifier.h | 6 | ||||
-rw-r--r-- | kernel/bpf/syscall.c | 5 | ||||
-rw-r--r-- | kernel/bpf/verifier.c | 72 | ||||
-rw-r--r-- | tools/lib/bpf/libbpf.c | 11 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c | 18 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/progs/test_verif_scale.c | 118 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_progs.c | 5 |
8 files changed, 221 insertions, 16 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f02367faa58dbe..df053b36d5b9dc 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -420,6 +420,8 @@ struct bpf_array { }; }; +//#define BPF_COMPLEXITY_LIMIT_INSNS 131072 +#define BPF_COMPLEXITY_LIMIT_INSNS 1310720 #define MAX_TAIL_CALL_CNT 32 struct bpf_event_entry { diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 7d8228d1c8981d..97f938aaa06b23 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -207,6 +207,7 @@ struct bpf_verifier_state { struct bpf_verifier_state_list { struct bpf_verifier_state state; struct bpf_verifier_state_list *next; + int miss_cnt, hit_cnt; }; /* Possible states for alu_state member. */ @@ -284,6 +285,11 @@ struct bpf_verifier_env { struct bpf_verifier_log log; struct bpf_subprog_info subprog_info[BPF_MAX_SUBPROGS + 1]; u32 subprog_cnt; + u32 insn_processed; + u64 verification_time; + u32 max_states_per_insn; + u32 total_states; + u32 peak_states; }; __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log, diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 62f6bced3a3c48..d3579281668d3f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1549,7 +1549,8 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) /* eBPF programs must be GPL compatible to use GPL-ed functions */ is_gpl = license_is_gpl_compatible(license); - if (attr->insn_cnt == 0 || attr->insn_cnt > BPF_MAXINSNS) + if (attr->insn_cnt == 0 || + attr->insn_cnt > (capable(CAP_SYS_ADMIN) ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) return -E2BIG; if (type != BPF_PROG_TYPE_SOCKET_FILTER && type != BPF_PROG_TYPE_CGROUP_SKB && @@ -1609,6 +1610,8 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) /* run eBPF verifier */ err = bpf_check(&prog, attr, uattr); if (err < 0) + printk("bpf_check %d\n", err); + if (err < 0) goto free_used_maps; prog = bpf_prog_select_runtime(prog, &err); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 86f9cd5d1c4e1a..3376a6860ed7f0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -176,7 +176,6 @@ struct bpf_verifier_stack_elem { struct bpf_verifier_stack_elem *next; }; -#define BPF_COMPLEXITY_LIMIT_INSNS 131072 #define BPF_COMPLEXITY_LIMIT_STACK 1024 #define BPF_COMPLEXITY_LIMIT_STATES 64 @@ -281,6 +280,10 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...) struct bpf_verifier_env *env = private_data; va_list args; +/* va_start(args, fmt); + vprintk(fmt, args); + va_end(args);*/ + if (!bpf_verifier_log_needed(&env->log)) return; @@ -448,8 +451,8 @@ static void print_verifier_state(struct bpf_verifier_env *env, tnum_is_const(reg->var_off)) { /* reg->off should be 0 for SCALAR_VALUE */ verbose(env, "%lld", reg->var_off.value + reg->off); - if (t == PTR_TO_STACK) - verbose(env, ",call_%d", func(env, reg)->callsite); +/* if (t == PTR_TO_STACK) + verbose(env, ",call_%d", func(env, reg)->callsite);*/ } else { verbose(env, "(id=%d ref_obj_id=%d", reg->id, reg->ref_obj_id); @@ -6100,11 +6103,13 @@ static int propagate_liveness(struct bpf_verifier_env *env, static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) { struct bpf_verifier_state_list *new_sl; - struct bpf_verifier_state_list *sl; + struct bpf_verifier_state_list *sl, **pprev; struct bpf_verifier_state *cur = env->cur_state, *new; int i, j, err, states_cnt = 0; - sl = env->explored_states[insn_idx]; + pprev= &env->explored_states[insn_idx]; + sl = *pprev; + if (!sl) /* this 'insn_idx' instruction wasn't marked, so we will not * be doing state search here @@ -6115,6 +6120,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) while (sl != STATE_LIST_MARK) { if (states_equal(env, &sl->state, cur)) { + sl->hit_cnt++; /* reached equivalent register/stack state, * prune the search. * Registers read by the continuation are read by us. @@ -6130,10 +6136,44 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) return err; return 1; } - sl = sl->next; states_cnt++; + sl->miss_cnt++; + if (sl->miss_cnt > sl->hit_cnt * 3 + 3) { + *pprev = sl->next; +/* print_verifier_state(env, sl->state.frame[sl->state.curframe]);*/ + if (sl->state.frame[0]->regs[0].live & REG_LIVE_DONE) { + free_verifier_state(&sl->state, false); + kfree(sl); + env->peak_states--; + } else { + printk("Leaking\n"); + } + sl = *pprev; + continue; + } + pprev = &sl->next; + sl = *pprev; } + if (env->max_states_per_insn < states_cnt) + env->max_states_per_insn = states_cnt; + +/* if (states_cnt >= 50) { + sl = env->explored_states[insn_idx]; + printk("insn %5d states_cnt %d\n", insn_idx, states_cnt); + states_cnt = 0; + while (sl != STATE_LIST_MARK) { + verbose(env, "%3d: ", states_cnt++); + print_verifier_state(env, sl->state.frame[sl->state.curframe]); + if (sl->state.curframe == 1) { + verbose(env, " : "); + print_verifier_state(env, sl->state.frame[0]); + } + sl = sl->next; + } + return 0; + }*/ + if (!env->allow_ptr_leaks && states_cnt > BPF_COMPLEXITY_LIMIT_STATES) return 0; @@ -6147,6 +6187,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) new_sl = kzalloc(sizeof(struct bpf_verifier_state_list), GFP_KERNEL); if (!new_sl) return -ENOMEM; + env->total_states++; + env->peak_states++; /* add new state to the head of linked list */ new = &new_sl->state; @@ -6232,7 +6274,6 @@ static int do_check(struct bpf_verifier_env *env) struct bpf_insn *insns = env->prog->insnsi; struct bpf_reg_state *regs; int insn_cnt = env->prog->len, i; - int insn_processed = 0; bool do_print_state = false; env->prev_linfo = NULL; @@ -6267,10 +6308,10 @@ static int do_check(struct bpf_verifier_env *env) insn = &insns[env->insn_idx]; class = BPF_CLASS(insn->code); - if (++insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) { + if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) { verbose(env, "BPF program is too large. Processed %d insn\n", - insn_processed); + env->insn_processed); return -E2BIG; } @@ -6575,7 +6616,7 @@ process_bpf_exit: } verbose(env, "processed %d insns (limit %d), stack depth ", - insn_processed, BPF_COMPLEXITY_LIMIT_INSNS); + env->insn_processed, BPF_COMPLEXITY_LIMIT_INSNS); for (i = 0; i < env->subprog_cnt; i++) { u32 depth = env->subprog_info[i].stack_depth; @@ -7810,6 +7851,7 @@ static void free_states(struct bpf_verifier_env *env) int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, union bpf_attr __user *uattr) { + u64 start_time = ktime_get_ns(); struct bpf_verifier_env *env; struct bpf_verifier_log *log; int i, len, ret = -EINVAL; @@ -7851,7 +7893,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, ret = -EINVAL; /* log attributes have to be sane */ - if (log->len_total < 128 || log->len_total > UINT_MAX >> 8 || + if (log->len_total < 128 || log->len_total > UINT_MAX >> 1 || !log->level || !log->ubuf) goto err_unlock; } @@ -7933,6 +7975,14 @@ skip_full_check: if (ret == 0) ret = fixup_call_args(env); + env->verification_time = ktime_get_ns() - start_time; + printk("processed %d insns (limit %d) time %lld usec max_states_per_insn %d total_states %d peak_states %d\n", + env->insn_processed, BPF_COMPLEXITY_LIMIT_INSNS, + env->verification_time / 1000, + env->max_states_per_insn, + env->total_states, + env->peak_states); + if (log->level && bpf_verifier_log_full(log)) ret = -ENOSPC; if (log->level && !log->ubuf) { diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 5e977d2688da79..398eeba00cd34e 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1489,6 +1489,7 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, { struct bpf_load_program_attr load_attr; char *cp, errmsg[STRERR_BUFSIZE]; + int log_buf_size = BPF_LOG_BUF_SIZE; char *log_buf; int ret; @@ -1512,11 +1513,12 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, if (!load_attr.insns || !load_attr.insns_cnt) return -EINVAL; - log_buf = malloc(BPF_LOG_BUF_SIZE); +retry_load: + log_buf = malloc(log_buf_size); if (!log_buf) pr_warning("Alloc log buffer for bpf loader error, continue without log\n"); - ret = bpf_load_program_xattr(&load_attr, log_buf, BPF_LOG_BUF_SIZE); + ret = bpf_load_program_xattr(&load_attr, log_buf, log_buf_size); if (ret >= 0) { *pfd = ret; @@ -1524,6 +1526,11 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, goto out; } + if (errno == ENOSPC) { + log_buf_size <<= 1; + free(log_buf); + goto retry_load; + } ret = -LIBBPF_ERRNO__LOAD; cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); pr_warning("load bpf program failed: %s\n", cp); diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c new file mode 100644 index 00000000000000..d67ffbd9e93c3c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> + +void test_bpf_verif_scale(void) +{ + const char *file = "./test_verif_scale.o"; + struct bpf_object *obj; + int err, prog_fd; + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) { + error_cnt++; + return; + } + + printf("test_verif_scale:OK\n"); + bpf_object__close(obj); +} diff --git a/tools/testing/selftests/bpf/progs/test_verif_scale.c b/tools/testing/selftests/bpf/progs/test_verif_scale.c new file mode 100644 index 00000000000000..24e8bef3a986d8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_verif_scale.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <linux/pkt_cls.h> +#include <linux/bpf.h> +#include <linux/in.h> +#include <linux/if_ether.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/icmp.h> +#include <linux/icmpv6.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include "bpf_helpers.h" +#include "test_iptunnel_common.h" +#include "bpf_endian.h" + +int _version SEC("version") = 1; + +static __attribute__((always_inline)) __u32 rol32(__u32 word, unsigned int shift) +{ + return (word << shift) | (word >> ((-shift) & 31)); +} + +/* copy paste of jhash from kernel sources to make sure llvm + * can compile it into valid sequence of bpf instructions + */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= c; a ^= rol32(c, 4); c += b; \ + b -= a; b ^= rol32(a, 6); a += c; \ + c -= b; c ^= rol32(b, 8); b += a; \ + a -= c; a ^= rol32(c, 16); c += b; \ + b -= a; b ^= rol32(a, 19); a += c; \ + c -= b; c ^= rol32(b, 4); b += a; \ +} + +#define __jhash_final(a, b, c) \ +{ \ + c ^= b; c -= rol32(b, 14); \ + a ^= c; a -= rol32(c, 11); \ + b ^= a; b -= rol32(a, 25); \ + c ^= b; c -= rol32(b, 16); \ + a ^= c; a -= rol32(c, 4); \ + b ^= a; b -= rol32(a, 14); \ + c ^= b; c -= rol32(b, 24); \ +} + +#define JHASH_INITVAL 0xdeadbeef + +typedef unsigned int u32; + +static __attribute__((noinline)) +//static __attribute__((always_inline)) +u32 jhash(const void *key, u32 length, u32 initval) +{ + u32 a, b, c; + const unsigned char *k = key; + + a = b = c = JHASH_INITVAL + length + initval; + + while (length > 12) { + a += *(volatile u32 *)(k); + b += *(volatile u32 *)(k + 4); + c += *(volatile u32 *)(k + 8); + __jhash_mix(a, b, c); + length -= 12; + k += 12; + } + switch (length) { + case 12: c += (u32)k[11]<<24; + case 11: c += (u32)k[10]<<16; + case 10: c += (u32)k[9]<<8; + case 9: c += k[8]; + case 8: b += (u32)k[7]<<24; + case 7: b += (u32)k[6]<<16; + case 6: b += (u32)k[5]<<8; + case 5: b += k[4]; + case 4: a += (u32)k[3]<<24; + case 3: a += (u32)k[2]<<16; + case 2: a += (u32)k[1]<<8; + case 1: a += k[0]; + c ^= a; + __jhash_final(a, b, c); + case 0: /* Nothing left to add */ + break; + } + + return c; +} + +SEC("l4lb-demo") +int balancer_ingress(struct __sk_buff *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + void *ptr; + int ret = 0, nh_off, i = 0; + + nh_off = sizeof(struct ethhdr); + + /* pragma unroll doesn't work on large loops */ + +#define C do { \ + ptr = data + i; \ + if (ptr + nh_off > data_end) \ + break; \ + ctx->tc_index = jhash(ptr, nh_off, ctx->cb[0] + i++); \ + } while (0); +#define C30 C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C; +#define C10 C30;C30;C30;C30;C30;C30;C30;C30;C30;C30; + C30;C30;C30; /* 300 calls */ +// C10; + return 0; +} +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 5d10aee9e27744..3120c7e148fd9f 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -168,9 +168,10 @@ int main(void) jit_enabled = is_jit_enabled(); -#define CALL +test_bpf_verif_scale(); +/*#define CALL #include <prog_tests/tests.h> -#undef CALL +#undef CALL*/ printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; |