forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									468e2f64d2
								
							
						
					
					
						commit
						390ee7e29f
					
				
					 2 changed files with 112 additions and 0 deletions
				
			
		| 
						 | 
					@ -3073,6 +3073,43 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int check_return_code(struct bpf_verifier_env *env)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bpf_reg_state *reg;
 | 
				
			||||||
 | 
						struct tnum range = tnum_range(0, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (env->prog->type) {
 | 
				
			||||||
 | 
						case BPF_PROG_TYPE_CGROUP_SKB:
 | 
				
			||||||
 | 
						case BPF_PROG_TYPE_CGROUP_SOCK:
 | 
				
			||||||
 | 
						case BPF_PROG_TYPE_SOCK_OPS:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = &env->cur_state.regs[BPF_REG_0];
 | 
				
			||||||
 | 
						if (reg->type != SCALAR_VALUE) {
 | 
				
			||||||
 | 
							verbose("At program exit the register R0 is not a known value (%s)\n",
 | 
				
			||||||
 | 
								reg_type_str[reg->type]);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!tnum_in(range, reg->var_off)) {
 | 
				
			||||||
 | 
							verbose("At program exit the register R0 ");
 | 
				
			||||||
 | 
							if (!tnum_is_unknown(reg->var_off)) {
 | 
				
			||||||
 | 
								char tn_buf[48];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
 | 
				
			||||||
 | 
								verbose("has value %s", tn_buf);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								verbose("has unknown scalar value");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							verbose(" should have been 0 or 1\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* non-recursive DFS pseudo code
 | 
					/* non-recursive DFS pseudo code
 | 
				
			||||||
 * 1  procedure DFS-iterative(G,v):
 | 
					 * 1  procedure DFS-iterative(G,v):
 | 
				
			||||||
 * 2      label v as discovered
 | 
					 * 2      label v as discovered
 | 
				
			||||||
| 
						 | 
					@ -3863,6 +3900,9 @@ static int do_check(struct bpf_verifier_env *env)
 | 
				
			||||||
					return -EACCES;
 | 
										return -EACCES;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									err = check_return_code(env);
 | 
				
			||||||
 | 
									if (err)
 | 
				
			||||||
 | 
										return err;
 | 
				
			||||||
process_bpf_exit:
 | 
					process_bpf_exit:
 | 
				
			||||||
				insn_idx = pop_stack(env, &prev_insn_idx);
 | 
									insn_idx = pop_stack(env, &prev_insn_idx);
 | 
				
			||||||
				if (insn_idx < 0) {
 | 
									if (insn_idx < 0) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6892,6 +6892,78 @@ static struct bpf_test tests[] = {
 | 
				
			||||||
		.result = ACCEPT,
 | 
							.result = ACCEPT,
 | 
				
			||||||
		.prog_type = BPF_PROG_TYPE_XDP,
 | 
							.prog_type = BPF_PROG_TYPE_XDP,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test1",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.errstr = "R0 has value (0x0; 0xffffffff)",
 | 
				
			||||||
 | 
							.result = REJECT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test2",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
 | 
				
			||||||
 | 
								BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.result = ACCEPT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test3",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
 | 
				
			||||||
 | 
								BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 3),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.errstr = "R0 has value (0x0; 0x3)",
 | 
				
			||||||
 | 
							.result = REJECT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test4",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_MOV64_IMM(BPF_REG_0, 1),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.result = ACCEPT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test5",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_MOV64_IMM(BPF_REG_0, 2),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.errstr = "R0 has value (0x2; 0x0)",
 | 
				
			||||||
 | 
							.result = REJECT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test6",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.errstr = "R0 is not a known value (ctx)",
 | 
				
			||||||
 | 
							.result = REJECT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							"bpf_exit with invalid return code. test7",
 | 
				
			||||||
 | 
							.insns = {
 | 
				
			||||||
 | 
								BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
 | 
				
			||||||
 | 
								BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 4),
 | 
				
			||||||
 | 
								BPF_ALU64_REG(BPF_MUL, BPF_REG_0, BPF_REG_2),
 | 
				
			||||||
 | 
								BPF_EXIT_INSN(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.errstr = "R0 has unknown scalar value",
 | 
				
			||||||
 | 
							.result = REJECT,
 | 
				
			||||||
 | 
							.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int probe_filter_length(const struct bpf_insn *fp)
 | 
					static int probe_filter_length(const struct bpf_insn *fp)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue