Commit e2854bc3 authored by Alexei Starovoitov's avatar Alexei Starovoitov Committed by Andrii Nakryiko

Merge branch 'bpf-retire-the-unsupported_ops-usage-in-struct_ops'

Martin KaFai Lau says:

====================
bpf: Retire the unsupported_ops usage in struct_ops

From: Martin KaFai Lau <martin.lau@kernel.org>

This series retires the unsupported_ops usage and depends on the
null-ness check on the cfi_stubs instead.

Please see individual patches for details.

v2:
- Fixed a gcc compiler warning on Patch 1.
====================

Link: https://lore.kernel.org/r/20240722183049.2254692-1-martin.lau@linux.devSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents 0d7c0612 4009c95f
...@@ -1795,6 +1795,7 @@ struct bpf_struct_ops_common_value { ...@@ -1795,6 +1795,7 @@ struct bpf_struct_ops_common_value {
#define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA)) #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
bool bpf_struct_ops_get(const void *kdata); bool bpf_struct_ops_get(const void *kdata);
void bpf_struct_ops_put(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, int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
void *value); void *value);
int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks, 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) ...@@ -1851,6 +1852,10 @@ static inline void bpf_module_put(const void *data, struct module *owner)
{ {
module_put(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, static inline int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map,
void *key, void *key,
void *value) void *value)
......
...@@ -1040,6 +1040,13 @@ void bpf_struct_ops_put(const void *kdata) ...@@ -1040,6 +1040,13 @@ void bpf_struct_ops_put(const void *kdata)
bpf_map_put(&st_map->map); 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) 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; struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
......
...@@ -21132,6 +21132,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) ...@@ -21132,6 +21132,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
u32 btf_id, member_idx; u32 btf_id, member_idx;
struct btf *btf; struct btf *btf;
const char *mname; const char *mname;
int err;
if (!prog->gpl_compatible) { if (!prog->gpl_compatible) {
verbose(env, "struct ops programs must have a GPL compatible license\n"); 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) ...@@ -21179,8 +21180,15 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
return -EINVAL; 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) { if (st_ops->check_member) {
int err = st_ops->check_member(t, member, prog); err = st_ops->check_member(t, member, prog);
if (err) { if (err) {
verbose(env, "attach to unsupported member %s of struct %s\n", verbose(env, "attach to unsupported member %s of struct %s\n",
......
...@@ -14,10 +14,6 @@ ...@@ -14,10 +14,6 @@
/* "extern" is to avoid sparse warning. It is only used in bpf_struct_ops.c. */ /* "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 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 const struct btf_type *tcp_sock_type;
static u32 tcp_sock_id, sock_id; static u32 tcp_sock_id, sock_id;
static const struct btf_type *tcp_congestion_ops_type; static const struct btf_type *tcp_congestion_ops_type;
...@@ -45,18 +41,6 @@ static int bpf_tcp_ca_init(struct btf *btf) ...@@ -45,18 +41,6 @@ static int bpf_tcp_ca_init(struct btf *btf)
return 0; 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, static bool bpf_tcp_ca_is_valid_access(int off, int size,
enum bpf_access_type type, enum bpf_access_type type,
const struct bpf_prog *prog, const struct bpf_prog *prog,
...@@ -251,15 +235,6 @@ static int bpf_tcp_ca_init_member(const struct btf_type *t, ...@@ -251,15 +235,6 @@ static int bpf_tcp_ca_init_member(const struct btf_type *t,
return 0; 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) static int bpf_tcp_ca_reg(void *kdata, struct bpf_link *link)
{ {
return tcp_register_congestion_control(kdata); return tcp_register_congestion_control(kdata);
...@@ -354,7 +329,6 @@ static struct bpf_struct_ops bpf_tcp_congestion_ops = { ...@@ -354,7 +329,6 @@ static struct bpf_struct_ops bpf_tcp_congestion_ops = {
.reg = bpf_tcp_ca_reg, .reg = bpf_tcp_ca_reg,
.unreg = bpf_tcp_ca_unreg, .unreg = bpf_tcp_ca_unreg,
.update = bpf_tcp_ca_update, .update = bpf_tcp_ca_update,
.check_member = bpf_tcp_ca_check_member,
.init_member = bpf_tcp_ca_init_member, .init_member = bpf_tcp_ca_init_member,
.init = bpf_tcp_ca_init, .init = bpf_tcp_ca_init,
.validate = bpf_tcp_ca_validate, .validate = bpf_tcp_ca_validate,
......
...@@ -1024,6 +1024,11 @@ static void bpf_testmod_test_2(int a, int b) ...@@ -1024,6 +1024,11 @@ static void bpf_testmod_test_2(int a, int b)
{ {
} }
static int bpf_testmod_tramp(int value)
{
return 0;
}
static int bpf_testmod_ops__test_maybe_null(int dummy, static int bpf_testmod_ops__test_maybe_null(int dummy,
struct task_struct *task__nullable) struct task_struct *task__nullable)
{ {
...@@ -1080,6 +1085,7 @@ static int bpf_testmod_init(void) ...@@ -1080,6 +1085,7 @@ static int bpf_testmod_init(void)
.kfunc_btf_id = bpf_testmod_dtor_ids[1] .kfunc_btf_id = bpf_testmod_dtor_ids[1]
}, },
}; };
void **tramp;
int ret; int ret;
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_testmod_common_kfunc_set); ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_testmod_common_kfunc_set);
...@@ -1103,6 +1109,14 @@ static int bpf_testmod_init(void) ...@@ -1103,6 +1109,14 @@ static int bpf_testmod_init(void)
ret = register_bpf_testmod_uprobe(); ret = register_bpf_testmod_uprobe();
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Ensure nothing is between tramp_1..tramp_40 */
BUILD_BUG_ON(offsetof(struct bpf_testmod_ops, tramp_1) + 40 * sizeof(long) !=
offsetofend(struct bpf_testmod_ops, tramp_40));
tramp = (void **)&__bpf_testmod_ops.tramp_1;
while (tramp <= (void **)&__bpf_testmod_ops.tramp_40)
*tramp++ = bpf_testmod_tramp;
return 0; return 0;
} }
......
...@@ -35,6 +35,7 @@ struct bpf_testmod_ops { ...@@ -35,6 +35,7 @@ struct bpf_testmod_ops {
void (*test_2)(int a, int b); void (*test_2)(int a, int b);
/* Used to test nullable arguments. */ /* Used to test nullable arguments. */
int (*test_maybe_null)(int dummy, struct task_struct *task); int (*test_maybe_null)(int dummy, struct task_struct *task);
int (*unsupported_ops)(void);
/* The following fields are used to test shadow copies. */ /* The following fields are used to test shadow copies. */
char onebyte; char onebyte;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "struct_ops_nulled_out_cb.skel.h" #include "struct_ops_nulled_out_cb.skel.h"
#include "struct_ops_forgotten_cb.skel.h" #include "struct_ops_forgotten_cb.skel.h"
#include "struct_ops_detach.skel.h" #include "struct_ops_detach.skel.h"
#include "unsupported_ops.skel.h"
static void check_map_info(struct bpf_map_info *info) static void check_map_info(struct bpf_map_info *info)
{ {
...@@ -311,5 +312,6 @@ void serial_test_struct_ops_module(void) ...@@ -311,5 +312,6 @@ void serial_test_struct_ops_module(void)
test_struct_ops_forgotten_cb(); test_struct_ops_forgotten_cb();
if (test__start_subtest("test_detach_link")) if (test__start_subtest("test_detach_link"))
test_detach_link(); test_detach_link();
RUN_TESTS(unsupported_ops);
} }
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod.h"
char _license[] SEC("license") = "GPL";
SEC("struct_ops/unsupported_ops")
__failure
__msg("attach to unsupported member unsupported_ops of struct bpf_testmod_ops")
int BPF_PROG(unsupported_ops)
{
return 0;
}
SEC(".struct_ops.link")
struct bpf_testmod_ops testmod = {
.unsupported_ops = (void *)unsupported_ops,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment