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 <kafai@xxxxxx> --- 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