This patch makes BTF verifier to accept extern func. It is used for
allowing bpf program to call a limited set of kernel functions
in a later patch.

When writing bpf prog, the extern kernel function needs
to be declared under a ELF section (".ksyms") which is
the same as the current extern kernel variables and that should
keep its usage consistent without requiring to remember another
section name.

For example, in a bpf_prog.c:

extern int foo(struct sock *) __attribute__((section(".ksyms")))

[24] FUNC_PROTO '(anon)' ret_type_id=15 vlen=1
        '(anon)' type_id=18
[25] FUNC 'foo' type_id=24 linkage=extern
[ ... ]
[33] DATASEC '.ksyms' size=0 vlen=1
        type_id=25 offset=0 size=0

LLVM will put the "func" type into the BTF datasec ".ksyms".
The current "btf_datasec_check_meta()" assumes everything under
it is a "var" and ensures it has non-zero size ("!vsi->size" test).
The non-zero size check is not true for "func".  This patch postpones the
"!vsi-size" test from "btf_datasec_check_meta()" to
"btf_datasec_resolve()" which has all types collected to decide
if a vsi is a "var" or a "func" and then enforce the "vsi->size"
differently.

If the datasec only has "func", its "t->size" could be zero.
Thus, the current "!t->size" test is no longer valid.  The
invalid "t->size" will still be caught by the later
"last_vsi_end_off > t->size" check.   This patch also takes this
chance to consolidate other "t->size" tests ("vsi->offset >= t->size"
"vsi->size > t->size", and "t->size < sum") into the existing
"last_vsi_end_off > t->size" test.

The LLVM will also put those extern kernel function as an extern
linkage func in the BTF:

[24] FUNC_PROTO '(anon)' ret_type_id=15 vlen=1
        '(anon)' type_id=18
[25] FUNC 'foo' type_id=24 linkage=extern

This patch allows BTF_FUNC_EXTERN in btf_func_check_meta().
Also extern kernel function declaration does not
necessary have arg name. Another change in btf_func_check() is
to allow extern function having no arg name.

The btf selftest is adjusted accordingly.  New tests are also added.

The required LLVM patch: https://reviews.llvm.org/D93563

Signed-off-by: Martin KaFai Lau <ka...@fb.com>
---
 kernel/bpf/btf.c                             |  52 ++++---
 tools/testing/selftests/bpf/prog_tests/btf.c | 154 ++++++++++++++++++-
 2 files changed, 178 insertions(+), 28 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 369faeddf1df..96cd24020a38 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3439,7 +3439,7 @@ static s32 btf_func_check_meta(struct btf_verifier_env 
*env,
                return -EINVAL;
        }
 
-       if (btf_type_vlen(t) > BTF_FUNC_GLOBAL) {
+       if (btf_type_vlen(t) > BTF_FUNC_EXTERN) {
                btf_verifier_log_type(env, t, "Invalid func linkage");
                return -EINVAL;
        }
@@ -3532,7 +3532,7 @@ static s32 btf_datasec_check_meta(struct btf_verifier_env 
*env,
                                  u32 meta_left)
 {
        const struct btf_var_secinfo *vsi;
-       u64 last_vsi_end_off = 0, sum = 0;
+       u64 last_vsi_end_off = 0;
        u32 i, meta_needed;
 
        meta_needed = btf_type_vlen(t) * sizeof(*vsi);
@@ -3543,11 +3543,6 @@ static s32 btf_datasec_check_meta(struct 
btf_verifier_env *env,
                return -EINVAL;
        }
 
-       if (!t->size) {
-               btf_verifier_log_type(env, t, "size == 0");
-               return -EINVAL;
-       }
-
        if (btf_type_kflag(t)) {
                btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
                return -EINVAL;
@@ -3569,19 +3564,13 @@ static s32 btf_datasec_check_meta(struct 
btf_verifier_env *env,
                        return -EINVAL;
                }
 
-               if (vsi->offset < last_vsi_end_off || vsi->offset >= t->size) {
+               if (vsi->offset < last_vsi_end_off) {
                        btf_verifier_log_vsi(env, t, vsi,
                                             "Invalid offset");
                        return -EINVAL;
                }
 
-               if (!vsi->size || vsi->size > t->size) {
-                       btf_verifier_log_vsi(env, t, vsi,
-                                            "Invalid size");
-                       return -EINVAL;
-               }
-
-               last_vsi_end_off = vsi->offset + vsi->size;
+               last_vsi_end_off = (u64)vsi->offset + vsi->size;
                if (last_vsi_end_off > t->size) {
                        btf_verifier_log_vsi(env, t, vsi,
                                             "Invalid offset+size");
@@ -3589,12 +3578,6 @@ static s32 btf_datasec_check_meta(struct 
btf_verifier_env *env,
                }
 
                btf_verifier_log_vsi(env, t, vsi, NULL);
-               sum += vsi->size;
-       }
-
-       if (t->size < sum) {
-               btf_verifier_log_type(env, t, "Invalid btf_info size");
-               return -EINVAL;
        }
 
        return meta_needed;
@@ -3611,9 +3594,28 @@ static int btf_datasec_resolve(struct btf_verifier_env 
*env,
                u32 var_type_id = vsi->type, type_id, type_size = 0;
                const struct btf_type *var_type = btf_type_by_id(env->btf,
                                                                 var_type_id);
-               if (!var_type || !btf_type_is_var(var_type)) {
+               if (!var_type) {
+                       btf_verifier_log_vsi(env, v->t, vsi,
+                                            "type not found");
+                       return -EINVAL;
+               }
+
+               if (btf_type_is_func(var_type)) {
+                       if (vsi->size || vsi->offset) {
+                               btf_verifier_log_vsi(env, v->t, vsi,
+                                                    "Invalid size/offset");
+                               return -EINVAL;
+                       }
+                       continue;
+               } else if (btf_type_is_var(var_type)) {
+                       if (!vsi->size) {
+                               btf_verifier_log_vsi(env, v->t, vsi,
+                                                    "Invalid size");
+                               return -EINVAL;
+                       }
+               } else {
                        btf_verifier_log_vsi(env, v->t, vsi,
-                                            "Not a VAR kind member");
+                                            "Neither a VAR nor a FUNC");
                        return -EINVAL;
                }
 
@@ -3849,9 +3851,11 @@ static int btf_func_check(struct btf_verifier_env *env,
        const struct btf_param *args;
        const struct btf *btf;
        u16 nr_args, i;
+       bool is_extern;
 
        btf = env->btf;
        proto_type = btf_type_by_id(btf, t->type);
+       is_extern = btf_type_vlen(t) == BTF_FUNC_EXTERN;
 
        if (!proto_type || !btf_type_is_func_proto(proto_type)) {
                btf_verifier_log_type(env, t, "Invalid type_id");
@@ -3861,7 +3865,7 @@ static int btf_func_check(struct btf_verifier_env *env,
        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) {
+               if (!is_extern && !args[i].name_off && args[i].type) {
                        btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
                        return -EINVAL;
                }
diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c 
b/tools/testing/selftests/bpf/prog_tests/btf.c
index 0457ae32b270..e469482833b2 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -498,7 +498,7 @@ static struct btf_raw_test raw_tests[] = {
        .value_type_id = 7,
        .max_entries = 1,
        .btf_load_err = true,
-       .err_str = "Invalid size",
+       .err_str = "Invalid offset+size",
 },
 {
        .descr = "global data test #10, invalid var size",
@@ -696,7 +696,7 @@ static struct btf_raw_test raw_tests[] = {
        .err_str = "Invalid offset",
 },
 {
-       .descr = "global data test #15, not var kind",
+       .descr = "global data test #15, not var/func kind",
        .raw_types = {
                /* int */
                BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
@@ -716,7 +716,7 @@ static struct btf_raw_test raw_tests[] = {
        .value_type_id = 3,
        .max_entries = 1,
        .btf_load_err = true,
-       .err_str = "Not a VAR kind member",
+       .err_str = "Neither a VAR nor a FUNC",
 },
 {
        .descr = "global data test #16, invalid var referencing sec",
@@ -2803,7 +2803,7 @@ static struct btf_raw_test raw_tests[] = {
                        BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
                        BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
                /* void func(int a, unsigned int b) */
-               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 2), 3),   
/* [4] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 3), 3),   
/* [4] */
                BTF_END_RAW,
        },
        .str_sec = "\0a\0b\0func",
@@ -3531,6 +3531,152 @@ static struct btf_raw_test raw_tests[] = {
        .max_entries = 1,
 },
 
+{
+       .descr = "datasec: func only",
+       .raw_types = {
+               /* int */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(void) */
+               BTF_FUNC_PROTO_ENC(0, 0),               /* [2] */
+               BTF_FUNC_ENC(NAME_NTH(1), 2),           /* [3] */
+               BTF_FUNC_ENC(NAME_NTH(2), 2),           /* [4] */
+               /* .ksym section */
+               BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 2), 
0), /* [5] */
+               BTF_VAR_SECINFO_ENC(3, 0, 0),
+               BTF_VAR_SECINFO_ENC(4, 0, 0),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0foo1\0foo2\0.ksym\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 1,
+},
+
+{
+       .descr = "datasec: func and var",
+       .raw_types = {
+               /* int */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(void) */
+               BTF_FUNC_PROTO_ENC(0, 0),               /* [2] */
+               BTF_FUNC_ENC(NAME_NTH(1), 2),           /* [3] */
+               BTF_FUNC_ENC(NAME_NTH(2), 2),           /* [4] */
+               /* int */
+               BTF_VAR_ENC(NAME_NTH(4), 1, 0),         /* [5] */
+               BTF_VAR_ENC(NAME_NTH(5), 1, 0),         /* [6] */
+               /* .ksym section */
+               BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 
8), /* [7] */
+               BTF_VAR_SECINFO_ENC(3, 0, 0),
+               BTF_VAR_SECINFO_ENC(4, 0, 0),
+               BTF_VAR_SECINFO_ENC(5, 0, 4),
+               BTF_VAR_SECINFO_ENC(6, 4, 4),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 1,
+},
+
+{
+       .descr = "datasec: func and var, invalid size/offset for func",
+       .raw_types = {
+               /* int */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(void) */
+               BTF_FUNC_PROTO_ENC(0, 0),               /* [2] */
+               BTF_FUNC_ENC(NAME_NTH(1), 2),           /* [3] */
+               BTF_FUNC_ENC(NAME_NTH(2), 2),           /* [4] */
+               /* int */
+               BTF_VAR_ENC(NAME_NTH(4), 1, 0),         /* [5] */
+               BTF_VAR_ENC(NAME_NTH(5), 1, 0),         /* [6] */
+               /* .ksym section */
+               BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 
8), /* [7] */
+               BTF_VAR_SECINFO_ENC(3, 0, 0),
+               BTF_VAR_SECINFO_ENC(5, 0, 4),
+               BTF_VAR_SECINFO_ENC(4, 4, 0),   /* func has non zero 
vsi->offset */
+               BTF_VAR_SECINFO_ENC(6, 4, 4),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 1,
+       .btf_load_err = true,
+       .err_str = "Invalid size/offset",
+},
+
+{
+       .descr = "datasec: func and var, datasec size 0",
+       .raw_types = {
+               /* int */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(void) */
+               BTF_FUNC_PROTO_ENC(0, 0),               /* [2] */
+               BTF_FUNC_ENC(NAME_NTH(1), 2),           /* [3] */
+               BTF_FUNC_ENC(NAME_NTH(2), 2),           /* [4] */
+               /* int */
+               BTF_VAR_ENC(NAME_NTH(4), 1, 0),         /* [5] */
+               BTF_VAR_ENC(NAME_NTH(5), 1, 0),         /* [6] */
+               /* .ksym section */
+               BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 
0), /* [7] */
+               BTF_VAR_SECINFO_ENC(3, 0, 0),
+               BTF_VAR_SECINFO_ENC(4, 0, 0),
+               BTF_VAR_SECINFO_ENC(5, 0, 4),
+               BTF_VAR_SECINFO_ENC(6, 4, 4),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 1,
+       .btf_load_err = true,
+       .err_str = "Invalid offset+size",
+},
+
+{
+       .descr = "datasec: func and var, zero vsi->size for var",
+       .raw_types = {
+               /* int */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(void) */
+               BTF_FUNC_PROTO_ENC(0, 0),               /* [2] */
+               BTF_FUNC_ENC(NAME_NTH(1), 2),           /* [3] */
+               BTF_FUNC_ENC(NAME_NTH(2), 2),           /* [4] */
+               /* int */
+               BTF_VAR_ENC(NAME_NTH(4), 1, 0),         /* [5] */
+               BTF_VAR_ENC(NAME_NTH(5), 1, 0),         /* [6] */
+               /* .ksym section */
+               BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 
8), /* [7] */
+               BTF_VAR_SECINFO_ENC(3, 0, 0),
+               BTF_VAR_SECINFO_ENC(4, 0, 0),
+               BTF_VAR_SECINFO_ENC(5, 0, 0),   /* var has zero vsi->size */
+               BTF_VAR_SECINFO_ENC(6, 0, 4),
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 1,
+       .btf_load_err = true,
+       .err_str = "Invalid size",
+},
+
 {
        .descr = "float test #1, well-formed",
        .raw_types = {
-- 
2.30.2

Reply via email to