]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
bpf: Check unsupported ops from the bpf_struct_ops's cfi_stubs
authorMartin KaFai Lau <martin.lau@kernel.org>
Mon, 22 Jul 2024 18:30:45 +0000 (11:30 -0700)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 29 Jul 2024 19:54:13 +0000 (12:54 -0700)
The bpf_tcp_ca struct_ops currently uses a "u32 unsupported_ops[]"
array to track which ops is not supported.

After cfi_stubs had been added, the function pointer in cfi_stubs is
also NULL for the unsupported ops. Thus, the "u32 unsupported_ops[]"
becomes redundant. This observation was originally brought up in the
bpf/cfi discussion:
https://lore.kernel.org/bpf/CAADnVQJoEkdjyCEJRPASjBw1QGsKYrF33QdMGc1RZa9b88bAEA@mail.gmail.com/

The recent bpf qdisc patch (https://lore.kernel.org/bpf/20240714175130.4051012-6-amery.hung@bytedance.com/)
also needs to specify quite many unsupported ops. It is a good time
to clean it up.

This patch removes the need of "u32 unsupported_ops[]" and tests for null-ness
in the cfi_stubs instead.

Testing the cfi_stubs is done in a new function bpf_struct_ops_supported().
The verifier will call bpf_struct_ops_supported() when loading the
struct_ops program. The ".check_member" is removed from the bpf_tcp_ca
in this patch. ".check_member" could still be useful for other subsytems
to enforce other restrictions (e.g. sched_ext checks for prog->sleepable).

To keep the same error return, ENOTSUPP is used.

Cc: Amery Hung <ameryhung@gmail.com>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://lore.kernel.org/r/20240722183049.2254692-2-martin.lau@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
include/linux/bpf.h
kernel/bpf/bpf_struct_ops.c
kernel/bpf/verifier.c
net/ipv4/bpf_tcp_ca.c

index 3b94ec161e8ccfb22af9a8051cc299cb55b85013..4c54864316ee49b1b80167cfe4c56d197d19ae95 100644 (file)
@@ -1795,6 +1795,7 @@ struct bpf_struct_ops_common_value {
 #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
 bool bpf_struct_ops_get(const void *kdata);
 void bpf_struct_ops_put(const void *kdata);
+int bpf_struct_ops_supported(const struct bpf_struct_ops *st_ops, u32 moff);
 int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
                                       void *value);
 int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks,
@@ -1851,6 +1852,10 @@ static inline void bpf_module_put(const void *data, struct module *owner)
 {
        module_put(owner);
 }
+static inline int bpf_struct_ops_supported(const struct bpf_struct_ops *st_ops, u32 moff)
+{
+       return -ENOTSUPP;
+}
 static inline int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map,
                                                     void *key,
                                                     void *value)
index bb3eabc0dc76923d5715d2906acbc3428841dc60..fda3dd2ee9844fec459c6dfa62d21c00da98a67c 100644 (file)
@@ -1040,6 +1040,13 @@ void bpf_struct_ops_put(const void *kdata)
        bpf_map_put(&st_map->map);
 }
 
+int bpf_struct_ops_supported(const struct bpf_struct_ops *st_ops, u32 moff)
+{
+       void *func_ptr = *(void **)(st_ops->cfi_stubs + moff);
+
+       return func_ptr ? 0 : -ENOTSUPP;
+}
+
 static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)
 {
        struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
index f8c474e8e5978c1e099b788f988250e11e8a891c..247a038d3a14ca9954086c567bfe3ca57de8d840 100644 (file)
@@ -21132,6 +21132,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
        u32 btf_id, member_idx;
        struct btf *btf;
        const char *mname;
+       int err;
 
        if (!prog->gpl_compatible) {
                verbose(env, "struct ops programs must have a GPL compatible license\n");
@@ -21179,8 +21180,15 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
                return -EINVAL;
        }
 
+       err = bpf_struct_ops_supported(st_ops, __btf_member_bit_offset(t, member) / 8);
+       if (err) {
+               verbose(env, "attach to unsupported member %s of struct %s\n",
+                       mname, st_ops->name);
+               return err;
+       }
+
        if (st_ops->check_member) {
-               int err = st_ops->check_member(t, member, prog);
+               err = st_ops->check_member(t, member, prog);
 
                if (err) {
                        verbose(env, "attach to unsupported member %s of struct %s\n",
index 3f88d0961e5b2f40994b9d2cfaedb87f5103fab3..554804774628e4b3d03bc3b19b96d717a581439e 100644 (file)
 /* "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,
@@ -251,15 +235,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, struct bpf_link *link)
 {
        return tcp_register_congestion_control(kdata);
@@ -354,7 +329,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,