Commit dfeb8f4c authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'verifier-fixes'



Daniel Borkmann says:

====================
The series contains two fixes in BPF core and test cases. For details
please see individual patches. Thanks!
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 36153532 832c6f2c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -51,6 +51,9 @@ struct bpf_reg_state {
		 *   PTR_TO_MAP_VALUE_OR_NULL
		 */
		struct bpf_map *map_ptr;

		/* Max size from any of the above. */
		unsigned long raw;
	};
	/* Fixed part of pointer offset, pointer types only */
	s32 off;
+12 −9
Original line number Diff line number Diff line
@@ -2852,10 +2852,6 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
		regs[BPF_REG_0].type = NOT_INIT;
	} else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL ||
		   fn->ret_type == RET_PTR_TO_MAP_VALUE) {
		if (fn->ret_type == RET_PTR_TO_MAP_VALUE)
			regs[BPF_REG_0].type = PTR_TO_MAP_VALUE;
		else
			regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL;
		/* There is no offset yet applied, variable or fixed */
		mark_reg_known_zero(env, regs, BPF_REG_0);
		/* remember map_ptr, so that check_map_access()
@@ -2868,7 +2864,12 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
			return -EINVAL;
		}
		regs[BPF_REG_0].map_ptr = meta.map_ptr;
		if (fn->ret_type == RET_PTR_TO_MAP_VALUE) {
			regs[BPF_REG_0].type = PTR_TO_MAP_VALUE;
		} else {
			regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL;
			regs[BPF_REG_0].id = ++env->id_gen;
		}
	} else if (fn->ret_type == RET_PTR_TO_SOCKET_OR_NULL) {
		int id = acquire_reference_state(env, insn_idx);
		if (id < 0)
@@ -3046,7 +3047,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
			dst_reg->umax_value = umax_ptr;
			dst_reg->var_off = ptr_reg->var_off;
			dst_reg->off = ptr_reg->off + smin_val;
			dst_reg->range = ptr_reg->range;
			dst_reg->raw = ptr_reg->raw;
			break;
		}
		/* A new variable offset is created.  Note that off_reg->off
@@ -3076,10 +3077,11 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
		}
		dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off);
		dst_reg->off = ptr_reg->off;
		dst_reg->raw = ptr_reg->raw;
		if (reg_is_pkt_pointer(ptr_reg)) {
			dst_reg->id = ++env->id_gen;
			/* something was added to pkt_ptr, set range to zero */
			dst_reg->range = 0;
			dst_reg->raw = 0;
		}
		break;
	case BPF_SUB:
@@ -3108,7 +3110,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
			dst_reg->var_off = ptr_reg->var_off;
			dst_reg->id = ptr_reg->id;
			dst_reg->off = ptr_reg->off - smin_val;
			dst_reg->range = ptr_reg->range;
			dst_reg->raw = ptr_reg->raw;
			break;
		}
		/* A new variable offset is created.  If the subtrahend is known
@@ -3134,11 +3136,12 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
		}
		dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off);
		dst_reg->off = ptr_reg->off;
		dst_reg->raw = ptr_reg->raw;
		if (reg_is_pkt_pointer(ptr_reg)) {
			dst_reg->id = ++env->id_gen;
			/* something was added to pkt_ptr, set range to zero */
			if (smin_val < 0)
				dst_reg->range = 0;
				dst_reg->raw = 0;
		}
		break;
	case BPF_AND:
+290 −31
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ struct bpf_test {
	int fixup_percpu_cgroup_storage[MAX_FIXUPS];
	const char *errstr;
	const char *errstr_unpriv;
	uint32_t retval;
	uint32_t retval, retval_unpriv;
	enum {
		UNDEF,
		ACCEPT,
@@ -3084,6 +3084,8 @@ static struct bpf_test tests[] = {
		.fixup_prog1 = { 2 },
		.result = ACCEPT,
		.retval = 42,
		/* Verifier rewrite for unpriv skips tail call here. */
		.retval_unpriv = 2,
	},
	{
		"stack pointer arithmetic",
@@ -6454,6 +6456,256 @@ static struct bpf_test tests[] = {
		.errstr = "R1 min value is negative",
		.prog_type = BPF_PROG_TYPE_TRACEPOINT,
	},
	{
		"map access: known scalar += value_ptr",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
			BPF_MOV64_IMM(BPF_REG_1, 4),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0),
			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = ACCEPT,
		.retval = 1,
	},
	{
		"map access: value_ptr += known scalar",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
			BPF_MOV64_IMM(BPF_REG_1, 4),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = ACCEPT,
		.retval = 1,
	},
	{
		"map access: unknown scalar += value_ptr",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xf),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0),
			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = ACCEPT,
		.retval = 1,
	},
	{
		"map access: value_ptr += unknown scalar",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xf),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = ACCEPT,
		.retval = 1,
	},
	{
		"map access: value_ptr += value_ptr",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_0),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = REJECT,
		.errstr = "R0 pointer += pointer prohibited",
	},
	{
		"map access: known scalar -= value_ptr",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
			BPF_MOV64_IMM(BPF_REG_1, 4),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_0),
			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = REJECT,
		.errstr = "R1 tried to subtract pointer from scalar",
	},
	{
		"map access: value_ptr -= known scalar",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
			BPF_MOV64_IMM(BPF_REG_1, 4),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_1),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = REJECT,
		.errstr = "R0 min value is outside of the array range",
	},
	{
		"map access: value_ptr -= known scalar, 2",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
			BPF_MOV64_IMM(BPF_REG_1, 6),
			BPF_MOV64_IMM(BPF_REG_2, 4),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_2),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = ACCEPT,
		.retval = 1,
	},
	{
		"map access: unknown scalar -= value_ptr",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xf),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_0),
			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = REJECT,
		.errstr = "R1 tried to subtract pointer from scalar",
	},
	{
		"map access: value_ptr -= unknown scalar",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xf),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_1),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = REJECT,
		.errstr = "R0 min value is negative",
	},
	{
		"map access: value_ptr -= unknown scalar, 2",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xf),
			BPF_ALU64_IMM(BPF_OR, BPF_REG_1, 0x7),
			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0x7),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_1),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = ACCEPT,
		.retval = 1,
	},
	{
		"map access: value_ptr -= value_ptr",
		.insns = {
			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
			BPF_LD_MAP_FD(BPF_REG_1, 0),
			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
				     BPF_FUNC_map_lookup_elem),
			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
			BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_0),
			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
			BPF_MOV64_IMM(BPF_REG_0, 1),
			BPF_EXIT_INSN(),
		},
		.fixup_map_array_48b = { 3 },
		.result = REJECT,
		.errstr = "R0 invalid mem access 'inv'",
		.errstr_unpriv = "R0 pointer -= pointer prohibited",
	},
	{
		"map lookup helper access to map",
		.insns = {
@@ -13899,6 +14151,33 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_map_type prog_type,
	}
}

static int set_admin(bool admin)
{
	cap_t caps;
	const cap_value_t cap_val = CAP_SYS_ADMIN;
	int ret = -1;

	caps = cap_get_proc();
	if (!caps) {
		perror("cap_get_proc");
		return -1;
	}
	if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_val,
				admin ? CAP_SET : CAP_CLEAR)) {
		perror("cap_set_flag");
		goto out;
	}
	if (cap_set_proc(caps)) {
		perror("cap_set_proc");
		goto out;
	}
	ret = 0;
out:
	if (cap_free(caps))
		perror("cap_free");
	return ret;
}

static void do_test_single(struct bpf_test *test, bool unpriv,
			   int *passes, int *errors)
{
@@ -13907,6 +14186,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
	struct bpf_insn *prog = test->insns;
	int map_fds[MAX_NR_MAPS];
	const char *expected_err;
	uint32_t expected_val;
	uint32_t retval;
	int i, err;

@@ -13926,6 +14206,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
		       test->result_unpriv : test->result;
	expected_err = unpriv && test->errstr_unpriv ?
		       test->errstr_unpriv : test->errstr;
	expected_val = unpriv && test->retval_unpriv ?
		       test->retval_unpriv : test->retval;

	reject_from_alignment = fd_prog < 0 &&
				(test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS) &&
@@ -13959,16 +14241,20 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
		__u8 tmp[TEST_DATA_LEN << 2];
		__u32 size_tmp = sizeof(tmp);

		if (unpriv)
			set_admin(true);
		err = bpf_prog_test_run(fd_prog, 1, test->data,
					sizeof(test->data), tmp, &size_tmp,
					&retval, NULL);
		if (unpriv)
			set_admin(false);
		if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
			printf("Unexpected bpf_prog_test_run error\n");
			goto fail_log;
		}
		if (!err && retval != test->retval &&
		    test->retval != POINTER_VALUE) {
			printf("FAIL retval %d != %d\n", retval, test->retval);
		if (!err && retval != expected_val &&
		    expected_val != POINTER_VALUE) {
			printf("FAIL retval %d != %d\n", retval, expected_val);
			goto fail_log;
		}
	}
@@ -14011,33 +14297,6 @@ static bool is_admin(void)
	return (sysadmin == CAP_SET);
}

static int set_admin(bool admin)
{
	cap_t caps;
	const cap_value_t cap_val = CAP_SYS_ADMIN;
	int ret = -1;

	caps = cap_get_proc();
	if (!caps) {
		perror("cap_get_proc");
		return -1;
	}
	if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_val,
				admin ? CAP_SET : CAP_CLEAR)) {
		perror("cap_set_flag");
		goto out;
	}
	if (cap_set_proc(caps)) {
		perror("cap_set_proc");
		goto out;
	}
	ret = 0;
out:
	if (cap_free(caps))
		perror("cap_free");
	return ret;
}

static void get_unpriv_disabled()
{
	char buf[2];