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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.