Commit f79e7ea5 authored by Lorenz Bauer's avatar Lorenz Bauer Committed by Alexei Starovoitov
Browse files

bpf: Use a table to drive helper arg type checks



The mapping between bpf_arg_type and bpf_reg_type is encoded in a big
hairy if statement that is hard to follow. The debug output also leaves
to be desired: if a reg_type doesn't match we only print one of the
options, instead printing all the valid ones.

Convert the if statement into a table which is then used to drive type
checking. If none of the reg_types match we print all options, e.g.:

    R2 type=rdonly_buf expected=fp, pkt, pkt_meta, map_value

Signed-off-by: default avatarLorenz Bauer <lmb@cloudflare.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarMartin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20200921121227.255763-12-lmb@cloudflare.com
parent fd1b0d60
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -292,6 +292,7 @@ enum bpf_arg_type {
	ARG_PTR_TO_ALLOC_MEM,	/* pointer to dynamically allocated memory */
	ARG_PTR_TO_ALLOC_MEM,	/* pointer to dynamically allocated memory */
	ARG_PTR_TO_ALLOC_MEM_OR_NULL,	/* pointer to dynamically allocated memory or NULL */
	ARG_PTR_TO_ALLOC_MEM_OR_NULL,	/* pointer to dynamically allocated memory or NULL */
	ARG_CONST_ALLOC_SIZE_OR_ZERO,	/* number of allocated bytes requested */
	ARG_CONST_ALLOC_SIZE_OR_ZERO,	/* number of allocated bytes requested */
	__BPF_ARG_TYPE_MAX,
};
};


/* type of values returned from helper functions */
/* type of values returned from helper functions */
+109 −74
Original line number Original line Diff line number Diff line
@@ -3903,12 +3903,6 @@ static bool arg_type_is_mem_size(enum bpf_arg_type type)
	       type == ARG_CONST_SIZE_OR_ZERO;
	       type == ARG_CONST_SIZE_OR_ZERO;
}
}


static bool arg_type_is_alloc_mem_ptr(enum bpf_arg_type type)
{
	return type == ARG_PTR_TO_ALLOC_MEM ||
	       type == ARG_PTR_TO_ALLOC_MEM_OR_NULL;
}

static bool arg_type_is_alloc_size(enum bpf_arg_type type)
static bool arg_type_is_alloc_size(enum bpf_arg_type type)
{
{
	return type == ARG_CONST_ALLOC_SIZE_OR_ZERO;
	return type == ARG_CONST_ALLOC_SIZE_OR_ZERO;
@@ -3957,14 +3951,115 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
	return 0;
	return 0;
}
}


struct bpf_reg_types {
	const enum bpf_reg_type types[10];
};

static const struct bpf_reg_types map_key_value_types = {
	.types = {
		PTR_TO_STACK,
		PTR_TO_PACKET,
		PTR_TO_PACKET_META,
		PTR_TO_MAP_VALUE,
	},
};

static const struct bpf_reg_types sock_types = {
	.types = {
		PTR_TO_SOCK_COMMON,
		PTR_TO_SOCKET,
		PTR_TO_TCP_SOCK,
		PTR_TO_XDP_SOCK,
	},
};

static const struct bpf_reg_types mem_types = {
	.types = {
		PTR_TO_STACK,
		PTR_TO_PACKET,
		PTR_TO_PACKET_META,
		PTR_TO_MAP_VALUE,
		PTR_TO_MEM,
		PTR_TO_RDONLY_BUF,
		PTR_TO_RDWR_BUF,
	},
};

static const struct bpf_reg_types int_ptr_types = {
	.types = {
		PTR_TO_STACK,
		PTR_TO_PACKET,
		PTR_TO_PACKET_META,
		PTR_TO_MAP_VALUE,
	},
};

static const struct bpf_reg_types fullsock_types = { .types = { PTR_TO_SOCKET } };
static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } };
static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } };
static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM } };
static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } };
static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } };
static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } };

static const struct bpf_reg_types *compatible_reg_types[] = {
	[ARG_PTR_TO_MAP_KEY]		= &map_key_value_types,
	[ARG_PTR_TO_MAP_VALUE]		= &map_key_value_types,
	[ARG_PTR_TO_UNINIT_MAP_VALUE]	= &map_key_value_types,
	[ARG_PTR_TO_MAP_VALUE_OR_NULL]	= &map_key_value_types,
	[ARG_CONST_SIZE]		= &scalar_types,
	[ARG_CONST_SIZE_OR_ZERO]	= &scalar_types,
	[ARG_CONST_ALLOC_SIZE_OR_ZERO]	= &scalar_types,
	[ARG_CONST_MAP_PTR]		= &const_map_ptr_types,
	[ARG_PTR_TO_CTX]		= &context_types,
	[ARG_PTR_TO_CTX_OR_NULL]	= &context_types,
	[ARG_PTR_TO_SOCK_COMMON]	= &sock_types,
	[ARG_PTR_TO_SOCKET]		= &fullsock_types,
	[ARG_PTR_TO_SOCKET_OR_NULL]	= &fullsock_types,
	[ARG_PTR_TO_BTF_ID]		= &btf_ptr_types,
	[ARG_PTR_TO_SPIN_LOCK]		= &spin_lock_types,
	[ARG_PTR_TO_MEM]		= &mem_types,
	[ARG_PTR_TO_MEM_OR_NULL]	= &mem_types,
	[ARG_PTR_TO_UNINIT_MEM]		= &mem_types,
	[ARG_PTR_TO_ALLOC_MEM]		= &alloc_mem_types,
	[ARG_PTR_TO_ALLOC_MEM_OR_NULL]	= &alloc_mem_types,
	[ARG_PTR_TO_INT]		= &int_ptr_types,
	[ARG_PTR_TO_LONG]		= &int_ptr_types,
	[__BPF_ARG_TYPE_MAX]		= NULL,
};

static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
			  const struct bpf_reg_types *compatible)
{
	struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
	enum bpf_reg_type expected, type = reg->type;
	int i, j;

	for (i = 0; i < ARRAY_SIZE(compatible->types); i++) {
		expected = compatible->types[i];
		if (expected == NOT_INIT)
			break;

		if (type == expected)
			return 0;
	}

	verbose(env, "R%d type=%s expected=", regno, reg_type_str[type]);
	for (j = 0; j + 1 < i; j++)
		verbose(env, "%s, ", reg_type_str[compatible->types[j]]);
	verbose(env, "%s\n", reg_type_str[compatible->types[j]]);
	return -EACCES;
}

static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
			  struct bpf_call_arg_meta *meta,
			  struct bpf_call_arg_meta *meta,
			  const struct bpf_func_proto *fn)
			  const struct bpf_func_proto *fn)
{
{
	u32 regno = BPF_REG_1 + arg;
	u32 regno = BPF_REG_1 + arg;
	struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
	struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
	enum bpf_reg_type expected_type, type = reg->type;
	enum bpf_arg_type arg_type = fn->arg_type[arg];
	enum bpf_arg_type arg_type = fn->arg_type[arg];
	const struct bpf_reg_types *compatible;
	enum bpf_reg_type type = reg->type;
	int err = 0;
	int err = 0;


	if (arg_type == ARG_DONTCARE)
	if (arg_type == ARG_DONTCARE)
@@ -4003,72 +4098,16 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
		 */
		 */
		goto skip_type_check;
		goto skip_type_check;


	if (arg_type == ARG_PTR_TO_MAP_KEY ||
	compatible = compatible_reg_types[arg_type];
	    arg_type == ARG_PTR_TO_MAP_VALUE ||
	if (!compatible) {
	    arg_type == ARG_PTR_TO_UNINIT_MAP_VALUE ||
		verbose(env, "verifier internal error: unsupported arg type %d\n", arg_type);
	    arg_type == ARG_PTR_TO_MAP_VALUE_OR_NULL) {
		expected_type = PTR_TO_STACK;
		if (!type_is_pkt_pointer(type) &&
		    type != PTR_TO_MAP_VALUE &&
		    type != expected_type)
			goto err_type;
	} else if (arg_type == ARG_CONST_SIZE ||
		   arg_type == ARG_CONST_SIZE_OR_ZERO ||
		   arg_type == ARG_CONST_ALLOC_SIZE_OR_ZERO) {
		expected_type = SCALAR_VALUE;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type == ARG_CONST_MAP_PTR) {
		expected_type = CONST_PTR_TO_MAP;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type == ARG_PTR_TO_CTX ||
		   arg_type == ARG_PTR_TO_CTX_OR_NULL) {
		expected_type = PTR_TO_CTX;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type == ARG_PTR_TO_SOCK_COMMON) {
		expected_type = PTR_TO_SOCK_COMMON;
		/* Any sk pointer can be ARG_PTR_TO_SOCK_COMMON */
		if (!type_is_sk_pointer(type))
			goto err_type;
	} else if (arg_type == ARG_PTR_TO_SOCKET ||
		   arg_type == ARG_PTR_TO_SOCKET_OR_NULL) {
		expected_type = PTR_TO_SOCKET;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type == ARG_PTR_TO_BTF_ID) {
		expected_type = PTR_TO_BTF_ID;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type == ARG_PTR_TO_SPIN_LOCK) {
		expected_type = PTR_TO_MAP_VALUE;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type_is_mem_ptr(arg_type)) {
		expected_type = PTR_TO_STACK;
		if (!type_is_pkt_pointer(type) &&
		    type != PTR_TO_MAP_VALUE &&
		    type != PTR_TO_MEM &&
		    type != PTR_TO_RDONLY_BUF &&
		    type != PTR_TO_RDWR_BUF &&
		    type != expected_type)
			goto err_type;
	} else if (arg_type_is_alloc_mem_ptr(arg_type)) {
		expected_type = PTR_TO_MEM;
		if (type != expected_type)
			goto err_type;
	} else if (arg_type_is_int_ptr(arg_type)) {
		expected_type = PTR_TO_STACK;
		if (!type_is_pkt_pointer(type) &&
		    type != PTR_TO_MAP_VALUE &&
		    type != expected_type)
			goto err_type;
	} else {
		verbose(env, "unsupported arg_type %d\n", arg_type);
		return -EFAULT;
		return -EFAULT;
	}
	}


	err = check_reg_type(env, regno, compatible);
	if (err)
		return err;

	if (type == PTR_TO_BTF_ID) {
	if (type == PTR_TO_BTF_ID) {
		const u32 *btf_id = fn->arg_btf_id[arg];
		const u32 *btf_id = fn->arg_btf_id[arg];


@@ -4221,10 +4260,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
	}
	}


	return err;
	return err;
err_type:
	verbose(env, "R%d type=%s expected=%s\n", regno,
		reg_type_str[type], reg_type_str[expected_type]);
	return -EACCES;
}
}


static bool may_update_sockmap(struct bpf_verifier_env *env, int func_id)
static bool may_update_sockmap(struct bpf_verifier_env *env, int func_id)