]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
bpf: Use a table to drive helper arg type checks
authorLorenz Bauer <lmb@cloudflare.com>
Mon, 21 Sep 2020 12:12:27 +0000 (13:12 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 21 Sep 2020 22:00:41 +0000 (15:00 -0700)
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: Lorenz Bauer <lmb@cloudflare.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20200921121227.255763-12-lmb@cloudflare.com
include/linux/bpf.h
kernel/bpf/verifier.c

index 87b0d5dcc1ffa130d64375b79f35cf15b5afe906..fc5c901c75421c9a1a4b026cb6dc0b43a60216eb 100644 (file)
@@ -292,6 +292,7 @@ enum bpf_arg_type {
        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_CONST_ALLOC_SIZE_OR_ZERO,   /* number of allocated bytes requested */
+       __BPF_ARG_TYPE_MAX,
 };
 
 /* type of values returned from helper functions */
index 129416ea0256e84dcddd43f85e0553f90beac5f2..15ab889b0a3fae5eaae89d0e11f47431664fb44a 100644 (file)
@@ -3903,12 +3903,6 @@ static bool arg_type_is_mem_size(enum bpf_arg_type type)
               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)
 {
        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;
 }
 
+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,
                          struct bpf_call_arg_meta *meta,
                          const struct bpf_func_proto *fn)
 {
        u32 regno = BPF_REG_1 + arg;
        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];
+       const struct bpf_reg_types *compatible;
+       enum bpf_reg_type type = reg->type;
        int err = 0;
 
        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;
 
-       if (arg_type == ARG_PTR_TO_MAP_KEY ||
-           arg_type == ARG_PTR_TO_MAP_VALUE ||
-           arg_type == ARG_PTR_TO_UNINIT_MAP_VALUE ||
-           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);
+       compatible = compatible_reg_types[arg_type];
+       if (!compatible) {
+               verbose(env, "verifier internal error: unsupported arg type %d\n", arg_type);
                return -EFAULT;
        }
 
+       err = check_reg_type(env, regno, compatible);
+       if (err)
+               return err;
+
        if (type == PTR_TO_BTF_ID) {
                const u32 *btf_id = fn->arg_btf_id[arg];
 
@@ -4221,10 +4260,6 @@ skip_type_check:
        }
 
        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)