diff options
author | Alexei Starovoitov <ast@fb.com> | 2017-10-03 01:50:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-10-04 19:05:05 -0400 |
commit | 390ee7e29fc8e6e90d3065b00afb047c4ee552f9 (patch) | |
tree | 9a5d9870bb4cf67905cb638cbe1b3063251a100f | |
parent | 468e2f64d220fe2dc11caa2bcb9b3a1e50fc7321 (diff) |
bpf: enforce return code for cgroup-bpf programs
with addition of tnum logic the verifier got smart enough and
we can enforce return codes at program load time.
For now do so for cgroup-bpf program types.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | kernel/bpf/verifier.c | 40 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_verifier.c | 72 |
2 files changed, 112 insertions, 0 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 4cf9b72c59a0..52b022310f6a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
@@ -3073,6 +3073,43 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) | |||
3073 | return 0; | 3073 | return 0; |
3074 | } | 3074 | } |
3075 | 3075 | ||
3076 | static int check_return_code(struct bpf_verifier_env *env) | ||
3077 | { | ||
3078 | struct bpf_reg_state *reg; | ||
3079 | struct tnum range = tnum_range(0, 1); | ||
3080 | |||
3081 | switch (env->prog->type) { | ||
3082 | case BPF_PROG_TYPE_CGROUP_SKB: | ||
3083 | case BPF_PROG_TYPE_CGROUP_SOCK: | ||
3084 | case BPF_PROG_TYPE_SOCK_OPS: | ||
3085 | break; | ||
3086 | default: | ||
3087 | return 0; | ||
3088 | } | ||
3089 | |||
3090 | reg = &env->cur_state.regs[BPF_REG_0]; | ||
3091 | if (reg->type != SCALAR_VALUE) { | ||
3092 | verbose("At program exit the register R0 is not a known value (%s)\n", | ||
3093 | reg_type_str[reg->type]); | ||
3094 | return -EINVAL; | ||
3095 | } | ||
3096 | |||
3097 | if (!tnum_in(range, reg->var_off)) { | ||
3098 | verbose("At program exit the register R0 "); | ||
3099 | if (!tnum_is_unknown(reg->var_off)) { | ||
3100 | char tn_buf[48]; | ||
3101 | |||
3102 | tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); | ||
3103 | verbose("has value %s", tn_buf); | ||
3104 | } else { | ||
3105 | verbose("has unknown scalar value"); | ||
3106 | } | ||
3107 | verbose(" should have been 0 or 1\n"); | ||
3108 | return -EINVAL; | ||
3109 | } | ||
3110 | return 0; | ||
3111 | } | ||
3112 | |||
3076 | /* non-recursive DFS pseudo code | 3113 | /* non-recursive DFS pseudo code |
3077 | * 1 procedure DFS-iterative(G,v): | 3114 | * 1 procedure DFS-iterative(G,v): |
3078 | * 2 label v as discovered | 3115 | * 2 label v as discovered |
@@ -3863,6 +3900,9 @@ static int do_check(struct bpf_verifier_env *env) | |||
3863 | return -EACCES; | 3900 | return -EACCES; |
3864 | } | 3901 | } |
3865 | 3902 | ||
3903 | err = check_return_code(env); | ||
3904 | if (err) | ||
3905 | return err; | ||
3866 | process_bpf_exit: | 3906 | process_bpf_exit: |
3867 | insn_idx = pop_stack(env, &prev_insn_idx); | 3907 | insn_idx = pop_stack(env, &prev_insn_idx); |
3868 | if (insn_idx < 0) { | 3908 | if (insn_idx < 0) { |
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 290d5056c165..cc91d0159f43 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c | |||
@@ -6892,6 +6892,78 @@ static struct bpf_test tests[] = { | |||
6892 | .result = ACCEPT, | 6892 | .result = ACCEPT, |
6893 | .prog_type = BPF_PROG_TYPE_XDP, | 6893 | .prog_type = BPF_PROG_TYPE_XDP, |
6894 | }, | 6894 | }, |
6895 | { | ||
6896 | "bpf_exit with invalid return code. test1", | ||
6897 | .insns = { | ||
6898 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), | ||
6899 | BPF_EXIT_INSN(), | ||
6900 | }, | ||
6901 | .errstr = "R0 has value (0x0; 0xffffffff)", | ||
6902 | .result = REJECT, | ||
6903 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6904 | }, | ||
6905 | { | ||
6906 | "bpf_exit with invalid return code. test2", | ||
6907 | .insns = { | ||
6908 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), | ||
6909 | BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1), | ||
6910 | BPF_EXIT_INSN(), | ||
6911 | }, | ||
6912 | .result = ACCEPT, | ||
6913 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6914 | }, | ||
6915 | { | ||
6916 | "bpf_exit with invalid return code. test3", | ||
6917 | .insns = { | ||
6918 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), | ||
6919 | BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 3), | ||
6920 | BPF_EXIT_INSN(), | ||
6921 | }, | ||
6922 | .errstr = "R0 has value (0x0; 0x3)", | ||
6923 | .result = REJECT, | ||
6924 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6925 | }, | ||
6926 | { | ||
6927 | "bpf_exit with invalid return code. test4", | ||
6928 | .insns = { | ||
6929 | BPF_MOV64_IMM(BPF_REG_0, 1), | ||
6930 | BPF_EXIT_INSN(), | ||
6931 | }, | ||
6932 | .result = ACCEPT, | ||
6933 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6934 | }, | ||
6935 | { | ||
6936 | "bpf_exit with invalid return code. test5", | ||
6937 | .insns = { | ||
6938 | BPF_MOV64_IMM(BPF_REG_0, 2), | ||
6939 | BPF_EXIT_INSN(), | ||
6940 | }, | ||
6941 | .errstr = "R0 has value (0x2; 0x0)", | ||
6942 | .result = REJECT, | ||
6943 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6944 | }, | ||
6945 | { | ||
6946 | "bpf_exit with invalid return code. test6", | ||
6947 | .insns = { | ||
6948 | BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), | ||
6949 | BPF_EXIT_INSN(), | ||
6950 | }, | ||
6951 | .errstr = "R0 is not a known value (ctx)", | ||
6952 | .result = REJECT, | ||
6953 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6954 | }, | ||
6955 | { | ||
6956 | "bpf_exit with invalid return code. test7", | ||
6957 | .insns = { | ||
6958 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), | ||
6959 | BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 4), | ||
6960 | BPF_ALU64_REG(BPF_MUL, BPF_REG_0, BPF_REG_2), | ||
6961 | BPF_EXIT_INSN(), | ||
6962 | }, | ||
6963 | .errstr = "R0 has unknown scalar value", | ||
6964 | .result = REJECT, | ||
6965 | .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, | ||
6966 | }, | ||
6895 | }; | 6967 | }; |
6896 | 6968 | ||
6897 | static int probe_filter_length(const struct bpf_insn *fp) | 6969 | static int probe_filter_length(const struct bpf_insn *fp) |