bpf: btf: Add BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO
authorMartin KaFai Lau <kafai@fb.com>
Mon, 19 Nov 2018 23:29:08 +0000 (15:29 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 20 Nov 2018 18:54:38 +0000 (10:54 -0800)
This patch adds BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO
to support the function debug info.

BTF_KIND_FUNC_PROTO must not have a name (i.e. !t->name_off)
and it is followed by >= 0 'struct bpf_param' objects to
describe the function arguments.

The BTF_KIND_FUNC must have a valid name and it must
refer back to a BTF_KIND_FUNC_PROTO.

The above is the conclusion after the discussion between
Edward Cree, Alexei, Daniel, Yonghong and Martin.

By combining BTF_KIND_FUNC and BTF_LIND_FUNC_PROTO,
a complete function signature can be obtained.  It will be
used in the later patches to learn the function signature of
a running bpf program.

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/uapi/linux/btf.h
kernel/bpf/btf.c

index 972265f328717b8286edc2fc93c9d2a54ed394f8..14f66948fc95f149e0c77a7412d23421a9ea5e71 100644 (file)
@@ -40,7 +40,8 @@ struct btf_type {
        /* "size" is used by INT, ENUM, STRUCT and UNION.
         * "size" tells the size of the type it is describing.
         *
-        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+        * FUNC and FUNC_PROTO.
         * "type" is a type_id referring to another type.
         */
        union {
@@ -64,8 +65,10 @@ struct btf_type {
 #define BTF_KIND_VOLATILE      9       /* Volatile     */
 #define BTF_KIND_CONST         10      /* Const        */
 #define BTF_KIND_RESTRICT      11      /* Restrict     */
-#define BTF_KIND_MAX           11
-#define NR_BTF_KINDS           12
+#define BTF_KIND_FUNC          12      /* Function     */
+#define BTF_KIND_FUNC_PROTO    13      /* Function Proto       */
+#define BTF_KIND_MAX           13
+#define NR_BTF_KINDS           14
 
 /* For some specific BTF_KIND, "struct btf_type" is immediately
  * followed by extra data.
@@ -110,4 +113,13 @@ struct btf_member {
        __u32   offset; /* offset in bits */
 };
 
+/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
+ * The exact number of btf_param is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_param {
+       __u32   name_off;
+       __u32   type;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
index 2a50d87de485bb7681c415e09aa625649bd4849b..6a2be79b73fc789e2fc333be8d7f1d12c90978d8 100644 (file)
@@ -5,6 +5,7 @@
 #include <uapi/linux/types.h>
 #include <linux/seq_file.h>
 #include <linux/compiler.h>
+#include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/anon_inodes.h>
@@ -259,6 +260,8 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
        [BTF_KIND_VOLATILE]     = "VOLATILE",
        [BTF_KIND_CONST]        = "CONST",
        [BTF_KIND_RESTRICT]     = "RESTRICT",
+       [BTF_KIND_FUNC]         = "FUNC",
+       [BTF_KIND_FUNC_PROTO]   = "FUNC_PROTO",
 };
 
 struct btf_kind_operations {
@@ -281,6 +284,9 @@ struct btf_kind_operations {
 static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS];
 static struct btf_type btf_void;
 
+static int btf_resolve(struct btf_verifier_env *env,
+                      const struct btf_type *t, u32 type_id);
+
 static bool btf_type_is_modifier(const struct btf_type *t)
 {
        /* Some of them is not strictly a C modifier
@@ -314,9 +320,20 @@ static bool btf_type_is_fwd(const struct btf_type *t)
        return BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
 }
 
+static bool btf_type_is_func(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC;
+}
+
+static bool btf_type_is_func_proto(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC_PROTO;
+}
+
 static bool btf_type_nosize(const struct btf_type *t)
 {
-       return btf_type_is_void(t) || btf_type_is_fwd(t);
+       return btf_type_is_void(t) || btf_type_is_fwd(t) ||
+              btf_type_is_func(t) || btf_type_is_func_proto(t);
 }
 
 static bool btf_type_nosize_or_null(const struct btf_type *t)
@@ -433,6 +450,30 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
                offset < btf->hdr.str_len;
 }
 
+/* Only C-style identifier is permitted. This can be relaxed if
+ * necessary.
+ */
+static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
+{
+       /* offset must be valid */
+       const char *src = &btf->strings[offset];
+       const char *src_limit;
+
+       if (!isalpha(*src) && *src != '_')
+               return false;
+
+       /* set a limit on identifier length */
+       src_limit = src + KSYM_NAME_LEN;
+       src++;
+       while (*src && src < src_limit) {
+               if (!isalnum(*src) && *src != '_')
+                       return false;
+               src++;
+       }
+
+       return !*src;
+}
+
 static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
 {
        if (!offset)
@@ -747,11 +788,15 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env,
                /* int, enum or void is a sink */
                return !btf_type_needs_resolve(next_type);
        case RESOLVE_PTR:
-               /* int, enum, void, struct or array is a sink for ptr */
+               /* int, enum, void, struct, array, func or func_proto is a sink
+                * for ptr
+                */
                return !btf_type_is_modifier(next_type) &&
                        !btf_type_is_ptr(next_type);
        case RESOLVE_STRUCT_OR_ARRAY:
-               /* int, enum, void or ptr is a sink for struct and array */
+               /* int, enum, void, ptr, func or func_proto is a sink
+                * for struct and array
+                */
                return !btf_type_is_modifier(next_type) &&
                        !btf_type_is_array(next_type) &&
                        !btf_type_is_struct(next_type);
@@ -1170,10 +1215,6 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
-       /* "typedef void new_void", "const void"...etc */
-       if (btf_type_is_void(next_type) || btf_type_is_fwd(next_type))
-               goto resolved;
-
        if (!env_type_is_resolve_sink(env, next_type) &&
            !env_type_is_resolved(env, next_type_id))
                return env_stack_push(env, next_type, next_type_id);
@@ -1184,13 +1225,18 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
         * save us a few type-following when we use it later (e.g. in
         * pretty print).
         */
-       if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
-           !btf_type_nosize(btf_type_id_resolve(btf, &next_type_id))) {
-               btf_verifier_log_type(env, v->t, "Invalid type_id");
-               return -EINVAL;
+       if (!btf_type_id_size(btf, &next_type_id, &next_type_size)) {
+               if (env_type_is_resolved(env, next_type_id))
+                       next_type = btf_type_id_resolve(btf, &next_type_id);
+
+               /* "typedef void new_void", "const void"...etc */
+               if (!btf_type_is_void(next_type) &&
+                   !btf_type_is_fwd(next_type)) {
+                       btf_verifier_log_type(env, v->t, "Invalid type_id");
+                       return -EINVAL;
+               }
        }
 
-resolved:
        env_stack_pop_resolved(env, next_type_id, next_type_size);
 
        return 0;
@@ -1203,7 +1249,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
        const struct btf_type *t = v->t;
        u32 next_type_id = t->type;
        struct btf *btf = env->btf;
-       u32 next_type_size = 0;
 
        next_type = btf_type_by_id(btf, next_type_id);
        if (!next_type) {
@@ -1211,10 +1256,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
-       /* "void *" */
-       if (btf_type_is_void(next_type) || btf_type_is_fwd(next_type))
-               goto resolved;
-
        if (!env_type_is_resolve_sink(env, next_type) &&
            !env_type_is_resolved(env, next_type_id))
                return env_stack_push(env, next_type, next_type_id);
@@ -1241,13 +1282,18 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
                                              resolved_type_id);
        }
 
-       if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
-           !btf_type_nosize(btf_type_id_resolve(btf, &next_type_id))) {
-               btf_verifier_log_type(env, v->t, "Invalid type_id");
-               return -EINVAL;
+       if (!btf_type_id_size(btf, &next_type_id, NULL)) {
+               if (env_type_is_resolved(env, next_type_id))
+                       next_type = btf_type_id_resolve(btf, &next_type_id);
+
+               if (!btf_type_is_void(next_type) &&
+                   !btf_type_is_fwd(next_type) &&
+                   !btf_type_is_func_proto(next_type)) {
+                       btf_verifier_log_type(env, v->t, "Invalid type_id");
+                       return -EINVAL;
+               }
        }
 
-resolved:
        env_stack_pop_resolved(env, next_type_id, 0);
 
        return 0;
@@ -1787,6 +1833,232 @@ static struct btf_kind_operations enum_ops = {
        .seq_show = btf_enum_seq_show,
 };
 
+static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
+                                    const struct btf_type *t,
+                                    u32 meta_left)
+{
+       u32 meta_needed = btf_type_vlen(t) * sizeof(struct btf_param);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (t->name_off) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return meta_needed;
+}
+
+static void btf_func_proto_log(struct btf_verifier_env *env,
+                              const struct btf_type *t)
+{
+       const struct btf_param *args = (const struct btf_param *)(t + 1);
+       u16 nr_args = btf_type_vlen(t), i;
+
+       btf_verifier_log(env, "return=%u args=(", t->type);
+       if (!nr_args) {
+               btf_verifier_log(env, "void");
+               goto done;
+       }
+
+       if (nr_args == 1 && !args[0].type) {
+               /* Only one vararg */
+               btf_verifier_log(env, "vararg");
+               goto done;
+       }
+
+       btf_verifier_log(env, "%u %s", args[0].type,
+                        btf_name_by_offset(env->btf,
+                                           args[0].name_off));
+       for (i = 1; i < nr_args - 1; i++)
+               btf_verifier_log(env, ", %u %s", args[i].type,
+                                btf_name_by_offset(env->btf,
+                                                   args[i].name_off));
+
+       if (nr_args > 1) {
+               const struct btf_param *last_arg = &args[nr_args - 1];
+
+               if (last_arg->type)
+                       btf_verifier_log(env, ", %u %s", last_arg->type,
+                                        btf_name_by_offset(env->btf,
+                                                           last_arg->name_off));
+               else
+                       btf_verifier_log(env, ", vararg");
+       }
+
+done:
+       btf_verifier_log(env, ")");
+}
+
+static struct btf_kind_operations func_proto_ops = {
+       .check_meta = btf_func_proto_check_meta,
+       .resolve = btf_df_resolve,
+       /*
+        * BTF_KIND_FUNC_PROTO cannot be directly referred by
+        * a struct's member.
+        *
+        * It should be a funciton pointer instead.
+        * (i.e. struct's member -> BTF_KIND_PTR -> BTF_KIND_FUNC_PROTO)
+        *
+        * Hence, there is no btf_func_check_member().
+        */
+       .check_member = btf_df_check_member,
+       .log_details = btf_func_proto_log,
+       .seq_show = btf_df_seq_show,
+};
+
+static s32 btf_func_check_meta(struct btf_verifier_env *env,
+                              const struct btf_type *t,
+                              u32 meta_left)
+{
+       if (!t->name_off ||
+           !btf_name_valid_identifier(env->btf, t->name_off)) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
+       if (btf_type_vlen(t)) {
+               btf_verifier_log_type(env, t, "vlen != 0");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return 0;
+}
+
+static struct btf_kind_operations func_ops = {
+       .check_meta = btf_func_check_meta,
+       .resolve = btf_df_resolve,
+       .check_member = btf_df_check_member,
+       .log_details = btf_ref_type_log,
+       .seq_show = btf_df_seq_show,
+};
+
+static int btf_func_proto_check(struct btf_verifier_env *env,
+                               const struct btf_type *t)
+{
+       const struct btf_type *ret_type;
+       const struct btf_param *args;
+       const struct btf *btf;
+       u16 nr_args, i;
+       int err;
+
+       btf = env->btf;
+       args = (const struct btf_param *)(t + 1);
+       nr_args = btf_type_vlen(t);
+
+       /* Check func return type which could be "void" (t->type == 0) */
+       if (t->type) {
+               u32 ret_type_id = t->type;
+
+               ret_type = btf_type_by_id(btf, ret_type_id);
+               if (!ret_type) {
+                       btf_verifier_log_type(env, t, "Invalid return type");
+                       return -EINVAL;
+               }
+
+               if (btf_type_needs_resolve(ret_type) &&
+                   !env_type_is_resolved(env, ret_type_id)) {
+                       err = btf_resolve(env, ret_type, ret_type_id);
+                       if (err)
+                               return err;
+               }
+
+               /* Ensure the return type is a type that has a size */
+               if (!btf_type_id_size(btf, &ret_type_id, NULL)) {
+                       btf_verifier_log_type(env, t, "Invalid return type");
+                       return -EINVAL;
+               }
+       }
+
+       if (!nr_args)
+               return 0;
+
+       /* Last func arg type_id could be 0 if it is a vararg */
+       if (!args[nr_args - 1].type) {
+               if (args[nr_args - 1].name_off) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u",
+                                             nr_args);
+                       return -EINVAL;
+               }
+               nr_args--;
+       }
+
+       err = 0;
+       for (i = 0; i < nr_args; i++) {
+               const struct btf_type *arg_type;
+               u32 arg_type_id;
+
+               arg_type_id = args[i].type;
+               arg_type = btf_type_by_id(btf, arg_type_id);
+               if (!arg_type) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (args[i].name_off &&
+                   (!btf_name_offset_valid(btf, args[i].name_off) ||
+                    !btf_name_valid_identifier(btf, args[i].name_off))) {
+                       btf_verifier_log_type(env, t,
+                                             "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (btf_type_needs_resolve(arg_type) &&
+                   !env_type_is_resolved(env, arg_type_id)) {
+                       err = btf_resolve(env, arg_type, arg_type_id);
+                       if (err)
+                               break;
+               }
+
+               if (!btf_type_id_size(btf, &arg_type_id, NULL)) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int btf_func_check(struct btf_verifier_env *env,
+                         const struct btf_type *t)
+{
+       const struct btf_type *proto_type;
+       const struct btf_param *args;
+       const struct btf *btf;
+       u16 nr_args, i;
+
+       btf = env->btf;
+       proto_type = btf_type_by_id(btf, t->type);
+
+       if (!proto_type || !btf_type_is_func_proto(proto_type)) {
+               btf_verifier_log_type(env, t, "Invalid type_id");
+               return -EINVAL;
+       }
+
+       args = (const struct btf_param *)(proto_type + 1);
+       nr_args = btf_type_vlen(proto_type);
+       for (i = 0; i < nr_args; i++) {
+               if (!args[i].name_off && args[i].type) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_INT] = &int_ops,
        [BTF_KIND_PTR] = &ptr_ops,
@@ -1799,6 +2071,8 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_VOLATILE] = &modifier_ops,
        [BTF_KIND_CONST] = &modifier_ops,
        [BTF_KIND_RESTRICT] = &modifier_ops,
+       [BTF_KIND_FUNC] = &func_ops,
+       [BTF_KIND_FUNC_PROTO] = &func_proto_ops,
 };
 
 static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -1870,30 +2144,6 @@ static int btf_check_all_metas(struct btf_verifier_env *env)
        return 0;
 }
 
-static int btf_resolve(struct btf_verifier_env *env,
-                      const struct btf_type *t, u32 type_id)
-{
-       const struct resolve_vertex *v;
-       int err = 0;
-
-       env->resolve_mode = RESOLVE_TBD;
-       env_stack_push(env, t, type_id);
-       while (!err && (v = env_stack_peak(env))) {
-               env->log_type_id = v->type_id;
-               err = btf_type_ops(v->t)->resolve(env, v);
-       }
-
-       env->log_type_id = type_id;
-       if (err == -E2BIG)
-               btf_verifier_log_type(env, t,
-                                     "Exceeded max resolving depth:%u",
-                                     MAX_RESOLVE_DEPTH);
-       else if (err == -EEXIST)
-               btf_verifier_log_type(env, t, "Loop detected");
-
-       return err;
-}
-
 static bool btf_resolve_valid(struct btf_verifier_env *env,
                              const struct btf_type *t,
                              u32 type_id)
@@ -1927,6 +2177,39 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
        return false;
 }
 
+static int btf_resolve(struct btf_verifier_env *env,
+                      const struct btf_type *t, u32 type_id)
+{
+       u32 save_log_type_id = env->log_type_id;
+       const struct resolve_vertex *v;
+       int err = 0;
+
+       env->resolve_mode = RESOLVE_TBD;
+       env_stack_push(env, t, type_id);
+       while (!err && (v = env_stack_peak(env))) {
+               env->log_type_id = v->type_id;
+               err = btf_type_ops(v->t)->resolve(env, v);
+       }
+
+       env->log_type_id = type_id;
+       if (err == -E2BIG) {
+               btf_verifier_log_type(env, t,
+                                     "Exceeded max resolving depth:%u",
+                                     MAX_RESOLVE_DEPTH);
+       } else if (err == -EEXIST) {
+               btf_verifier_log_type(env, t, "Loop detected");
+       }
+
+       /* Final sanity check */
+       if (!err && !btf_resolve_valid(env, t, type_id)) {
+               btf_verifier_log_type(env, t, "Invalid resolve state");
+               err = -EINVAL;
+       }
+
+       env->log_type_id = save_log_type_id;
+       return err;
+}
+
 static int btf_check_all_types(struct btf_verifier_env *env)
 {
        struct btf *btf = env->btf;
@@ -1949,10 +2232,16 @@ static int btf_check_all_types(struct btf_verifier_env *env)
                                return err;
                }
 
-               if (btf_type_needs_resolve(t) &&
-                   !btf_resolve_valid(env, t, type_id)) {
-                       btf_verifier_log_type(env, t, "Invalid resolve state");
-                       return -EINVAL;
+               if (btf_type_is_func_proto(t)) {
+                       err = btf_func_proto_check(env, t);
+                       if (err)
+                               return err;
+               }
+
+               if (btf_type_is_func(t)) {
+                       err = btf_func_check(env, t);
+                       if (err)
+                               return err;
                }
        }