[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20210316011348.4175708-1-kafai@fb.com>
Date: Mon, 15 Mar 2021 18:13:48 -0700
From: Martin KaFai Lau <kafai@...com>
To: <bpf@...r.kernel.org>
CC: Alexei Starovoitov <ast@...nel.org>,
Daniel Borkmann <daniel@...earbox.net>, <kernel-team@...com>,
<netdev@...r.kernel.org>
Subject: [PATCH bpf-next 02/15] bpf: btf: Support parsing extern func
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@...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
Powered by blists - more mailing lists