From: Martin KaFai Lau <martin.lau@linux.dev>
To: thinker.li@gmail.com
Cc: sinquersw@gmail.com, kuifeng@meta.com, bpf@vger.kernel.org,
ast@kernel.org, song@kernel.org, kernel-team@meta.com,
andrii@kernel.org
Subject: Re: [PATCH bpf-next v4 2/3] bpf: Check cfi_stubs before registering a struct_ops type.
Date: Wed, 21 Feb 2024 10:25:22 -0800 [thread overview]
Message-ID: <8e6e79d6-e003-446b-bc36-b6a4500f802b@linux.dev> (raw)
In-Reply-To: <20240221075213.2071454-3-thinker.li@gmail.com>
On 2/20/24 11:52 PM, thinker.li@gmail.com wrote:
> From: Kui-Feng Lee <thinker.li@gmail.com>
>
> Recently, cfi_stubs were introduced. However, existing struct_ops types
> that are not in the upstream may not be aware of this, resulting in kernel
> crashes. By rejecting struct_ops types that do not provide cfi_stubs during
> registration, these crashes can be avoided.
>
> Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
> ---
> kernel/bpf/bpf_struct_ops.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
> index 0d7be97a2411..c1c502caae08 100644
> --- a/kernel/bpf/bpf_struct_ops.c
> +++ b/kernel/bpf/bpf_struct_ops.c
> @@ -302,6 +302,11 @@ int bpf_struct_ops_desc_init(struct bpf_struct_ops_desc *st_ops_desc,
> }
> sprintf(value_name, "%s%s", VALUE_PREFIX, st_ops->name);
>
> + if (!st_ops->cfi_stubs) {
> + pr_warn("struct %s has no cfi_stubs\n", st_ops->name);
> + return -EINVAL;
> + }
> +
> type_id = btf_find_by_name_kind(btf, st_ops->name,
> BTF_KIND_STRUCT);
> if (type_id < 0) {
> @@ -339,6 +344,7 @@ int bpf_struct_ops_desc_init(struct bpf_struct_ops_desc *st_ops_desc,
>
> for_each_member(i, t, member) {
> const struct btf_type *func_proto;
> + u32 moff;
>
> mname = btf_name_by_offset(btf, member->name_off);
> if (!*mname) {
> @@ -361,6 +367,17 @@ int bpf_struct_ops_desc_init(struct bpf_struct_ops_desc *st_ops_desc,
> if (!func_proto)
> continue;
>
> + moff = __btf_member_bit_offset(t, member) / 8;
> + err = st_ops->check_member ?
> + st_ops->check_member(t, member, NULL) : 0;
I don't think it is necessary to make check_member more complicated by taking
NULL prog. The struct_ops implementer then needs to handle this extra NULL
prog case.
Have you thought about Alexei's earlier suggestion in v3 to reuse the NULL
member in cfi_stubs to flag unsupported member and remove the unsupported_ops[]
from bpf_tcp_ca.c?
If the verifier can consistently reject loading unsupported bpf prog, it will
not reach the bpf_struct_ops_map_update_elem and then hits the NULL member
in cfi_stubs during map_update_elem.
Untested code:
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 011d54a1dc53..c57cb0e2a8a7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20370,6 +20370,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
u32 btf_id, member_idx;
struct btf *btf;
const char *mname;
+ u32 moff;
if (!prog->gpl_compatible) {
verbose(env, "struct ops programs must have a GPL compatible license\n");
@@ -20417,11 +20418,18 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
return -EINVAL;
}
+ moff = __btf_member_bit_offset(t, member) / 8;
+ if (!*(void **)(st_ops->cfi_stubs + moff)) {
+ verbose(env, "attach to unsupported member %s of struct %s\n",
+ mname, st_ops->name);
+ return -ENOTSUPP;
+ }
+
if (st_ops->check_member) {
int err = st_ops->check_member(t, member, prog);
if (err) {
- verbose(env, "attach to unsupported member %s of struct %s\n",
+ verbose(env, "cannot attach to member %s of struct %s\n",
mname, st_ops->name);
return err;
}
diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index 7f518ea5f4ac..bcb1fcd00973 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -14,10 +14,6 @@
/* "extern" is to avoid sparse warning. It is only used in bpf_struct_ops.c. */
static struct bpf_struct_ops bpf_tcp_congestion_ops;
-static u32 unsupported_ops[] = {
- offsetof(struct tcp_congestion_ops, get_info),
-};
-
static const struct btf_type *tcp_sock_type;
static u32 tcp_sock_id, sock_id;
static const struct btf_type *tcp_congestion_ops_type;
@@ -45,18 +41,6 @@ static int bpf_tcp_ca_init(struct btf *btf)
return 0;
}
-static bool is_unsupported(u32 member_offset)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(unsupported_ops); i++) {
- if (member_offset == unsupported_ops[i])
- return true;
- }
-
- return false;
-}
-
static bool bpf_tcp_ca_is_valid_access(int off, int size,
enum bpf_access_type type,
const struct bpf_prog *prog,
@@ -248,15 +232,6 @@ static int bpf_tcp_ca_init_member(const struct btf_type *t,
return 0;
}
-static int bpf_tcp_ca_check_member(const struct btf_type *t,
- const struct btf_member *member,
- const struct bpf_prog *prog)
-{
- if (is_unsupported(__btf_member_bit_offset(t, member) / 8))
- return -ENOTSUPP;
- return 0;
-}
-
static int bpf_tcp_ca_reg(void *kdata)
{
return tcp_register_congestion_control(kdata);
@@ -350,7 +325,6 @@ static struct bpf_struct_ops bpf_tcp_congestion_ops = {
.reg = bpf_tcp_ca_reg,
.unreg = bpf_tcp_ca_unreg,
.update = bpf_tcp_ca_update,
- .check_member = bpf_tcp_ca_check_member,
.init_member = bpf_tcp_ca_init_member,
.init = bpf_tcp_ca_init,
.validate = bpf_tcp_ca_validate,
--
2.34.1
> +
> + if (!err && !*(void **)(st_ops->cfi_stubs + moff)) {
> + pr_warn("member %s in struct %s has no cfi stub function\n",
> + mname, st_ops->name);
> + err = -EINVAL;
> + goto errout;
> + }
> +
> if (btf_distill_func_proto(log, btf,
> func_proto, mname,
> &st_ops->func_models[i])) {
next prev parent reply other threads:[~2024-02-21 18:25 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-21 7:52 [PATCH bpf-next v4 0/3] Check cfi_stubs before registering a struct_ops type thinker.li
2024-02-21 7:52 ` [PATCH bpf-next v4 1/3] bpf, net: allow passing NULL prog to check_member thinker.li
2024-02-21 7:52 ` [PATCH bpf-next v4 2/3] bpf: Check cfi_stubs before registering a struct_ops type thinker.li
2024-02-21 18:25 ` Martin KaFai Lau [this message]
2024-02-21 23:13 ` Kui-Feng Lee
2024-02-22 1:11 ` Kui-Feng Lee
2024-02-21 7:52 ` [PATCH bpf-next v4 3/3] selftests/bpf: Test case for lacking CFI stub functions thinker.li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8e6e79d6-e003-446b-bc36-b6a4500f802b@linux.dev \
--to=martin.lau@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=kernel-team@meta.com \
--cc=kuifeng@meta.com \
--cc=sinquersw@gmail.com \
--cc=song@kernel.org \
--cc=thinker.li@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox