[PATCH bpf-next 2/4] bpf: allow to access bpf_prog during bpf_struct_access

D. Wythe
2024-10-24T02:43:04Z

From: "D. Wythe" <alibuda@linux.alibaba.com>

When implements a struct_ops for a subsystem, the fields allowed to
be written within different callbacks for the same structure might
vary, and applying the same restriction logic could lead to unexpected
issues.

Although we can use kfunc to solve it, creating a new kfunc
just for a simple assignment has low value and introduces obvious
maintenance overhead.

This patch allows the subsystem to implement independent validation
logic for different callbacks by passing an additional prog parameter,
then the subsystem can implement independent logic by checking the
prog->expected_attach_type.

Signed-off-by: D. Wythe <alibuda@linux.alibaba.com>
---
include/linux/bpf.h | 1 +
include/linux/filter.h | 1 +
kernel/bpf/verifier.c | 2 +-
kernel/sched/ext.c | 5 +++--
net/bpf/bpf_dummy_struct_ops.c | 1 +
net/core/filter.c | 7 +++++--
net/ipv4/bpf_tcp_ca.c | 1 +
net/netfilter/nf_conntrack_bpf.c | 1 +
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c | 1 +
9 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 19d8ca8..648bafd 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -987,6 +987,7 @@ struct bpf_verifier_ops {
struct bpf_prog *prog, u32 *target_size);
int (*btf_struct_access)(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size);
};

diff --git a/include/linux/filter.h b/include/linux/filter.h
index 7d7578a..2b583e2 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -670,6 +670,7 @@ struct sk_filter {
extern struct mutex nf_conn_btf_access_lock;
extern int (*nfct_btf_struct_access)(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size);

typedef unsigned int (*bpf_dispatcher_fn)(const void *ctx,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9a7ed52..e5f37cb 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6638,7 +6638,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
verbose(env, "verifier internal error: reg->btf must be kernel btf\n");
return -EFAULT;
}
- ret = env->ops->btf_struct_access(&env->log, reg, off, size);
+ ret = env->ops->btf_struct_access(&env->log, reg, env->prog, off, size);
} else {
/* Writes are permitted with default btf_struct_access for
* program allocated objects (which always have ref_obj_id > 0),
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 410a4df..7b10563 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -5344,8 +5344,9 @@ static bool bpf_scx_is_valid_access(int off, int size,
}

static int bpf_scx_btf_struct_access(struct bpf_verifier_log *log,
- const struct bpf_reg_state *reg, int off,
- int size)
+ const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
+ int off, int size)
{
const struct btf_type *t;

diff --git a/net/bpf/bpf_dummy_struct_ops.c b/net/bpf/bpf_dummy_struct_ops.c
index f71f67c..5bc7aec 100644
--- a/net/bpf/bpf_dummy_struct_ops.c
+++ b/net/bpf/bpf_dummy_struct_ops.c
@@ -234,6 +234,7 @@ static int bpf_dummy_ops_check_member(const struct btf_type *t,

static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size)
{
const struct btf_type *state;
diff --git a/net/core/filter.c b/net/core/filter.c
index a88e6924..273393c 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -9014,18 +9014,20 @@ static bool tc_cls_act_is_valid_access(int off, int size,

int (*nfct_btf_struct_access)(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size);
EXPORT_SYMBOL_GPL(nfct_btf_struct_access);

static int tc_cls_act_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size)
{
int ret = -EACCES;

mutex_lock(&nf_conn_btf_access_lock);
if (nfct_btf_struct_access)
- ret = nfct_btf_struct_access(log, reg, off, size);
+ ret = nfct_btf_struct_access(log, reg, prog, off, size);
mutex_unlock(&nf_conn_btf_access_lock);

return ret;
@@ -9100,13 +9102,14 @@ void bpf_warn_invalid_xdp_action(struct net_device *dev, struct bpf_prog *prog,

static int xdp_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size)
{
int ret = -EACCES;

mutex_lock(&nf_conn_btf_access_lock);
if (nfct_btf_struct_access)
- ret = nfct_btf_struct_access(log, reg, off, size);
+ ret = nfct_btf_struct_access(log, reg, prog, off, size);
mutex_unlock(&nf_conn_btf_access_lock);

return ret;
diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index 5548047..c459774 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -60,6 +60,7 @@ static bool bpf_tcp_ca_is_valid_access(int off, int size,

static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size)
{
const struct btf_type *t;
diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c
index 4a136fc..dad3659 100644
--- a/net/netfilter/nf_conntrack_bpf.c
+++ b/net/netfilter/nf_conntrack_bpf.c
@@ -235,6 +235,7 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net,
/* Check writes into `struct nf_conn` */
static int _nf_conntrack_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size)
{
const struct btf_type *ncit, *nct, *t;
diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
index 8835761..b537480 100644
--- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
@@ -1243,6 +1243,7 @@ static int st_ops_gen_epilogue(struct bpf_insn *insn_buf, const struct bpf_prog

static int st_ops_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
+ const struct bpf_prog *prog,
int off, int size)
{
if (off < 0 || off + size > sizeof(struct st_ops_args))
--
1.8.3.1